package token import ( "crypto/rand" "errors" "fmt" "math/big" "time" "github.com/golang-jwt/jwt" "github.com/google/uuid" ) var ( ErrInvalidToken = errors.New("token is invalid") ErrExpiredToken = errors.New("token has expired") ErrTokenUnknown = errors.New("token unknown error") ErrNotEvenAToken = errors.New("not even a token") ErrUnhandleToken = errors.New("unhandel token") ) type Maker interface { CreateToken(issuer, username string, duration time.Duration) (string, error) VerifyToken(token string) (*CustomClaims, error) } type CustomClaims struct { UserName string `json:"user_name"` jwt.StandardClaims } func NewCustomClaim(issuer, username string, duration time.Duration) (*CustomClaims, error) { tokenID, err := uuid.NewRandom() if err != nil { return nil, err } claims := CustomClaims{ username, jwt.StandardClaims{ Id: tokenID.String(), IssuedAt: time.Now().Unix(), ExpiresAt: time.Now().Add(duration).Unix(), Issuer: issuer, }, } return &claims, nil } type JWTMaker struct { secretKey string } const minSecretKeySize = 32 func NewJWTMaker(secretKey string) (Maker, error) { if len(secretKey) < minSecretKeySize { return nil, fmt.Errorf("invalid key size: must be at least %d characters", minSecretKeySize) } return &JWTMaker{secretKey}, nil } func (maker *JWTMaker) CreateToken(issuer, username string, duration time.Duration) (string, error) { customClaim, err := NewCustomClaim(issuer, username, duration) if err != nil { return "", err } jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, customClaim) return jwtToken.SignedString([]byte(maker.secretKey)) } // VerifyToken checks if the token is valid or not func (maker *JWTMaker) VerifyToken(token string) (*CustomClaims, error) { keyFunc := func(token *jwt.Token) (interface{}, error) { _, ok := token.Method.(*jwt.SigningMethodHMAC) if !ok { return nil, ErrInvalidToken } return []byte(maker.secretKey), nil } jwtToken, err := jwt.ParseWithClaims(token, &CustomClaims{}, keyFunc) if err != nil { if verr, ok := err.(*jwt.ValidationError); ok { if verr.Errors&jwt.ValidationErrorMalformed != 0 { return nil, ErrNotEvenAToken } else if verr.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 { return nil, ErrExpiredToken } else { return nil, ErrUnhandleToken } } else { return nil, ErrTokenUnknown } } payload, ok := jwtToken.Claims.(*CustomClaims) if !ok { return nil, ErrInvalidToken } return payload, nil } func GenerateSecretKey() (string, error) { n := 120 const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-" ret := make([]byte, n) for i := 0; i < n; i++ { num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters)))) if err != nil { return "", err } ret[i] = letters[num.Int64()] } return string(ret), nil }