// // Copyright (C) 2024 veypi // 2025-07-24 15:27:31 // Distributed under terms of the MIT license. // package oauth import ( "time" "github.com/veypi/OneAuth/cfg" "github.com/vyes-ai/vigo" "gorm.io/gorm" ) // AuthorizeRequest 授权请求参数 type AuthorizeRequest struct { ResponseType string `json:"response_type" src:"query" desc:"响应类型"` ClientID string `json:"client_id" src:"query" desc:"客户端ID"` RedirectURI string `json:"redirect_uri" src:"query" desc:"重定向URI"` Scope string `json:"scope" src:"query" desc:"授权范围"` State string `json:"state" src:"query" desc:"状态"` CodeChallenge string `json:"code_challenge" src:"query" desc:"代码挑战"` CodeChallengeMethod string `json:"code_challenge_method" src:"query" desc:"代码挑战方法"` } // AuthorizeResponse 授权响应 type AuthorizeResponse struct { Code string `json:"code,omitempty"` State string `json:"state,omitempty"` RedirectURI string `json:"redirect_uri"` Error string `json:"error,omitempty"` ErrorDesc string `json:"error_description,omitempty"` } // handleAuthorize 处理OAuth授权请求 func handleAuthorize(x *vigo.X, args *AuthorizeRequest) (*AuthorizeResponse, error) { db := cfg.DB() // 1. 验证响应类型 if args.ResponseType != ResponseTypeCode { errorURI := BuildErrorRedirectURI(args.RedirectURI, ErrorUnsupportedResponseType, "不支持的响应类型", args.State) return &AuthorizeResponse{ Error: "unsupported_response_type", ErrorDesc: "不支持的响应类型", RedirectURI: errorURI, }, nil } // 2. 验证客户端 var client OAuthClient if err := db.Where("client_id = ? AND is_active = ?", args.ClientID, true).First(&client).Error; err != nil { if err == gorm.ErrRecordNotFound { errorURI := BuildErrorRedirectURI(args.RedirectURI, ErrorInvalidClient, "无效的客户端", args.State) return &AuthorizeResponse{ Error: "invalid_client", ErrorDesc: "无效的客户端", RedirectURI: errorURI, }, nil } return nil, vigo.NewError("数据库查询失败").WithError(err).WithCode(500) } // 3. 验证重定向URI if !client.IsRedirectURIValid(args.RedirectURI) { return nil, vigo.NewError("无效的重定向URI").WithCode(400) } // 4. 验证作用域 if args.Scope != "" && !client.HasScope(args.Scope) { errorURI := BuildErrorRedirectURI(args.RedirectURI, ErrorInvalidScope, "无效的授权范围", args.State) return &AuthorizeResponse{ Error: "invalid_scope", ErrorDesc: "无效的授权范围", RedirectURI: errorURI, }, nil } // TODO: 在实际应用中,这里应该: // 1. 检查用户是否已登录 // 2. 显示授权同意页面 // 3. 用户同意后生成授权码 // 为了演示,这里假设用户已登录且同意授权 // 假设当前用户ID (实际应从session or JWT token中获取) userID := "demo-user-id" // 5. 生成授权码 code, err := generateRandomString(32) if err != nil { errorURI := BuildErrorRedirectURI(args.RedirectURI, ErrorServerError, "授权码生成失败", args.State) return &AuthorizeResponse{ Error: "server_error", ErrorDesc: "授权码生成失败", RedirectURI: errorURI, }, nil } // 6. 保存授权码 authCode := &OAuthAuthorizationCode{ Code: code, ClientID: client.ID, UserID: userID, RedirectURI: args.RedirectURI, Scope: args.Scope, ExpiresAt: time.Now().Add(10 * time.Minute), // 授权码10分钟有效 CodeChallenge: args.CodeChallenge, CodeChallengeMethod: args.CodeChallengeMethod, } if err := db.Create(authCode).Error; err != nil { errorURI := BuildErrorRedirectURI(args.RedirectURI, ErrorServerError, "授权码保存失败", args.State) return &AuthorizeResponse{ Error: "server_error", ErrorDesc: "授权码保存失败", RedirectURI: errorURI, }, nil } // 7. 返回授权码重定向 return &AuthorizeResponse{ Code: code, State: args.State, RedirectURI: BuildRedirectURI(args.RedirectURI, code, args.State), }, nil }