mirror of
https://github.com/labstack/echo.git
synced 2024-12-24 20:14:31 +02:00
Provide possibility to use key ids (#1289)
* provide possibility to use key ids * kid tests
This commit is contained in:
parent
b1bc80528b
commit
e2671fe963
@ -26,10 +26,14 @@ type (
|
|||||||
// It may be used to define a custom JWT error.
|
// It may be used to define a custom JWT error.
|
||||||
ErrorHandler JWTErrorHandler
|
ErrorHandler JWTErrorHandler
|
||||||
|
|
||||||
// Signing key to validate token.
|
// Signing key to validate token. Used as fallback if SigningKeys has length 0.
|
||||||
// Required.
|
// Required. This or SigningKeys.
|
||||||
SigningKey interface{}
|
SigningKey interface{}
|
||||||
|
|
||||||
|
// Map of signing keys to validate token with kid field usage.
|
||||||
|
// Required. This or SigningKey.
|
||||||
|
SigningKeys map[string]interface{}
|
||||||
|
|
||||||
// Signing method, used to check token signing method.
|
// Signing method, used to check token signing method.
|
||||||
// Optional. Default value HS256.
|
// Optional. Default value HS256.
|
||||||
SigningMethod string
|
SigningMethod string
|
||||||
@ -110,7 +114,7 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
|||||||
if config.Skipper == nil {
|
if config.Skipper == nil {
|
||||||
config.Skipper = DefaultJWTConfig.Skipper
|
config.Skipper = DefaultJWTConfig.Skipper
|
||||||
}
|
}
|
||||||
if config.SigningKey == nil {
|
if config.SigningKey == nil && len(config.SigningKeys) == 0 {
|
||||||
panic("echo: jwt middleware requires signing key")
|
panic("echo: jwt middleware requires signing key")
|
||||||
}
|
}
|
||||||
if config.SigningMethod == "" {
|
if config.SigningMethod == "" {
|
||||||
@ -133,6 +137,15 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
|||||||
if t.Method.Alg() != config.SigningMethod {
|
if t.Method.Alg() != config.SigningMethod {
|
||||||
return nil, fmt.Errorf("unexpected jwt signing method=%v", t.Header["alg"])
|
return nil, fmt.Errorf("unexpected jwt signing method=%v", t.Header["alg"])
|
||||||
}
|
}
|
||||||
|
if len(config.SigningKeys) > 0 {
|
||||||
|
if kid, ok := t.Header["kid"].(string); ok {
|
||||||
|
if key, ok := config.SigningKeys[kid]; ok {
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected jwt key id=%v", t.Header["kid"])
|
||||||
|
}
|
||||||
|
|
||||||
return config.SigningKey, nil
|
return config.SigningKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,3 +224,93 @@ func TestJWT(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestJWTwithKID(t *testing.T) {
|
||||||
|
test := assert.New(t)
|
||||||
|
|
||||||
|
e := echo.New()
|
||||||
|
handler := func(c echo.Context) error {
|
||||||
|
return c.String(http.StatusOK, "test")
|
||||||
|
}
|
||||||
|
firstToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6ImZpcnN0T25lIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.w5VGpHOe0jlNgf7jMVLHzIYH_XULmpUlreJnilwSkWk"
|
||||||
|
secondToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6InNlY29uZE9uZSJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.sdghDYQ85jdh0hgQ6bKbMguLI_NSPYWjkhVJkee-yZM"
|
||||||
|
wrongToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6InNlY29uZE9uZSJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.RyhLybtVLpoewF6nz9YN79oXo32kAtgUxp8FNwTkb90"
|
||||||
|
staticToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.1_-XFYUPpJfgsaGwYhgZEt7hfySMg-a3GN-nfZmbW7o"
|
||||||
|
validKeys := map[string]interface{}{"firstOne": []byte("first_secret"), "secondOne": []byte("second_secret")}
|
||||||
|
invalidKeys := map[string]interface{}{"thirdOne": []byte("third_secret")}
|
||||||
|
staticSecret := []byte("static_secret")
|
||||||
|
invalidStaticSecret := []byte("invalid_secret")
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
expErrCode int // 0 for Success
|
||||||
|
config JWTConfig
|
||||||
|
hdrAuth string
|
||||||
|
info string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
hdrAuth: DefaultJWTConfig.AuthScheme + " " + firstToken,
|
||||||
|
config: JWTConfig{SigningKeys: validKeys},
|
||||||
|
info: "First token valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hdrAuth: DefaultJWTConfig.AuthScheme + " " + secondToken,
|
||||||
|
config: JWTConfig{SigningKeys: validKeys},
|
||||||
|
info: "Second token valid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expErrCode: http.StatusUnauthorized,
|
||||||
|
hdrAuth: DefaultJWTConfig.AuthScheme + " " + wrongToken,
|
||||||
|
config: JWTConfig{SigningKeys: validKeys},
|
||||||
|
info: "Wrong key id token",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hdrAuth: DefaultJWTConfig.AuthScheme + " " + staticToken,
|
||||||
|
config: JWTConfig{SigningKey: staticSecret},
|
||||||
|
info: "Valid static secret token",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expErrCode: http.StatusUnauthorized,
|
||||||
|
hdrAuth: DefaultJWTConfig.AuthScheme + " " + staticToken,
|
||||||
|
config: JWTConfig{SigningKey: invalidStaticSecret},
|
||||||
|
info: "Invalid static secret",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expErrCode: http.StatusUnauthorized,
|
||||||
|
hdrAuth: DefaultJWTConfig.AuthScheme + " " + firstToken,
|
||||||
|
config: JWTConfig{SigningKeys: invalidKeys},
|
||||||
|
info: "Invalid keys first token",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expErrCode: http.StatusUnauthorized,
|
||||||
|
hdrAuth: DefaultJWTConfig.AuthScheme + " " + secondToken,
|
||||||
|
config: JWTConfig{SigningKeys: invalidKeys},
|
||||||
|
info: "Invalid keys second token",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||||
|
res := httptest.NewRecorder()
|
||||||
|
req.Header.Set(echo.HeaderAuthorization, tc.hdrAuth)
|
||||||
|
c := e.NewContext(req, res)
|
||||||
|
|
||||||
|
if tc.expErrCode != 0 {
|
||||||
|
h := JWTWithConfig(tc.config)(handler)
|
||||||
|
he := h(c).(*echo.HTTPError)
|
||||||
|
test.Equal(tc.expErrCode, he.Code, tc.info)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
h := JWTWithConfig(tc.config)(handler)
|
||||||
|
if test.NoError(h(c), tc.info) {
|
||||||
|
user := c.Get("user").(*jwt.Token)
|
||||||
|
switch claims := user.Claims.(type) {
|
||||||
|
case jwt.MapClaims:
|
||||||
|
test.Equal(claims["name"], "John Doe", tc.info)
|
||||||
|
case *jwtCustomClaims:
|
||||||
|
test.Equal(claims.Name, "John Doe", tc.info)
|
||||||
|
test.Equal(claims.Admin, true, tc.info)
|
||||||
|
default:
|
||||||
|
panic("unexpected type of claims")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user