package handler import ( "encoding/base64" "errors" "github.com/google/uuid" "github.com/loveuer/nf" "github.com/loveuer/nf/nft/log" "gorm.io/gorm" "net/http" "strings" "uauth/internal/store/cache" "uauth/internal/store/db" "uauth/internal/tool" "uauth/model" ) func HandleToken(c *nf.Ctx) error { type Req struct { Code string `form:"code"` GrantType string `form:"grant_type"` RedirectURI string `form:"redirect_uri"` } var ( err error req = new(Req) opId uint64 op = new(model.User) token string basic string bs []byte strs []string client = new(model.Client) ) if err = c.BodyParser(req); err != nil { return c.Status(http.StatusBadRequest).SendString("Bad Request: invalid form") } // client_secret if basic = c.Get("Authorization"); basic == "" { return c.Status(http.StatusUnauthorized).SendString("Authorization header missing") } switch { case strings.HasPrefix(basic, "Basic "): basic = strings.TrimPrefix(basic, "Basic ") default: return c.Status(http.StatusBadRequest).SendString("Bad Request: authorization scheme not supported") } if bs, err = base64.StdEncoding.DecodeString(basic); err != nil { log.Warn("[Token] base64 decode failed, raw = %s, err = %s", basic, err.Error()) return c.Status(http.StatusBadRequest).SendString("Bad Request: invalid basic authorization") } if strs = strings.SplitN(string(bs), ":", 2); len(strs) != 2 { log.Warn("[Token] basic split err, decode = %s", string(bs)) return c.Status(http.StatusBadRequest).SendString("Bad Request: invalid basic authorization") } clientId, clientSecret := strs[0], strs[1] if err = db.Default.Session().Model(&model.Client{}). Where("client_id", clientId). Take(client). Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return c.Status(http.StatusBadRequest).SendString("Bad Request: client invalid") } log.Error("[Token] db take client by id = %s, err = %s", clientId, err.Error()) return c.Status(http.StatusInternalServerError).SendString("Internal Server Error") } if client.ClientSecret != clientSecret { log.Warn("[Token] client_secret invalid, want = %s, got = %s", client.ClientSecret, clientSecret) return c.Status(http.StatusUnauthorized).SendString("Unauthorized: client secret invalid") } if err = cache.Client.GetScan(tool.Timeout(2), cache.Prefix+"auth_code:"+req.Code).Scan(&opId); err != nil { if errors.Is(err, cache.ErrorKeyNotFound) { return c.Status(http.StatusBadRequest).SendString("Bad Request: invalid code") } log.Error("[S] handleToken: get code from cache err = %s", err.Error()) return c.Status(http.StatusInternalServerError).SendString("Internal Server Error") } op.Id = opId if err = db.Default.Session().Take(op).Error; err != nil { log.Error("[S] handleToken: get op by id err, id = %d, err = %s", opId, err.Error()) return c.Status(http.StatusInternalServerError).SendString("Internal Server Error") } if token, err = op.JwtEncode(); err != nil { log.Error("[S] handleToken: encode token err, id = %d, err = %s", opId, err.Error()) return c.Status(http.StatusInternalServerError).SendString("Internal Server Error") } refreshToken := uuid.New().String() return c.JSON(map[string]any{ "access_token": token, "refresh_token": refreshToken, "token_type": "Bearer", "expires_in": 24 * 3600, }) }