8 | 8 |
"github.com/go-kit/kit/endpoint"
|
9 | 9 |
)
|
10 | 10 |
|
11 | |
type key string
|
|
11 |
type contextKey string
|
12 | 12 |
|
13 | 13 |
const (
|
14 | 14 |
// JWTTokenContextKey holds the key used to store a JWT Token in the context
|
15 | |
JWTTokenContextKey key = "JWTToken"
|
|
15 |
JWTTokenContextKey contextKey = "JWTToken"
|
16 | 16 |
// JWTClaimsContxtKey holds the key used to store the JWT Claims in the context
|
17 | |
JWTClaimsContextKey key = "JWTClaims"
|
|
17 |
JWTClaimsContextKey contextKey = "JWTClaims"
|
18 | 18 |
)
|
19 | 19 |
|
20 | 20 |
var (
|
21 | 21 |
ErrTokenContextMissing = errors.New("Token up for parsing was not passed through the context")
|
22 | 22 |
ErrTokenInvalid = errors.New("JWT Token was invalid")
|
|
23 |
ErrTokenExpired = errors.New("JWT Token is expired")
|
|
24 |
ErrTokenMalformed = errors.New("JWT Token is malformed")
|
|
25 |
ErrTokenNotActive = errors.New("Token is not valid yet")
|
23 | 26 |
ErrUnexpectedSigningMethod = errors.New("Unexpected signing method")
|
24 | |
ErrKIDNotFound = errors.New("Key ID was not found in key set")
|
25 | |
ErrNoKIDHeader = errors.New("Token doesn't have 'kid' header")
|
26 | 27 |
)
|
27 | 28 |
|
28 | 29 |
type Claims map[string]interface{}
|
|
34 | 35 |
|
35 | 36 |
// Create a new JWT token generating middleware, specifying signing method and the claims
|
36 | 37 |
// you would like it to contain. Particularly useful for clients.
|
37 | |
func NewSigner(kid string, keys KeySet, claims Claims) endpoint.Middleware {
|
|
38 |
func NewSigner(kid string, key []byte, method jwt.SigningMethod, claims Claims) endpoint.Middleware {
|
38 | 39 |
return func(next endpoint.Endpoint) endpoint.Endpoint {
|
39 | 40 |
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
40 | |
key, ok := keys[kid]
|
41 | |
if !ok {
|
42 | |
return nil, ErrKIDNotFound
|
43 | |
}
|
|
41 |
token := jwt.NewWithClaims(method, jwt.MapClaims(claims))
|
|
42 |
token.Header["kid"] = kid
|
44 | 43 |
|
45 | |
token := jwt.NewWithClaims(key.Method, jwt.MapClaims(claims))
|
46 | |
token.Header["kid"] = kid
|
47 | 44 |
// Sign and get the complete encoded token as a string using the secret
|
48 | |
tokenString, err := token.SignedString(key.Key)
|
|
45 |
tokenString, err := token.SignedString(key)
|
49 | 46 |
if err != nil {
|
50 | 47 |
return nil, err
|
51 | 48 |
}
|
|
59 | 56 |
// Create a new JWT token parsing middleware, specifying a jwt.Keyfunc interface and the
|
60 | 57 |
// signing method. Adds the resulting claims to endpoint context or returns error on invalid
|
61 | 58 |
// token. Particularly useful for servers.
|
62 | |
func NewParser(keys KeySet) endpoint.Middleware {
|
|
59 |
func NewParser(keyFunc jwt.Keyfunc, method jwt.SigningMethod) endpoint.Middleware {
|
63 | 60 |
return func(next endpoint.Endpoint) endpoint.Endpoint {
|
64 | 61 |
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
65 | 62 |
// tokenString is stored in the context from the transport handlers
|
|
73 | 70 |
// head of the token to identify which key to use, but the parsed token (head and claims) is provided
|
74 | 71 |
// to the callback, providing flexibility.
|
75 | 72 |
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
76 | |
kid, ok := token.Header["kid"]
|
77 | |
if !ok {
|
78 | |
return nil, ErrNoKIDHeader
|
79 | |
}
|
80 | |
|
81 | |
key, ok := keys[kid.(string)]
|
82 | |
if !ok {
|
83 | |
return nil, ErrKIDNotFound
|
84 | |
}
|
85 | |
|
86 | 73 |
// Don't forget to validate the alg is what you expect:
|
87 | |
if token.Method != key.Method {
|
|
74 |
if token.Method != method {
|
88 | 75 |
return nil, ErrUnexpectedSigningMethod
|
89 | 76 |
}
|
90 | 77 |
|
91 | |
return key.Key, nil
|
|
78 |
return keyFunc(token)
|
92 | 79 |
})
|
93 | 80 |
if err != nil {
|
94 | 81 |
if e, ok := err.(*jwt.ValidationError); ok && e.Inner != nil {
|
|
82 |
if e.Errors&jwt.ValidationErrorMalformed != 0 {
|
|
83 |
// Token is malformed
|
|
84 |
return nil, ErrTokenMalformed
|
|
85 |
} else if e.Errors&jwt.ValidationErrorExpired != 0 {
|
|
86 |
// Token is expired
|
|
87 |
return nil, ErrTokenExpired
|
|
88 |
} else if e.Errors&jwt.ValidationErrorNotValidYet != 0 {
|
|
89 |
// Token is not active yet
|
|
90 |
return nil, ErrTokenNotActive
|
|
91 |
}
|
|
92 |
|
95 | 93 |
return nil, e.Inner
|
96 | 94 |
}
|
97 | 95 |
|