Codebase list golang-github-go-kit-kit / 94628c7
Refactor away from passing a function to passing a struct with multiple options for signing keys Brian Kassouf 7 years ago
1 changed file(s) with 41 addition(s) and 12 deletion(s). Raw diff Collapse all Expand all
11
22 import (
33 "errors"
4 "fmt"
5 "reflect"
64
75 "golang.org/x/net/context"
86
1715 JWTClaimsContextKey = "JWTClaims"
1816 )
1917
18 var (
19 ErrTokenContextMissing = errors.New("Token up for parsing was not passed through the context")
20 ErrTokenInvalid = errors.New("JWT Token was invalid")
21 ErrUnexpectedSigningMethod = errors.New("Unexptected signing method")
22 ErrKIDNotFound = errors.New("Key ID was not found in key set")
23 ErrNoKIDHeader = errors.New("Token doesn't have 'kid' header")
24 )
25
26 type Claims map[string]interface{}
27
28 type KeySet map[string]struct {
29 Method jwt.SigningMethod
30 Key []byte
31 }
32
2033 // Create a new JWT token generating middleware, specifying signing method and the claims
2134 // you would like it to contain. Particularly useful for clients.
22 func NewSigner(key string, method jwt.SigningMethod, claims jwt.Claims) endpoint.Middleware {
35 func NewSigner(kid string, keys KeySet, claims Claims) endpoint.Middleware {
2336 return func(next endpoint.Endpoint) endpoint.Endpoint {
2437 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
25 token := jwt.NewWithClaims(method, claims)
38 key, ok := keys[kid]
39 if !ok {
40 return nil, ErrKIDNotFound
41 }
2642
43 token := jwt.NewWithClaims(key.Method, jwt.MapClaims(claims))
44 token.Header["kid"] = kid
2745 // Sign and get the complete encoded token as a string using the secret
28 tokenString, err := token.SignedString([]byte(key))
46 tokenString, err := token.SignedString(key.Key)
2947 if err != nil {
3048 return nil, err
3149 }
3957 // Create a new JWT token parsing middleware, specifying a jwt.Keyfunc interface and the
4058 // signing method. Adds the resulting claims to endpoint context or returns error on invalid
4159 // token. Particularly useful for servers.
42 func NewParser(keyFunc jwt.Keyfunc, method jwt.SigningMethod) endpoint.Middleware {
60 func NewParser(keys KeySet) endpoint.Middleware {
4361 return func(next endpoint.Endpoint) endpoint.Endpoint {
4462 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
4563 // tokenString is stored in the context from the transport handlers
4664 tokenString, ok := ctx.Value(JWTTokenContextKey).(string)
4765 if !ok {
48 return nil, errors.New("Token up for parsing was not passed through the context")
66 return nil, ErrTokenContextMissing
4967 }
5068
5169 // Parse takes the token string and a function for looking up the key. The latter is especially
5371 // head of the token to identify which key to use, but the parsed token (head and claims) is provided
5472 // to the callback, providing flexibility.
5573 token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
74 kid, ok := token.Header["kid"]
75 if !ok {
76 return nil, ErrNoKIDHeader
77 }
78
79 key, ok := keys[kid.(string)]
80 if !ok {
81 return nil, ErrKIDNotFound
82 }
83
5684 // Don't forget to validate the alg is what you expect:
57 if reflect.TypeOf(token.Method) != reflect.TypeOf(method) {
58 return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
85 if token.Method != key.Method {
86 return nil, ErrUnexpectedSigningMethod
5987 }
60 return keyFunc(token)
88
89 return key.Key, nil
6190 })
6291 if err != nil {
6392 return nil, err
6493 }
6594
6695 if !token.Valid {
67 return nil, errors.New("Could not parse JWT Token")
96 return nil, ErrTokenInvalid
6897 }
6998
7099 if claims, ok := token.Claims.(jwt.MapClaims); ok {
71 ctx = context.WithValue(ctx, JWTClaimsContextKey, claims)
100 ctx = context.WithValue(ctx, JWTClaimsContextKey, Claims(claims))
72101 }
73102
74103 return next(ctx, request)