Files
platform/web/services/auth.go

208 lines
5.3 KiB
Go

package services
import (
"context"
"errors"
"platform/web/auth"
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
"time"
"gorm.io/gorm"
)
var Auth = &authService{}
type authService struct{}
// OauthAuthorizationCode 验证授权码
func (s *authService) OauthAuthorizationCode(ctx context.Context, client *m.Client, code, redirectURI, codeVerifier string) (*TokenDetails, error) {
// TODO: 从数据库验证授权码
return nil, errors.New("TODO")
}
// OauthClientCredentials 验证客户端凭证
func (s *authService) OauthClientCredentials(ctx context.Context, client *m.Client, scope ...string) (*TokenDetails, error) {
var clientType auth.PayloadType
switch client.Spec {
case 1:
clientType = auth.PayloadClientPublic
case 2:
clientType = auth.PayloadClientPublic
case 3:
clientType = auth.PayloadClientConfidential
}
var permissions = make(map[string]struct{}, len(scope))
for _, item := range scope {
permissions[item] = struct{}{}
}
// 保存会话并返回令牌
authCtx := auth.Context{
Permissions: permissions,
Payload: auth.Payload{
Id: client.ID,
Type: clientType,
Name: client.Name,
},
}
// todo 数据库定义会话持续时间
token, err := Session.Create(ctx, authCtx, false)
if err != nil {
return nil, err
}
return token, nil
}
// OauthRefreshToken 验证刷新令牌
func (s *authService) OauthRefreshToken(ctx context.Context, _ *m.Client, refreshToken string, scope ...[]string) (*TokenDetails, error) {
// TODO: 从数据库验证刷新令牌
details, err := Session.Refresh(ctx, refreshToken)
if err != nil {
return nil, err
}
return details, nil
}
// OauthPassword 验证密码
func (s *authService) OauthPassword(ctx context.Context, _ *m.Client, data *GrantPasswordData, ip, agent string) (*TokenDetails, error) {
var user *m.User
err := q.Q.Transaction(func(tx *q.Query) error {
switch data.LoginType {
case OauthGrantPasswordTypePhoneCode:
// 验证验证码
err := Verifier.VerifySms(ctx, data.Username, data.Password)
if err != nil {
if errors.Is(err, ErrVerifierServiceInvalid) {
return ErrOauthInvalidRequest
}
return err
}
// 查找用户
user, err =
tx.User.Where(tx.User.Phone.Eq(data.Username)).Take()
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
case OauthGrantPasswordTypeEmailCode:
var err error
user, err = tx.User.Where(tx.User.Email.Eq(data.Username)).Take()
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
case OauthGrantPasswordTypePassword:
var err error
user, err = tx.User.
Where(tx.User.Or(
tx.User.Phone.Eq(data.Username),
tx.User.Email.Eq(data.Username),
tx.User.Username.Eq(data.Username),
)).
Take()
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
default:
return ErrOauthInvalidRequest
}
// 如果用户不存在,初始化用户 todo 初始化默认权限信息
if user == nil {
user = &m.User{
Phone: data.Username,
Username: data.Username,
}
}
// 更新用户的登录时间
user.LastLogin = core.LocalDateTime(time.Now())
user.LastLoginHost = ip
user.LastLoginAgent = agent
if err := tx.User.Omit(q.User.AdminID).Save(user); err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
// 保存到会话
authCtx := auth.Context{
Payload: auth.Payload{
Id: user.ID,
Type: auth.PayloadUser,
Name: user.Name,
Avatar: user.Avatar,
},
}
token, err := Session.Create(ctx, authCtx, data.Remember)
if err != nil {
return nil, err
}
return token, nil
}
type GrantCodeData struct {
Code string `json:"code" form:"code"`
RedirectURI string `json:"redirect_uri" form:"redirect_uri"`
CodeVerifier string `json:"code_verifier" form:"code_verifier"`
}
type GrantClientData struct {
}
type GrantRefreshData struct {
RefreshToken string `json:"refresh_token" form:"refresh_token"`
}
type GrantPasswordData struct {
LoginType OauthGrantLoginType `json:"login_type" form:"login_type"`
Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"`
Remember bool `json:"remember" form:"remember"`
}
type OauthGrantType string
const (
OauthGrantTypeAuthorizationCode = OauthGrantType("authorization_code")
OauthGrantTypeClientCredentials = OauthGrantType("client_credentials")
OauthGrantTypeRefreshToken = OauthGrantType("refresh_token")
OauthGrantTypePassword = OauthGrantType("password")
)
type OauthGrantLoginType string
const (
OauthGrantPasswordTypePassword = OauthGrantLoginType("password")
OauthGrantPasswordTypePhoneCode = OauthGrantLoginType("phone_code")
OauthGrantPasswordTypeEmailCode = OauthGrantLoginType("email_code")
)
type AuthServiceError string
func (e AuthServiceError) Error() string {
return string(e)
}
var (
ErrOauthInvalidRequest = AuthServiceError("invalid_request")
ErrOauthInvalidClient = AuthServiceError("invalid_client")
ErrOauthInvalidGrant = AuthServiceError("invalid_grant")
ErrOauthInvalidScope = AuthServiceError("invalid_scope")
ErrOauthUnauthorizedClient = AuthServiceError("unauthorized_client")
ErrOauthUnsupportedGrantType = AuthServiceError("unsupported_grant_type")
)