From b7dd1251148e5b58fdaab58bcd9220bab306cfcc Mon Sep 17 00:00:00 2001 From: Stefan Borsje Date: Fri, 17 Dec 2021 18:29:45 +0100 Subject: [PATCH] Allow JWT middleware to gracefully fail when CredentialsOptional is set to true --- middleware/jwt.go | 11 ++++++++++ middleware/jwt_test.go | 50 ++++++++++++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/middleware/jwt.go b/middleware/jwt.go index 21e33ab82..4558afa22 100644 --- a/middleware/jwt.go +++ b/middleware/jwt.go @@ -32,6 +32,10 @@ type ( // ErrorHandlerWithContext is almost identical to ErrorHandler, but it's passed the current context. ErrorHandlerWithContext JWTErrorHandlerWithContext + // A boolean indicating if the credentials are required or not + // Default value: false + CredentialsOptional bool + // Signing key to validate token. // This is one of the three options to provide a token validation key. // The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey. @@ -218,6 +222,10 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc { } // If none of extractor has a token, handle error if err != nil { + if config.CredentialsOptional { + return next(c) + } + if config.ErrorHandler != nil { return config.ErrorHandler(err) } @@ -237,6 +245,9 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc { } return next(c) } + if config.CredentialsOptional { + return next(c) + } if config.ErrorHandler != nil { return config.ErrorHandler(err) } diff --git a/middleware/jwt_test.go b/middleware/jwt_test.go index 5f36ce0a5..aad574815 100644 --- a/middleware/jwt_test.go +++ b/middleware/jwt_test.go @@ -75,14 +75,15 @@ func TestJWT(t *testing.T) { validAuth := DefaultJWTConfig.AuthScheme + " " + token for _, tc := range []struct { - expPanic bool - expErrCode int // 0 for Success - config JWTConfig - reqURL string // "/" if empty - hdrAuth string - hdrCookie string // test.Request doesn't provide SetCookie(); use name=val - formValues map[string]string - info string + expPanic bool + expErrCode int // 0 for Success + expEmptyCtx bool + config JWTConfig + reqURL string // "/" if empty + hdrAuth string + hdrCookie string // test.Request doesn't provide SetCookie(); use name=val + formValues map[string]string + info string }{ { expPanic: true, @@ -266,6 +267,17 @@ func TestJWT(t *testing.T) { config: JWTConfig{SigningKey: validKey}, info: "Valid JWT with lower case AuthScheme", }, + { + expEmptyCtx: true, + config: JWTConfig{SigningKey: validKey, CredentialsOptional: true}, + info: "Valid JWT", + }, + { + expEmptyCtx: false, + hdrAuth: validAuth, + config: JWTConfig{SigningKey: validKey, CredentialsOptional: true}, + info: "Valid JWT", + }, } { if tc.reqURL == "" { tc.reqURL = "/" @@ -309,15 +321,19 @@ func TestJWT(t *testing.T) { h := JWTWithConfig(tc.config)(handler) if assert.NoError(t, h(c), tc.info) { - user := c.Get("user").(*jwt.Token) - switch claims := user.Claims.(type) { - case jwt.MapClaims: - assert.Equal(t, claims["name"], "John Doe", tc.info) - case *jwtCustomClaims: - assert.Equal(t, claims.Name, "John Doe", tc.info) - assert.Equal(t, claims.Admin, true, tc.info) - default: - panic("unexpected type of claims") + if tc.expEmptyCtx { + assert.Nil(t, c.Get("user"), tc.info) + } else { + user := c.Get("user").(*jwt.Token) + switch claims := user.Claims.(type) { + case jwt.MapClaims: + assert.Equal(t, claims["name"], "John Doe", tc.info) + case *jwtCustomClaims: + assert.Equal(t, claims.Name, "John Doe", tc.info) + assert.Equal(t, claims.Admin, true, tc.info) + default: + panic("unexpected type of claims") + } } } }