@@ -29,15 +29,19 @@ type (
2929 // ErrorHandlerWithContext is almost identical to ErrorHandler, but it's passed the current context.
3030 ErrorHandlerWithContext JWTErrorHandlerWithContext
3131
32- // Signing key to validate token. Used as fallback if SigningKeys has length 0.
33- // Required. This or SigningKeys.
32+ // Signing key to validate token.
33+ // This is one of the three options to provide a token validation key.
34+ // The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey.
35+ // Required if neither user-defined KeyFunc nor SigningKeys is provided.
3436 SigningKey interface {}
3537
3638 // Map of signing keys to validate token with kid field usage.
37- // Required. This or SigningKey.
39+ // This is one of the three options to provide a token validation key.
40+ // The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey.
41+ // Required if neither user-defined KeyFunc nor SigningKey is provided.
3842 SigningKeys map [string ]interface {}
3943
40- // Signing method, used to check token signing method .
44+ // Signing method used to check the token's signing algorithm .
4145 // Optional. Default value HS256.
4246 SigningMethod string
4347
@@ -64,7 +68,16 @@ type (
6468 // Optional. Default value "Bearer".
6569 AuthScheme string
6670
67- keyFunc jwt.Keyfunc
71+ // KeyFunc defines a user-defined function that supplies the public key for a token validation.
72+ // The function shall take care of verifying the signing algorithm and selecting the proper key.
73+ // A user-defined KeyFunc can be useful if tokens are issued by an external party.
74+ //
75+ // When a user-defined KeyFunc is provided, SigningKey, SigningKeys, and SigningMethod are ignored.
76+ // This is one of the three options to provide a token validation key.
77+ // The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey.
78+ // Required if neither SigningKeys nor SigningKey is provided.
79+ // Default to an internal implementation verifying the signing algorithm and selecting the proper key.
80+ KeyFunc jwt.Keyfunc
6881 }
6982
7083 // JWTSuccessHandler defines a function which is executed for a valid token.
99112 TokenLookup : "header:" + echo .HeaderAuthorization ,
100113 AuthScheme : "Bearer" ,
101114 Claims : jwt.MapClaims {},
115+ KeyFunc : nil ,
102116 }
103117)
104118
@@ -123,7 +137,7 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
123137 if config .Skipper == nil {
124138 config .Skipper = DefaultJWTConfig .Skipper
125139 }
126- if config .SigningKey == nil && len (config .SigningKeys ) == 0 {
140+ if config .SigningKey == nil && len (config .SigningKeys ) == 0 && config . KeyFunc == nil {
127141 panic ("echo: jwt middleware requires signing key" )
128142 }
129143 if config .SigningMethod == "" {
@@ -141,21 +155,8 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
141155 if config .AuthScheme == "" {
142156 config .AuthScheme = DefaultJWTConfig .AuthScheme
143157 }
144- config .keyFunc = func (t * jwt.Token ) (interface {}, error ) {
145- // Check the signing method
146- if t .Method .Alg () != config .SigningMethod {
147- return nil , fmt .Errorf ("unexpected jwt signing method=%v" , t .Header ["alg" ])
148- }
149- if len (config .SigningKeys ) > 0 {
150- if kid , ok := t .Header ["kid" ].(string ); ok {
151- if key , ok := config .SigningKeys [kid ]; ok {
152- return key , nil
153- }
154- }
155- return nil , fmt .Errorf ("unexpected jwt key id=%v" , t .Header ["kid" ])
156- }
157-
158- return config .SigningKey , nil
158+ if config .KeyFunc == nil {
159+ config .KeyFunc = config .defaultKeyFunc
159160 }
160161
161162 // Initialize
@@ -196,11 +197,11 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
196197 token := new (jwt.Token )
197198 // Issue #647, #656
198199 if _ , ok := config .Claims .(jwt.MapClaims ); ok {
199- token , err = jwt .Parse (auth , config .keyFunc )
200+ token , err = jwt .Parse (auth , config .KeyFunc )
200201 } else {
201202 t := reflect .ValueOf (config .Claims ).Type ().Elem ()
202203 claims := reflect .New (t ).Interface ().(jwt.Claims )
203- token , err = jwt .ParseWithClaims (auth , claims , config .keyFunc )
204+ token , err = jwt .ParseWithClaims (auth , claims , config .KeyFunc )
204205 }
205206 if err == nil && token .Valid {
206207 // Store user information from token into context.
@@ -225,6 +226,24 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
225226 }
226227}
227228
229+ // defaultKeyFunc returns a signing key of the given token.
230+ func (config * JWTConfig ) defaultKeyFunc (t * jwt.Token ) (interface {}, error ) {
231+ // Check the signing method
232+ if t .Method .Alg () != config .SigningMethod {
233+ return nil , fmt .Errorf ("unexpected jwt signing method=%v" , t .Header ["alg" ])
234+ }
235+ if len (config .SigningKeys ) > 0 {
236+ if kid , ok := t .Header ["kid" ].(string ); ok {
237+ if key , ok := config .SigningKeys [kid ]; ok {
238+ return key , nil
239+ }
240+ }
241+ return nil , fmt .Errorf ("unexpected jwt key id=%v" , t .Header ["kid" ])
242+ }
243+
244+ return config .SigningKey , nil
245+ }
246+
228247// jwtFromHeader returns a `jwtExtractor` that extracts token from the request header.
229248func jwtFromHeader (header string , authScheme string ) jwtExtractor {
230249 return func (c echo.Context ) (string , error ) {
0 commit comments