biz-member-gateway/internal/logic/member/login_logic.go

169 lines
4.5 KiB
Go

package member
import (
"biz-member-gateway/internal/domain"
bizDomain "biz-member-gateway/internal/domain"
"biz-member-gateway/internal/svc"
"biz-member-gateway/internal/types"
"context"
"regexp"
"strings"
"time"
"code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/member"
memberUC "code.30cm.net/digimon/app-cloudep-member-server/pkg/domain/usecase"
"code.30cm.net/digimon/library-go/errs"
memberProto "code.30cm.net/digimon/proto-all/pkg/member"
permissionProto "code.30cm.net/digimon/proto-all/pkg/permission"
"github.com/golang/protobuf/proto"
"github.com/zeromicro/go-zero/core/logx"
)
type LoginLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NewLoginLogic 登入
func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
return &LoginLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginTokenResp, err error) {
var result memberUC.VerifyAuthResultResponse
// Step 1 驗證進來的 Token
platform := member.GetPlatformByPlatformCode(req.Platform)
switch platform {
case member.Digimon:
switch member.GetAccountTypeByCode(req.AccountType) {
case member.AccountTypePhone:
phone, isPhone := normalizeTaiwanMobile(req.Account)
if !isPhone {
return nil, errs.InvalidFormat("phone number is invalid")
}
req.Account = phone
case member.AccountTypeMail:
if !isValidEmail(req.Account) {
return nil, errs.InvalidFormat("email is invalid")
}
}
// 原始平台驗證
res, err := l.svcCtx.MemberRPC.VerifyPlatformAuthResult(l.ctx, &memberProto.VerifyAuthResultReq{
Account: proto.String(req.Account),
Token: req.Token,
})
if err != nil {
return nil, err
}
result.Status = res.Status
case member.Google:
// 原始平台驗證
_, err := l.svcCtx.MemberRPC.VerifyGoogleAuthResult(l.ctx, &memberProto.VerifyAuthResultReq{
Account: proto.String(req.Account),
Token: req.Token,
})
if err != nil {
return nil, err
}
result.Status = true
case member.Line:
// 原始平台驗證
accessToken, err := l.svcCtx.MemberRPC.LineCodeToAccessToken(l.ctx, &memberProto.LineGetTokenReq{
Code: req.Account,
})
if err != nil {
return nil, err
}
// 換資料
userInfo, err := l.svcCtx.MemberRPC.LineGetProfileByAccessToken(l.ctx, &memberProto.LineGetUserInfoReq{
Token: accessToken.Token,
})
if err != nil {
return nil, err
}
result.Status = true
req.Account = userInfo.UserId
case member.PlatformNone:
default:
return nil, errs.InvalidFormat("invalid platform")
}
if !result.Status {
return nil, errs.Unauthorized("failed to validate password ")
}
account, err := l.svcCtx.MemberRPC.GetUIDByAccount(l.ctx, &memberProto.GetUIDByAccountReq{
Account: req.Account,
})
if err != nil {
return nil, err
}
credentials := "client_credentials"
// TODO 去拿這個使用者Role
role := bizDomain.DefaultRole
t, err := l.svcCtx.TokenRPC.NewToken(l.ctx, &permissionProto.AuthorizationReq{
GrantType: credentials,
DeviceId: req.DeviceID,
Scope: role,
IsRefreshToken: true,
Account: req.Account,
Uid: account.GetUid(),
Expires: proto.Int64(time.Now().UTC().Add(l.svcCtx.Config.Token.Expired).UnixNano()), // 指定到期的時間
RefreshExpire: proto.Int64(time.Now().UTC().Add(l.svcCtx.Config.Token.RefreshExpired).UnixNano()), // 指定到期的時間
Data: map[string]string{},
Role: role,
})
if err != nil {
return nil, err
}
return &types.LoginTokenResp{
UID: account.GetUid(),
AccessToken: t.AccessToken,
RefreshToken: t.RefreshToken,
TokenType: domain.TokenTypeBearer,
}, nil
}
// 標準化號碼並驗證是否為合法台灣手機號碼
func normalizeTaiwanMobile(phone string) (string, bool) {
// 移除空格
phone = strings.ReplaceAll(phone, " ", "")
// 移除 "+886" 並將剩餘部分標準化
if strings.HasPrefix(phone, "+886") {
phone = strings.TrimPrefix(phone, "+886")
if !strings.HasPrefix(phone, "0") {
phone = "0" + phone
}
}
// 正則表達式驗證標準化後的號碼
regex := regexp.MustCompile(`^(09\d{8})$`)
if regex.MatchString(phone) {
return phone, true
}
return "", false
}
// 驗證 Email 格式的函數
func isValidEmail(email string) bool {
// 定義正則表達式
regex := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
return regex.MatchString(email)
}