package handler import ( _ "embed" "errors" "github.com/google/uuid" "github.com/loveuer/nf" "github.com/loveuer/nf/nft/log" "github.com/loveuer/nf/nft/resp" "gorm.io/gorm" "net/http" "net/url" "time" "uauth/model" "uauth/pkg/cache" "uauth/pkg/store" "uauth/tool" ) var ( //go:embed serve_authorize.html pageAuthorize string ) func Authorize(c *nf.Ctx) error { type Req struct { ClientId string `query:"client_id"` ResponseType string `query:"response_type"` RedirectURI string `query:"redirect_uri"` Scope string `query:"scope"` State string `query:"state"` } var ( ok bool op *model.User req = new(Req) err error client = &model.Client{} authRecord = &model.AuthorizationRecord{} uri *url.URL ) if err = c.QueryParser(req); err != nil { log.Error("[S] query parser err = %s", err.Error()) return c.Status(http.StatusBadRequest).SendString("Invalid request") } if req.ResponseType != "code" { log.Warn("[S] response type = %s", req.ResponseType) return c.Status(http.StatusBadRequest).SendString("Invalid request") } // 如果未登录,则跳转到登录界面 if op, ok = c.Locals("user").(*model.User); !ok { log.Info("[S] op not logined, redirect to login page") return c.Redirect("/oauth/v2/login?"+c.Request.URL.Query().Encode(), http.StatusFound) } log.Info("[S] Authorize: username = %s, client_id = %s", op.Username, req.ClientId) if err = store.Default.Session(tool.TimeoutCtx(c.Context(), 3)). Where("client_id", req.ClientId).Take(client).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return c.Status(http.StatusBadRequest).SendString("Bad Request: invalid client_id") } log.Error("[Authorize]: db take clients err = %s", err.Error()) return c.Status(http.StatusInternalServerError).SendString("Internal Server Error") } if err = store.Default.Session(tool.TimeoutCtx(c.Context(), 3)). Model(&model.AuthorizationRecord{}). Where("user_id", op.Id). Where("client_id", client.Id). Take(authRecord). Error; err != nil { // 用户第一次对该 client 进行授权 if errors.Is(err, gorm.ErrRecordNotFound) { return c.RenderHTML("authorize", pageAuthorize, map[string]any{ "user": map[string]any{ "username": op.Username, "avatar": "https://picsum.photos/200", }, "client_id": req.ClientId, "redirect_uri": req.RedirectURI, "scope": req.Scope, "state": req.State, }) } log.Error("[Authorize]: db take authorization_records err = %s", err.Error()) return resp.Resp500(c, err) } // 当用户已经授权过时 // 生成授权码并缓存授权码 log.Debug("[Authorize]: username = %s already approved %s", op.Username, client.Name) authorizationCode := uuid.New().String()[:8] if err = cache.Client.SetEx(c.Context(), cache.Prefix+"auth_code:"+authorizationCode, op.Id, 10*time.Minute); err != nil { return resp.Resp500(c, err) } if uri, err = url.Parse(req.RedirectURI); err != nil { log.Warn("[S] parse redirect uri = %s, err = %s", req.RedirectURI, err.Error()) return c.Status(http.StatusBadRequest).SendString("Bad Request: invalid redirect uri") } qs := uri.Query() qs.Add("code", authorizationCode) qs.Add("client_id", req.ClientId) qs.Add("scope", req.Scope) qs.Add("state", req.State) uri.ForceQuery = true value := uri.String() + qs.Encode() return c.RenderHTML("approve", pageApprove, map[string]interface{}{ "redirect_uri": value, }) }