Gin + GORM + pure-Go SQLite. Users/auth (JWT), API key management with quotas, proxy gateway with weighted channel failover and health checks, usage/billing ledger, cross-protocol conversion (Anthropic Messages / OpenAI Chat Completions / OpenAI Responses), and channel/model admin API. Channels declare native API formats and auto-convert the rest. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
67 lines
1.8 KiB
Go
67 lines
1.8 KiB
Go
package jwt
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
)
|
|
|
|
// Claims is the payload of a signed token.
|
|
type Claims struct {
|
|
UserID int64 `json:"uid"`
|
|
Username string `json:"username"`
|
|
Role string `json:"role"`
|
|
Type string `json:"typ"` // access | refresh
|
|
jwt.RegisteredClaims
|
|
}
|
|
|
|
func sign(secret string, c Claims) (string, error) {
|
|
t := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
|
|
return t.SignedString([]byte(secret))
|
|
}
|
|
|
|
// SignAccess issues a short-lived access token.
|
|
func SignAccess(secret string, userID int64, username, role string, ttl time.Duration) (string, error) {
|
|
return sign(secret, Claims{
|
|
UserID: userID, Username: username, Role: role, Type: "access",
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(ttl)),
|
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
|
Subject: fmtID(userID),
|
|
},
|
|
})
|
|
}
|
|
|
|
// SignRefresh issues a long-lived refresh token.
|
|
func SignRefresh(secret string, userID int64, ttl time.Duration) (string, error) {
|
|
return sign(secret, Claims{
|
|
UserID: userID, Type: "refresh",
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(ttl)),
|
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
|
Subject: fmtID(userID),
|
|
},
|
|
})
|
|
}
|
|
|
|
// Parse validates a token and returns its claims.
|
|
func Parse(secret, token string) (*Claims, error) {
|
|
var c Claims
|
|
parsed, err := jwt.ParseWithClaims(token, &c, func(t *jwt.Token) (interface{}, error) {
|
|
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
return nil, errors.New("unexpected signing method")
|
|
}
|
|
return []byte(secret), nil
|
|
})
|
|
if err != nil || !parsed.Valid {
|
|
return nil, errors.New("invalid token")
|
|
}
|
|
return &c, nil
|
|
}
|
|
|
|
func fmtID(id int64) string {
|
|
return strconv.FormatInt(id, 10)
|
|
}
|