Codebase list golang-github-go-kit-kit / d51e546
Add *WithClaims methods to jwt middleware for more advanced usage. Cameron Stitt 6 years ago
2 changed file(s) with 69 addition(s) and 25 deletion(s). Raw diff Collapse all Expand all
4646 // Claims is a map of arbitrary claim data.
4747 type Claims map[string]interface{}
4848
49 // NewSigner creates a new JWT token generating middleware, specifying key ID,
50 // signing string, signing method and the claims you would like it to contain.
49 // NewSignerWithClaims creates a new JWT token generating middleware, specifying key ID,
50 // signing string, signing method and the jwt.Claims you would like it to contain.
5151 // Tokens are signed with a Key ID header (kid) which is useful for determining
5252 // the key to use for parsing. Particularly useful for clients.
53 func NewSigner(kid string, key []byte, method jwt.SigningMethod, claims Claims) endpoint.Middleware {
53 func NewSignerWithClaims(kid string, key []byte, method jwt.SigningMethod, claims jwt.Claims) endpoint.Middleware {
5454 return func(next endpoint.Endpoint) endpoint.Endpoint {
5555 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
56 token := jwt.NewWithClaims(method, jwt.MapClaims(claims))
56 token := jwt.NewWithClaims(method, claims)
5757 token.Header["kid"] = kid
5858
5959 // Sign and get the complete encoded token as a string using the secret
6868 }
6969 }
7070
71 // NewParser creates a new JWT token parsing middleware, specifying a
72 // jwt.Keyfunc interface and the signing method. NewParser adds the resulting
73 // claims to endpoint context or returns error on invalid token. Particularly
74 // useful for servers.
75 func NewParser(keyFunc jwt.Keyfunc, method jwt.SigningMethod) endpoint.Middleware {
71 // NewSigner creates a new JWT token generating middleware, specifying key ID,
72 // signing string, signing method and the claims you would like it to contain.
73 // It passes these values onto NewSignerWithClaims to handle the signing process.
74 func NewSigner(kid string, key []byte, method jwt.SigningMethod, claims Claims) endpoint.Middleware {
75 return NewSignerWithClaims(kid, key, method, jwt.MapClaims(claims))
76 }
77
78 // NewParserWithClaims creates a new JWT token parsing middleware, specifying a
79 // jwt.Keyfunc interface, the signing method as well as the claims to parse into.
80 // NewParserWithClaims adds the resulting claims to endpoint context or returns error on invalid token.
81 // Particularly useful for servers.
82 func NewParserWithClaims(keyFunc jwt.Keyfunc, method jwt.SigningMethod, claims jwt.Claims) endpoint.Middleware {
7683 return func(next endpoint.Endpoint) endpoint.Endpoint {
7784 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
7885 // tokenString is stored in the context from the transport handlers.
8794 // of the token to identify which key to use, but the parsed token
8895 // (head and claims) is provided to the callback, providing
8996 // flexibility.
90 token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
97 token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
9198 // Don't forget to validate the alg is what you expect:
9299 if token.Method != method {
93100 return nil, ErrUnexpectedSigningMethod
118125 return nil, ErrTokenInvalid
119126 }
120127
121 if claims, ok := token.Claims.(jwt.MapClaims); ok {
122 ctx = context.WithValue(ctx, JWTClaimsContextKey, Claims(claims))
128 if tokenClaims, ok := token.Claims.(jwt.MapClaims); ok {
129 ctx = context.WithValue(ctx, JWTClaimsContextKey, Claims(tokenClaims))
130 } else {
131 ctx = context.WithValue(ctx, JWTClaimsContextKey, token.Claims)
123132 }
124133
125134 return next(ctx, request)
126135 }
127136 }
128137 }
138
139 // NewParser creates a new JWT token parsing middleware, specifying a
140 // jwt.KeyFunc interface and the signing method. It will utilize NewParserWithClaims
141 // and fall back to implementing the jwt.MapClaims type.
142 func NewParser(keyFunc jwt.Keyfunc, method jwt.SigningMethod) endpoint.Middleware {
143 return NewParserWithClaims(keyFunc, method, jwt.MapClaims{})
144 }
44 "testing"
55
66 jwt "github.com/dgrijalva/jwt-go"
7 "github.com/go-kit/kit/endpoint"
78 )
89
910 var (
10 kid = "kid"
11 key = []byte("test_signing_key")
12 method = jwt.SigningMethodHS256
13 invalidMethod = jwt.SigningMethodRS256
14 claims = Claims{"user": "go-kit"}
11 kid = "kid"
12 key = []byte("test_signing_key")
13 method = jwt.SigningMethodHS256
14 invalidMethod = jwt.SigningMethodRS256
15 claims = Claims{"user": "go-kit"}
16 mapClaims = jwt.MapClaims{"user": "go-kit"}
17 standardClaims = jwt.StandardClaims{Audience: "go-kit"}
1518 // Signed tokens generated at https://jwt.io/
16 signedKey = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtpZCIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZ28ta2l0In0.14M2VmYyApdSlV_LZ88ajjwuaLeIFplB8JpyNy0A19E"
17 invalidKey = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.e30.vKVCKto-Wn6rgz3vBdaZaCBGfCBDTXOENSo_X2Gq7qA"
19 signedKey = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtpZCIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZ28ta2l0In0.14M2VmYyApdSlV_LZ88ajjwuaLeIFplB8JpyNy0A19E"
20 standardSignedKey = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtpZCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJnby1raXQifQ.L5ypIJjCOOv3jJ8G5SelaHvR04UJuxmcBN5QW3m_aoY"
21 invalidKey = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.e30.vKVCKto-Wn6rgz3vBdaZaCBGfCBDTXOENSo_X2Gq7qA"
1822 )
1923
20 func TestSigner(t *testing.T) {
21 e := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil }
22
23 signer := NewSigner(kid, key, method, claims)(e)
24 func signingValidator(t *testing.T, signer endpoint.Endpoint, expectedKey string) {
2425 ctx, err := signer(context.Background(), struct{}{})
2526 if err != nil {
2627 t.Fatalf("Signer returned error: %s", err)
3132 t.Fatal("Token did not exist in context")
3233 }
3334
34 if token != signedKey {
35 t.Fatalf("JWT tokens did not match: expecting %s got %s", signedKey, token)
35 if token != expectedKey {
36 t.Fatalf("JWT tokens did not match: expecting %s got %s", expectedKey, token)
3637 }
38 }
39
40 func TestNewSigner(t *testing.T) {
41 e := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil }
42
43 signer := NewSigner(kid, key, method, claims)(e)
44 signingValidator(t, signer, signedKey)
45
46 signer = NewSignerWithClaims(kid, key, method, mapClaims)(e)
47 signingValidator(t, signer, signedKey)
48
49 signer = NewSignerWithClaims(kid, key, method, standardClaims)(e)
50 signingValidator(t, signer, standardSignedKey)
3751 }
3852
3953 func TestJWTParser(t *testing.T) {
101115 if cl["user"] != claims["user"] {
102116 t.Fatalf("JWT Claims.user did not match: expecting %s got %s", claims["user"], cl["user"])
103117 }
118
119 parser = NewParserWithClaims(keys, method, &jwt.StandardClaims{})(e)
120 ctx = context.WithValue(context.Background(), JWTTokenContextKey, standardSignedKey)
121 ctx1, err = parser(ctx, struct{}{})
122 if err != nil {
123 t.Fatalf("Parser returned error: %s", err)
124 }
125 stdCl, ok := ctx1.(context.Context).Value(JWTClaimsContextKey).(*jwt.StandardClaims)
126 if !ok {
127 t.Fatal("Claims were not passed into context correctly")
128 }
129 if !stdCl.VerifyAudience("go-kit", true) {
130 t.Fatal("JWT jwt.StandardClaims.Audience did not match: expecting %s got %s", standardClaims.Audience, stdCl.Audience)
131 }
104132 }