88 lines
2.1 KiB
Go
88 lines
2.1 KiB
Go
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 int
|
|
secret string
|
|
}
|
|
|
|
func NewTokenVerifier(secret string) (*TokenVerifier, error) {
|
|
if secret == "" {
|
|
return nil, ErrEmptySecret
|
|
}
|
|
|
|
return &TokenVerifier{
|
|
Expiration: 72,
|
|
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(time.Hour * time.Duration(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
|
|
}
|