110 lines
3.3 KiB
Go
Raw Normal View History

2024-10-23 17:46:15 +08:00
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,
})
}