add login and register api

This commit is contained in:
王性驊 2025-10-06 21:14:58 +08:00
parent 40812db5bf
commit bbb6b4b746
11 changed files with 158 additions and 49 deletions

View File

@ -59,6 +59,7 @@ type (
// --- 4. 權杖刷新 ---
// RefreshTokenReq 更新 AccessToken
RefreshTokenReq {
AccessToken string `json:"access_token" validate:"required"`
RefreshToken string `json:"refresh_token" validate:"required"`
}

View File

@ -2,3 +2,4 @@ package domain
const SuccessCode = 10200
const SuccessMessage = "success"
const DefaultScope = "gateway"

View File

@ -0,0 +1,40 @@
package auth
import (
"backend/internal/svc"
"backend/internal/types"
"backend/pkg/permission/domain/entity"
"backend/pkg/permission/domain/token"
"context"
"time"
)
// 生成 Token
func generateToken(svc *svc.ServiceContext, ctx context.Context, req *types.LoginReq, uid string) (entity.TokenResp, error) {
// scope role 要修改refresh tl
role := "user"
tk, err := svc.TokenUC.NewToken(ctx, entity.AuthorizationReq{
GrantType: token.ClientCredentials.ToString(),
DeviceID: uid, // TODO 沒傳暫時先用UID 替代
Scope: "gateway",
IsRefreshToken: true,
Expires: time.Now().UTC().Add(svc.Config.Token.AccessTokenExpiry).Unix(),
Data: map[string]string{
"uid": uid,
},
Role: role,
Account: req.LoginID,
})
if err != nil {
return entity.TokenResp{}, err
}
return entity.TokenResp{
AccessToken: tk.AccessToken,
TokenType: tk.TokenType,
ExpiresIn: tk.ExpiresIn,
RefreshToken: tk.RefreshToken,
}, nil
}

View File

@ -1,6 +1,10 @@
package auth
import (
"backend/pkg/library/errs"
"backend/pkg/library/errs/code"
memberD "backend/pkg/member/domain/member"
member "backend/pkg/member/domain/usecase"
"context"
"backend/internal/svc"
@ -15,7 +19,7 @@ type LoginLogic struct {
svcCtx *svc.ServiceContext
}
// 使用者登入
// NewLoginLogic 使用者登入
func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
return &LoginLogic{
Logger: logx.WithContext(ctx),
@ -25,7 +29,65 @@ func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic
}
func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginResp, err error) {
// todo: add your logic here and delete this line
//var result member.VerifyAuthResultResponse
switch req.AuthMethod {
case "credentials":
cr, err := l.svcCtx.AccountUC.VerifyPlatformAuthResult(l.ctx, member.VerifyAuthResultRequest{
Account: req.LoginID,
Token: req.Credentials.Password,
})
if err != nil {
return nil, err
}
return
if !cr.Status {
return nil, errs.Unauthorized("failed to verify password")
}
case "platform":
switch req.Platform.Provider {
case memberD.Google.ToString():
_, err := l.svcCtx.AccountUC.VerifyGoogleAuthResult(l.ctx, member.VerifyAuthResultRequest{
Account: req.LoginID,
Token: req.Platform.Token,
})
if err != nil {
return nil, err
}
case memberD.Line.ToString():
// 原始平台驗證
accessToken, err := l.svcCtx.AccountUC.LineCodeToAccessToken(l.ctx, req.LoginID)
if err != nil {
return nil, err
}
// 換資料
userInfo, err := l.svcCtx.AccountUC.LineGetProfileByAccessToken(l.ctx, accessToken.AccessToken)
if err != nil {
return nil, err
}
req.LoginID = userInfo.UserID
default:
return nil, errs.InvalidFormatWithScope(code.CloudEPMember, "unsupported 3 party platform")
}
default:
return nil, errs.InvalidFormatWithScope(code.CloudEPMember, "failed to get correct auth method")
}
account, err := l.svcCtx.AccountUC.GetUIDByAccount(l.ctx, member.GetUIDByAccountRequest{
Account: req.LoginID,
})
if err != nil {
return nil, err
}
tk, err := generateToken(l.svcCtx, l.ctx, req, account.UID)
if err != nil {
return nil, err
}
return &types.LoginResp{
UID: account.UID,
AccessToken: tk.AccessToken,
RefreshToken: tk.RefreshToken,
TokenType: tk.TokenType,
}, nil
}

View File

@ -1,7 +1,10 @@
package auth
import (
"backend/internal/domain"
"backend/pkg/permission/domain/entity"
"context"
"time"
"backend/internal/svc"
"backend/internal/types"
@ -15,7 +18,7 @@ type RefreshTokenLogic struct {
svcCtx *svc.ServiceContext
}
// 刷新 Access Token
// NewRefreshTokenLogic 刷新 Access Token
func NewRefreshTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RefreshTokenLogic {
return &RefreshTokenLogic{
Logger: logx.WithContext(ctx),
@ -25,7 +28,24 @@ func NewRefreshTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Refr
}
func (l *RefreshTokenLogic) RefreshToken(req *types.RefreshTokenReq) (resp *types.RefreshTokenResp, err error) {
// todo: add your logic here and delete this line
data, err := l.svcCtx.TokenUC.ReadTokenBasicData(l.ctx, req.AccessToken)
if err != nil {
return nil, err
}
return
tk, err := l.svcCtx.TokenUC.RefreshToken(l.ctx, entity.RefreshTokenReq{
Token: req.RefreshToken,
Scope: domain.DefaultScope,
Expires: time.Now().UTC().Add(l.svcCtx.Config.Token.RefreshTokenExpiry).Unix(),
DeviceID: data["uid"],
})
if err != nil {
return nil, err
}
return &types.RefreshTokenResp{
AccessToken: tk.Token,
RefreshToken: tk.OneTimeToken,
TokenType: tk.TokenType,
}, nil
}

View File

@ -7,11 +7,7 @@ import (
"backend/pkg/library/errs/code"
mb "backend/pkg/member/domain/member"
member "backend/pkg/member/domain/usecase"
"backend/pkg/permission/domain/entity"
"backend/pkg/permission/domain/token"
"context"
"time"
"google.golang.org/protobuf/proto"
"github.com/zeromicro/go-zero/core/logx"
@ -82,7 +78,7 @@ func (l *RegisterLogic) Register(req *types.LoginReq) (resp *types.LoginResp, er
// Step 5: 生成 Token
req.LoginID = bd.CreateAccountReq.LoginID
tk, err := l.generateToken(req, account.UID)
tk, err := generateToken(l.svcCtx, l.ctx, req, account.UID)
if err != nil {
return nil, err
}
@ -184,34 +180,3 @@ func buildLineData(ctx context.Context, req *types.LoginReq, svc *svc.ServiceCon
},
}, nil
}
// 生成 Token
func (l *RegisterLogic) generateToken(req *types.LoginReq, uid string) (entity.TokenResp, error) {
// scope role 要修改refresh tl
role := "user"
tk, err := l.svcCtx.TokenUC.NewToken(l.ctx, entity.AuthorizationReq{
GrantType: token.ClientCredentials.ToString(),
DeviceID: uid, // TODO 沒傳暫時先用UID 替代
Scope: "gateway",
IsRefreshToken: true,
Expires: time.Now().UTC().Add(l.svcCtx.Config.Token.AccessTokenExpiry).Unix(),
Data: map[string]string{
"uid": uid,
"role": role,
},
Role: role,
Account: req.LoginID,
})
if err != nil {
return entity.TokenResp{}, err
}
return entity.TokenResp{
AccessToken: tk.AccessToken,
TokenType: tk.TokenType,
ExpiresIn: tk.ExpiresIn,
RefreshToken: tk.RefreshToken,
}, nil
}

View File

@ -15,7 +15,6 @@ type RequestPasswordResetLogic struct {
svcCtx *svc.ServiceContext
}
// 請求發送密碼重設驗證碼
func NewRequestPasswordResetLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RequestPasswordResetLogic {
return &RequestPasswordResetLogic{
Logger: logx.WithContext(ctx),
@ -24,6 +23,7 @@ func NewRequestPasswordResetLogic(ctx context.Context, svcCtx *svc.ServiceContex
}
}
// RequestPasswordReset 請求發送密碼重設驗證碼 aka 忘記密碼
func (l *RequestPasswordResetLogic) RequestPasswordReset(req *types.RequestPasswordResetReq) (resp *types.RespOK, err error) {
// todo: add your logic here and delete this line

View File

@ -15,7 +15,6 @@ type VerifyPasswordResetCodeLogic struct {
svcCtx *svc.ServiceContext
}
// 校驗密碼重設驗證碼
func NewVerifyPasswordResetCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *VerifyPasswordResetCodeLogic {
return &VerifyPasswordResetCodeLogic{
Logger: logx.WithContext(ctx),
@ -24,6 +23,7 @@ func NewVerifyPasswordResetCodeLogic(ctx context.Context, svcCtx *svc.ServiceCon
}
}
// VerifyPasswordResetCode 校驗密碼重設驗證碼(頁面需求,預先檢查看看, 顯示表演用)
func (l *VerifyPasswordResetCodeLogic) VerifyPasswordResetCode(req *types.VerifyCodeReq) (resp *types.RespOK, err error) {
// todo: add your logic here and delete this line

View File

@ -49,6 +49,7 @@ type PlatformPayload struct {
}
type RefreshTokenReq struct {
AccessToken string `json:"access_token" validate:"required"`
RefreshToken string `json:"refresh_token" validate:"required"`
}

View File

@ -174,12 +174,13 @@ func (use *TokenUseCase) RefreshToken(ctx context.Context, req entity.RefreshTok
credentials := token.ClientCredentials
newToken, err := use.newToken(ctx, &entity.AuthorizationReq{
GrantType: credentials.ToString(),
Scope: req.Scope,
Scope: claimsData.Scope(),
DeviceID: req.DeviceID,
Data: claimsData,
Expires: req.Expires,
IsRefreshToken: true,
Account: req.DeviceID,
Account: claimsData.Account(),
Role: claimsData.Role(),
})
if err != nil {
return entity.RefreshTokenResp{},
@ -474,9 +475,9 @@ func (use *TokenUseCase) wrapTokenError(ctx context.Context, param wrapTokenErro
{Key: "func", Value: param.funcName},
{Key: "err", Value: param.err.Error()},
}
logx.WithContext(ctx).Errorw(param.message, logFields...)
wrappedErr := errs.NewError(
code.CatToken,
code.CatToken,
@ -595,7 +596,7 @@ func (use *TokenUseCase) BlacklistToken(ctx context.Context, token string, reaso
})
}
logx.WithContext(ctx).Infow("token blacklisted",
logx.WithContext(ctx).Infow("token blacklisted",
logx.Field("jti", jtiStr),
logx.Field("uid", uid),
logx.Field("reason", reason))

View File

@ -57,3 +57,21 @@ func (tc tokenClaims) UID() string {
return uid
}
func (tc tokenClaims) Scope() string {
scope, ok := tc["scope"]
if !ok {
return ""
}
return scope
}
func (tc tokenClaims) Account() string {
scope, ok := tc["account"]
if !ok {
return ""
}
return scope
}