package jwt import ( "fmt" "time" "git.javil.eu/jacob1123/budgeteer" "git.javil.eu/jacob1123/budgeteer/postgres" "github.com/dgrijalva/jwt-go" "github.com/google/uuid" ) // TokenVerifier verifies Tokens. type TokenVerifier struct { Expiration time.Duration secret string } const DefaultExpiration = time.Hour * time.Duration(72) func NewTokenVerifier(secret string) (*TokenVerifier, error) { if secret == "" { return nil, ErrEmptySecret } return &TokenVerifier{ Expiration: DefaultExpiration, secret: secret, }, nil } var ( ErrUnexpectedSigningMethod = fmt.Errorf("unexpected signing method") ErrInvalidToken = fmt.Errorf("token is invalid") ErrTokenExpired = fmt.Errorf("token has expired") ErrEmptySecret = fmt.Errorf("secret is required") ) // CreateToken creates a new token from username and name. func (tv *TokenVerifier) CreateToken(user *postgres.User) (string, error) { if tv.secret == "" { return "", ErrEmptySecret } token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "usr": user.Email, "name": user.Name, "exp": time.Now().Add(tv.Expiration).Unix(), "id": user.ID, }) // Generate encoded token and send it as response. t, err := token.SignedString([]byte(tv.secret)) if err != nil { return "", fmt.Errorf("create token: %w", err) } return t, nil } // VerifyToken verifies a given string-token. func (tv *TokenVerifier) VerifyToken(tokenString string) (budgeteer.Token, error) { //nolint:ireturn if tv.secret == "" { return nil, ErrEmptySecret } token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("method '%v': %w", token.Header["alg"], ErrUnexpectedSigningMethod) } return []byte(tv.secret), nil }) if err != nil { return nil, fmt.Errorf("parse jwt: %w", err) } claims, err := verifyToken(token) if err != nil { return nil, fmt.Errorf("verify jwt: %w", err) } tkn := &Token{ //nolint:forcetypeassert username: claims["usr"].(string), name: claims["name"].(string), expiry: claims["exp"].(float64), id: uuid.MustParse(claims["id"].(string)), } return tkn, nil }