You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
106 lines
2.9 KiB
106 lines
2.9 KiB
package jwt
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/dustinpianalto/errors"
|
|
"github.com/dustinpianalto/quartermaster"
|
|
"github.com/golang-jwt/jwt"
|
|
)
|
|
|
|
var secretKey = []byte(os.Getenv("JWT_KEY"))
|
|
|
|
func AuthenticateJWTToken(req *http.Request) (*quartermaster.Claims, error) {
|
|
const method = errors.Method("jwt/AuthenticateJWTToken")
|
|
jwtToken, err := extractJWTToken(req)
|
|
|
|
if err != nil {
|
|
return nil, errors.E(method, "could not authenticate token", err)
|
|
}
|
|
|
|
claims, err := ParseJWT(jwtToken, secretKey)
|
|
|
|
if err != nil {
|
|
return nil, errors.E(method, "could not parse token", err)
|
|
}
|
|
|
|
return claims, nil
|
|
}
|
|
|
|
// ExtractJWTToken extracts bearer token from Authorization header
|
|
func extractJWTToken(req *http.Request) (string, error) {
|
|
const method = errors.Method("jwt/extractJWTToken")
|
|
tokenString := req.Header.Get("Authorization")
|
|
|
|
if tokenString == "" {
|
|
return "", errors.E(method, errors.Malformed, "token not found")
|
|
}
|
|
|
|
tokenString, err := stripTokenPrefix(tokenString)
|
|
|
|
if err != nil {
|
|
return "", errors.E(method, "error formatting token", err)
|
|
}
|
|
|
|
return tokenString, nil
|
|
}
|
|
|
|
// Strips 'Token' or 'Bearer' prefix from token string
|
|
func stripTokenPrefix(tok string) (string, error) {
|
|
// split token to 2 parts
|
|
tokenParts := strings.Split(tok, " ")
|
|
|
|
if len(tokenParts) < 2 {
|
|
return tokenParts[0], nil
|
|
}
|
|
|
|
return tokenParts[1], nil
|
|
}
|
|
|
|
func ParseJWT(tokenString string, key []byte) (*quartermaster.Claims, error) {
|
|
const method = errors.Method("jwt/ParseJWT")
|
|
var claims *quartermaster.Claims = &quartermaster.Claims{}
|
|
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
|
|
|
|
// validate the alg is what is expected:
|
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
return nil, errors.E(errors.Method("jwt/ParseJWT/Parse"), errors.Malformed, fmt.Sprintf("unexpected signing method: %v", token.Header["alg"]))
|
|
}
|
|
return key, nil
|
|
})
|
|
if err != nil {
|
|
return nil, errors.E(method, "error parsing token", err)
|
|
}
|
|
|
|
if token.Valid {
|
|
return claims, nil
|
|
|
|
} else if ve, ok := err.(*jwt.ValidationError); ok {
|
|
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
|
|
return nil, errors.E(method, errors.Malformed, "token is malformed")
|
|
} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
|
|
// Token is either expired or not active yet
|
|
return nil, errors.E(method, errors.Permission, "token is either expired or not yet valid")
|
|
} else {
|
|
return nil, errors.E(method, errors.Internal, "unknown error with token")
|
|
}
|
|
} else {
|
|
return nil, errors.E(method, errors.Permission, "token is not valid")
|
|
}
|
|
}
|
|
|
|
func CreateJWTToken(claims quartermaster.Claims) (string, error) {
|
|
const method = errors.Method("jwt/CreateJWTToken")
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenString, err := token.SignedString(secretKey)
|
|
|
|
if err != nil {
|
|
return "", errors.E(method, errors.Internal, "error signing token", err)
|
|
}
|
|
|
|
return tokenString, nil
|
|
}
|