package services import ( "context" "errors" "golang.org/x/crypto/bcrypt" "log/slog" "platform/pkg/u" auth2 "platform/web/auth" "platform/web/core" client2 "platform/web/domains/client" user2 "platform/web/domains/user" "platform/web/globals/orm" 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) (*auth2.TokenDetails, error) { return nil, errors.New("TODO") } // OauthClientCredentials 验证客户端凭证 func (s *authService) OauthClientCredentials(ctx context.Context, client *m.Client, scope ...string) (*auth2.TokenDetails, error) { var clientType = auth2.PayloadTypeFromClientSpec(client2.Spec(client.Spec)) var permissions = make(map[string]struct{}, len(scope)) for _, item := range scope { permissions[item] = struct{}{} } // 保存会话并返回令牌 authCtx := auth2.Context{ Permissions: permissions, Payload: auth2.Payload{ Id: client.ID, Type: clientType, Name: client.Name, }, } token, err := auth2.CreateSession(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) (*auth2.TokenDetails, error) { details, err := auth2.RefreshSession(ctx, refreshToken, true) if err != nil { return nil, err } return details, nil } // OauthPassword 验证密码 func (s *authService) OauthPassword(ctx context.Context, _ *m.Client, data *GrantPasswordData, ip, agent string) (*auth2.TokenDetails, error) { var user *m.User err := q.Q.Transaction(func(tx *q.Query) error { switch data.LoginType { case auth2.GrantPasswordPhone: // 验证验证码 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 auth2.GrantPasswordEmail: return core.NewServErr("邮箱登录暂不可用") case auth2.GrantPasswordSecret: var err error user, err = tx.User. Where(tx.User.Phone.Eq(data.Username)). Or(tx.User.Email.Eq(data.Username)). Or(tx.User.Username.Eq(data.Username)). Take() if err != nil { slog.Debug("查找用户失败", "error", err) return core.NewBizErr("用户不存在或密码错误") } // 账户状态 if user2.Status(user.Status) == user2.StatusDisabled { slog.Debug("账户状态异常", "username", data.Username, "status", user.Status) return core.NewBizErr("用户不存在或密码错误") } // 验证密码 if user.Password == nil || *user.Password == "" { slog.Debug("用户未设置密码", "username", data.Username) return core.NewBizErr("用户不存在或密码错误") } if bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(data.Password)) != nil { slog.Debug("密码验证失败", "username", data.Username) return core.NewBizErr("用户不存在或密码错误") } default: return ErrOauthInvalidRequest } // 如果用户不存在,初始化用户 todo 初始化默认权限信息 if user == nil { user = &m.User{ Phone: data.Username, Username: u.P(data.Username), } } // 更新用户的登录时间 user.LastLogin = u.P(orm.LocalDateTime(time.Now())) user.LastLoginHost = u.P(ip) user.LastLoginAgent = u.P(agent) if err := tx.User.Save(user); err != nil { return err } return nil }) if err != nil { return nil, err } // 保存到会话 var name = "" if user.Name != nil { name = *user.Name } authCtx := auth2.Context{ Payload: auth2.Payload{ Id: user.ID, Type: auth2.PayloadUser, Name: name, Avatar: user.Avatar, }, } token, err := auth2.CreateSession(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 auth2.PasswordGrantType `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 AuthServiceError string func (e AuthServiceError) Error() string { return string(e) } const ( 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") )