authkit initial
This commit is contained in:
parent
5173b0a43d
commit
134393fbca
43 changed files with 5188 additions and 1 deletions
69
jwt.go
Normal file
69
jwt.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package authkit
|
||||
|
||||
import (
|
||||
"git.juancwu.dev/juancwu/errx"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// accessClaims is the JWT shape issued by IssueJWT. The session_version
|
||||
// field carries the User.SessionVersion at issue time so AuthenticateJWT
|
||||
// can detect global revocations (logout-everywhere, password change).
|
||||
type accessClaims struct {
|
||||
jwt.RegisteredClaims
|
||||
SessionVersion int `json:"sv"`
|
||||
Method string `json:"m"`
|
||||
}
|
||||
|
||||
func (a *Auth) signAccessToken(userID uuid.UUID, sessionVersion int) (string, error) {
|
||||
const op = "authkit.signAccessToken"
|
||||
now := a.now()
|
||||
claims := accessClaims{
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
Subject: userID.String(),
|
||||
Issuer: a.cfg.JWTIssuer,
|
||||
Audience: jwt.ClaimStrings{a.cfg.JWTAudience},
|
||||
IssuedAt: jwt.NewNumericDate(now),
|
||||
ExpiresAt: jwt.NewNumericDate(now.Add(a.cfg.AccessTokenTTL)),
|
||||
ID: uuid.NewString(),
|
||||
},
|
||||
SessionVersion: sessionVersion,
|
||||
Method: string(AuthMethodJWT),
|
||||
}
|
||||
tok := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
signed, err := tok.SignedString(a.cfg.JWTSecret)
|
||||
if err != nil {
|
||||
return "", errx.Wrap(op, err)
|
||||
}
|
||||
return signed, nil
|
||||
}
|
||||
|
||||
// parseAccessToken validates the signature and returns the parsed claims.
|
||||
func (a *Auth) parseAccessToken(token string) (*accessClaims, error) {
|
||||
const op = "authkit.parseAccessToken"
|
||||
opts := []jwt.ParserOption{
|
||||
jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}),
|
||||
jwt.WithExpirationRequired(),
|
||||
jwt.WithIssuedAt(),
|
||||
jwt.WithTimeFunc(a.cfg.Clock),
|
||||
}
|
||||
if a.cfg.JWTIssuer != "" {
|
||||
opts = append(opts, jwt.WithIssuer(a.cfg.JWTIssuer))
|
||||
}
|
||||
if a.cfg.JWTAudience != "" {
|
||||
opts = append(opts, jwt.WithAudience(a.cfg.JWTAudience))
|
||||
}
|
||||
parser := jwt.NewParser(opts...)
|
||||
|
||||
parsed, err := parser.ParseWithClaims(token, &accessClaims{}, func(t *jwt.Token) (any, error) {
|
||||
return a.cfg.JWTSecret, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errx.Wrap(op, ErrTokenInvalid)
|
||||
}
|
||||
claims, ok := parsed.Claims.(*accessClaims)
|
||||
if !ok || !parsed.Valid {
|
||||
return nil, errx.Wrap(op, ErrTokenInvalid)
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue