...

Source file src/github.com/go-kit/kit/auth/jwt/middleware.go

Documentation: github.com/go-kit/kit/auth/jwt

     1  package jwt
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  
     7  	"github.com/go-kit/kit/endpoint"
     8  	"github.com/golang-jwt/jwt/v4"
     9  )
    10  
    11  type contextKey string
    12  
    13  const (
    14  	// JWTContextKey holds the key used to store a JWT in the context.
    15  	JWTContextKey contextKey = "JWTToken"
    16  
    17  	// JWTTokenContextKey is an alias for JWTContextKey.
    18  	//
    19  	// Deprecated: prefer JWTContextKey.
    20  	JWTTokenContextKey = JWTContextKey
    21  
    22  	// JWTClaimsContextKey holds the key used to store the JWT Claims in the
    23  	// context.
    24  	JWTClaimsContextKey contextKey = "JWTClaims"
    25  )
    26  
    27  var (
    28  	// ErrTokenContextMissing denotes a token was not passed into the parsing
    29  	// middleware's context.
    30  	ErrTokenContextMissing = errors.New("token up for parsing was not passed through the context")
    31  
    32  	// ErrTokenInvalid denotes a token was not able to be validated.
    33  	ErrTokenInvalid = errors.New("JWT was invalid")
    34  
    35  	// ErrTokenExpired denotes a token's expire header (exp) has since passed.
    36  	ErrTokenExpired = errors.New("JWT is expired")
    37  
    38  	// ErrTokenMalformed denotes a token was not formatted as a JWT.
    39  	ErrTokenMalformed = errors.New("JWT is malformed")
    40  
    41  	// ErrTokenNotActive denotes a token's not before header (nbf) is in the
    42  	// future.
    43  	ErrTokenNotActive = errors.New("token is not valid yet")
    44  
    45  	// ErrUnexpectedSigningMethod denotes a token was signed with an unexpected
    46  	// signing method.
    47  	ErrUnexpectedSigningMethod = errors.New("unexpected signing method")
    48  )
    49  
    50  // NewSigner creates a new JWT generating middleware, specifying key ID,
    51  // signing string, signing method and the claims you would like it to contain.
    52  // Tokens are signed with a Key ID header (kid) which is useful for determining
    53  // the key to use for parsing. Particularly useful for clients.
    54  func NewSigner(kid string, key []byte, method jwt.SigningMethod, claims jwt.Claims) endpoint.Middleware {
    55  	return func(next endpoint.Endpoint) endpoint.Endpoint {
    56  		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
    57  			token := jwt.NewWithClaims(method, claims)
    58  			token.Header["kid"] = kid
    59  
    60  			// Sign and get the complete encoded token as a string using the secret
    61  			tokenString, err := token.SignedString(key)
    62  			if err != nil {
    63  				return nil, err
    64  			}
    65  			ctx = context.WithValue(ctx, JWTContextKey, tokenString)
    66  
    67  			return next(ctx, request)
    68  		}
    69  	}
    70  }
    71  
    72  // ClaimsFactory is a factory for jwt.Claims.
    73  // Useful in NewParser middleware.
    74  type ClaimsFactory func() jwt.Claims
    75  
    76  // MapClaimsFactory is a ClaimsFactory that returns
    77  // an empty jwt.MapClaims.
    78  func MapClaimsFactory() jwt.Claims {
    79  	return jwt.MapClaims{}
    80  }
    81  
    82  // StandardClaimsFactory is a ClaimsFactory that returns
    83  // an empty jwt.StandardClaims.
    84  func StandardClaimsFactory() jwt.Claims {
    85  	return &jwt.StandardClaims{}
    86  }
    87  
    88  // NewParser creates a new JWT parsing middleware, specifying a
    89  // jwt.Keyfunc interface, the signing method and the claims type to be used. NewParser
    90  // adds the resulting claims to endpoint context or returns error on invalid token.
    91  // Particularly useful for servers.
    92  func NewParser(keyFunc jwt.Keyfunc, method jwt.SigningMethod, newClaims ClaimsFactory) endpoint.Middleware {
    93  	return func(next endpoint.Endpoint) endpoint.Endpoint {
    94  		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
    95  			// tokenString is stored in the context from the transport handlers.
    96  			tokenString, ok := ctx.Value(JWTContextKey).(string)
    97  			if !ok {
    98  				return nil, ErrTokenContextMissing
    99  			}
   100  
   101  			// Parse takes the token string and a function for looking up the
   102  			// key. The latter is especially useful if you use multiple keys
   103  			// for your application.  The standard is to use 'kid' in the head
   104  			// of the token to identify which key to use, but the parsed token
   105  			// (head and claims) is provided to the callback, providing
   106  			// flexibility.
   107  			token, err := jwt.ParseWithClaims(tokenString, newClaims(), func(token *jwt.Token) (interface{}, error) {
   108  				// Don't forget to validate the alg is what you expect:
   109  				if token.Method != method {
   110  					return nil, ErrUnexpectedSigningMethod
   111  				}
   112  
   113  				return keyFunc(token)
   114  			})
   115  			if err != nil {
   116  				if e, ok := err.(*jwt.ValidationError); ok {
   117  					switch {
   118  					case e.Errors&jwt.ValidationErrorMalformed != 0:
   119  						// Token is malformed
   120  						return nil, ErrTokenMalformed
   121  					case e.Errors&jwt.ValidationErrorExpired != 0:
   122  						// Token is expired
   123  						return nil, ErrTokenExpired
   124  					case e.Errors&jwt.ValidationErrorNotValidYet != 0:
   125  						// Token is not active yet
   126  						return nil, ErrTokenNotActive
   127  					case e.Inner != nil:
   128  						// report e.Inner
   129  						return nil, e.Inner
   130  					}
   131  					// We have a ValidationError but have no specific Go kit error for it.
   132  					// Fall through to return original error.
   133  				}
   134  				return nil, err
   135  			}
   136  
   137  			if !token.Valid {
   138  				return nil, ErrTokenInvalid
   139  			}
   140  
   141  			ctx = context.WithValue(ctx, JWTClaimsContextKey, token.Claims)
   142  
   143  			return next(ctx, request)
   144  		}
   145  	}
   146  }
   147  

View as plain text