package core import ( "errors" "fmt" "github.com/golang-jwt/jwt/v5" "time" ) type AuthenticationTokens struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` } type ResetNKodeClaims struct { Reset bool `json:"reset"` jwt.RegisteredClaims } const ( accessTokenExp = 5 * time.Minute refreshTokenExp = 30 * 24 * time.Hour resetNKodeTokenExp = 5 * time.Minute ) var secret = []byte("your-secret-key") func NewAuthenticationTokens(username string, customerId CustomerId) (AuthenticationTokens, error) { accessClaims := NewAccessClaim(username, customerId) refreshClaims := jwt.RegisteredClaims{ Subject: username, Issuer: CustomerIdToString(customerId), ExpiresAt: jwt.NewNumericDate(time.Now().Add(refreshTokenExp)), } accessJwt, err := EncodeAndSignClaims(accessClaims) if err != nil { return AuthenticationTokens{}, err } refreshJwt, err := EncodeAndSignClaims(refreshClaims) if err != nil { return AuthenticationTokens{}, err } return AuthenticationTokens{ AccessToken: accessJwt, RefreshToken: refreshJwt, }, nil } func NewAccessClaim(username string, customerId CustomerId) jwt.RegisteredClaims { return jwt.RegisteredClaims{ Subject: username, Issuer: CustomerIdToString(customerId), ExpiresAt: jwt.NewNumericDate(time.Now().Add(accessTokenExp)), } } func EncodeAndSignClaims(claims jwt.Claims) (string, error) { token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(secret) } func ParseRefreshToken(refreshToken string) (*jwt.RegisteredClaims, error) { token, err := jwt.ParseWithClaims(refreshToken, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) { return secret, nil }) if err != nil { return nil, fmt.Errorf("error parsing refresh token: %w", err) } claims, ok := token.Claims.(*jwt.RegisteredClaims) if !ok { return nil, errors.New("unable to parse claims") } return claims, nil } func ParseAccessToken(accessToken string) (*jwt.RegisteredClaims, error) { token, err := jwt.ParseWithClaims(accessToken, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) { return secret, nil }) if err != nil { return nil, fmt.Errorf("error parsing refresh token: %w", err) } claims, ok := token.Claims.(*jwt.RegisteredClaims) if !ok { return nil, errors.New("unable to parse claims") } return claims, nil } func ClaimExpired(claims jwt.RegisteredClaims) error { if claims.ExpiresAt == nil { return errors.New("claim exp is nil") } if claims.ExpiresAt.Time.After(time.Now()) { return nil } return errors.New("claim expired") } func ResetNKodeToken(userEmail Email, customerId CustomerId) (string, error) { resetClaims := ResetNKodeClaims{ true, jwt.RegisteredClaims{ Subject: string(userEmail), Issuer: CustomerIdToString(customerId), ExpiresAt: jwt.NewNumericDate(time.Now().Add(resetNKodeTokenExp)), }, } return EncodeAndSignClaims(resetClaims) } func ParseRestNKodeToken(resetNKodeToken string) (*ResetNKodeClaims, error) { token, err := jwt.ParseWithClaims(resetNKodeToken, &ResetNKodeClaims{}, func(token *jwt.Token) (interface{}, error) { return secret, nil }) if err != nil { return nil, fmt.Errorf("error parsing refresh token: %w", err) } claims, ok := token.Claims.(*ResetNKodeClaims) if !ok { return nil, errors.New("unable to parse claims") } return claims, nil }