Package list golang-github-go-kit-kit / b33aee6
go back to the keyfunc method of passing keys to support signers who don't control the KID header Brian Kassouf 5 years ago
2 changed file(s) with 32 addition(s) and 54 deletion(s). Raw diff Collapse all Expand all
88 "github.com/go-kit/kit/endpoint"
99 )
1010
11 type key string
11 type contextKey string
1212
1313 const (
1414 // JWTTokenContextKey holds the key used to store a JWT Token in the context
15 JWTTokenContextKey key = "JWTToken"
15 JWTTokenContextKey contextKey = "JWTToken"
1616 // JWTClaimsContxtKey holds the key used to store the JWT Claims in the context
17 JWTClaimsContextKey key = "JWTClaims"
17 JWTClaimsContextKey contextKey = "JWTClaims"
1818 )
1919
2020 var (
2121 ErrTokenContextMissing = errors.New("Token up for parsing was not passed through the context")
2222 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")
2326 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")
2627 )
2728
2829 type Claims map[string]interface{}
3435
3536 // Create a new JWT token generating middleware, specifying signing method and the claims
3637 // 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 {
3839 return func(next endpoint.Endpoint) endpoint.Endpoint {
3940 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
4443
45 token := jwt.NewWithClaims(key.Method, jwt.MapClaims(claims))
46 token.Header["kid"] = kid
4744 // 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)
4946 if err != nil {
5047 return nil, err
5148 }
5956 // Create a new JWT token parsing middleware, specifying a jwt.Keyfunc interface and the
6057 // signing method. Adds the resulting claims to endpoint context or returns error on invalid
6158 // token. Particularly useful for servers.
62 func NewParser(keys KeySet) endpoint.Middleware {
59 func NewParser(keyFunc jwt.Keyfunc, method jwt.SigningMethod) endpoint.Middleware {
6360 return func(next endpoint.Endpoint) endpoint.Endpoint {
6461 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
6562 // tokenString is stored in the context from the transport handlers
7370 // head of the token to identify which key to use, but the parsed token (head and claims) is provided
7471 // to the callback, providing flexibility.
7572 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
8673 // Don't forget to validate the alg is what you expect:
87 if token.Method != key.Method {
74 if token.Method != method {
8875 return nil, ErrUnexpectedSigningMethod
8976 }
9077
91 return key.Key, nil
78 return keyFunc(token)
9279 })
9380 if err != nil {
9481 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
9593 return nil, e.Inner
9694 }
9795
2020 func TestSigner(t *testing.T) {
2121 e := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil }
2222
23 keys := KeySet{
24 kid: {
25 Method: method,
26 Key: key,
27 },
28 }
29
30 signer := NewSigner(kid, keys, claims)(e)
23 signer := NewSigner(kid, key, method, claims)(e)
3124 ctx, err := signer(context.Background(), struct{}{})
3225 if err != nil {
3326 t.Fatalf("Signer returned error: %s", err)
4639 func TestJWTParser(t *testing.T) {
4740 e := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil }
4841
49 keys := KeySet{
50 kid: {
51 Method: method,
52 Key: key,
53 },
42 keys := func(token *jwt.Token) (interface{}, error) {
43 return key, nil
5444 }
5545
56 parser := NewParser(keys)(e)
46 parser := NewParser(keys, method)(e)
5747
5848 // No Token is passed into the parser
5949 _, err := parser(context.Background(), struct{}{})
7363 }
7464
7565 // Invalid Method is used in the parser
76 invalidMethodKeys := KeySet{
77 kid: {
78 Method: invalidMethod,
79 Key: key,
80 },
81 }
82
83 badParser := NewParser(invalidMethodKeys)(e)
66 badParser := NewParser(keys, invalidMethod)(e)
8467 ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey)
8568 _, err = badParser(ctx, struct{}{})
8669 if err == nil {
9275 }
9376
9477 // Invalid key is used in the parser
95 invalidKeys := KeySet{
96 kid: {
97 Method: method,
98 Key: []byte("bad"),
99 },
78 invalidKeys := func(token *jwt.Token) (interface{}, error) {
79 return []byte("bad"), nil
10080 }
10181
102 badParser = NewParser(invalidKeys)(e)
82 badParser = NewParser(invalidKeys, method)(e)
10383 ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey)
10484 _, err = badParser(ctx, struct{}{})
10585 if err == nil {