// // Copyright (C) 2024 veypi // 2025-07-24 15:27:31 // Distributed under terms of the MIT license. // package oauth import ( "github.com/veypi/OneAuth/cfg" "github.com/vyes-ai/vigo" "gorm.io/gorm" "time" ) // AuthorizeRequest 授权请求参数 type AuthorizeRequest struct { ResponseType string `form:"response_type" binding:"required"` ClientID string `form:"client_id" binding:"required"` RedirectURI string `form:"redirect_uri" binding:"required"` Scope string `form:"scope"` State string `form:"state"` CodeChallenge string `form:"code_challenge"` CodeChallengeMethod string `form:"code_challenge_method"` } // 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) error { args := &AuthorizeRequest{} if err := x.Parse(args); err != nil { return vigo.NewError("参数解析失败").WithError(err).WithCode(400) } db := cfg.DB() // 1. 验证响应类型 if args.ResponseType != ResponseTypeCode { errorURI := BuildErrorRedirectURI(args.RedirectURI, ErrorUnsupportedResponseType, "不支持的响应类型", args.State) return x.JSON(&AuthorizeResponse{ Error: "unsupported_response_type", ErrorDesc: "不支持的响应类型", RedirectURI: errorURI, }) } // 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 x.JSON(&AuthorizeResponse{ Error: "invalid_client", ErrorDesc: "无效的客户端", RedirectURI: errorURI, }) } return vigo.NewError("数据库查询失败").WithError(err).WithCode(500) } // 3. 验证重定向URI if !client.IsRedirectURIValid(args.RedirectURI) { return vigo.NewError("无效的重定向URI").WithCode(400) } // 4. 验证作用域 if args.Scope != "" && !client.HasScope(args.Scope) { errorURI := BuildErrorRedirectURI(args.RedirectURI, ErrorInvalidScope, "无效的授权范围", args.State) return x.JSON(&AuthorizeResponse{ Error: "invalid_scope", ErrorDesc: "无效的授权范围", RedirectURI: errorURI, }) } // TODO: 在实际应用中,这里应该: // 1. 检查用户是否已登录 // 2. 显示授权同意页面 // 3. 用户同意后生成授权码 // 为了演示,这里假设用户已登录且同意授权 // 假设当前用户ID (实际应从session或JWT token中获取) userID := "demo-user-id" // 5. 生成授权码 code, err := generateRandomString(32) if err != nil { errorURI := BuildErrorRedirectURI(args.RedirectURI, ErrorServerError, "授权码生成失败", args.State) return x.JSON(&AuthorizeResponse{ Error: "server_error", ErrorDesc: "授权码生成失败", RedirectURI: errorURI, }) } // 6. 创建授权码记录 authCode := &OAuthAuthorizationCode{ Code: code, ClientID: client.ID, UserID: userID, RedirectURI: args.RedirectURI, Scope: args.Scope, CodeChallenge: args.CodeChallenge, CodeChallengeMethod: args.CodeChallengeMethod, ExpiresAt: time.Now().Add(DefaultAuthorizationCodeExpiry), Used: false, } if err := db.Create(authCode).Error; err != nil { errorURI := BuildErrorRedirectURI(args.RedirectURI, ErrorServerError, "授权码保存失败", args.State) return x.JSON(&AuthorizeResponse{ Error: "server_error", ErrorDesc: "授权码保存失败", RedirectURI: errorURI, }) } // 7. 构建成功重定向URI redirectURI := BuildRedirectURI(args.RedirectURI, authCode.Code, args.State) return x.JSON(&AuthorizeResponse{ Code: authCode.Code, State: args.State, RedirectURI: redirectURI, }) }