diff --git a/cookbook/jwt/user-defined-keyfunc/server.go b/cookbook/jwt/user-defined-keyfunc/server.go new file mode 100644 index 00000000..f51768ca --- /dev/null +++ b/cookbook/jwt/user-defined-keyfunc/server.go @@ -0,0 +1,66 @@ +package main + +import ( + "context" + "errors" + "fmt" + "net/http" + + jwt "github.com/dgrijalva/jwt-go" + echo "github.com/labstack/echo/v4" + middleware "github.com/labstack/echo/v4/middleware" + jwk "github.com/lestrrat-go/jwx/jwk" +) + +func getKey(token *jwt.Token) (interface{}, error) { + + // Note: this downloads the keyset every time. + keySet, err := jwk.Fetch(context.Background(), "https://www.googleapis.com/oauth2/v3/certs") + if err != nil { + return nil, err + } + + keyID, ok := token.Header["kid"].(string) + if !ok { + return nil, errors.New("expecting JWT header to have a key ID in the kid field") + } + + key, found := keySet.LookupKeyID(keyID) + + if !found { + return nil, fmt.Errorf("unable to find key %q", keyID) + } + + var pubkey interface{} + if err := key.Raw(&pubkey); err != nil { + return nil, fmt.Errorf("Unable to get the public key. Error: %s", err.Error()) + } + + return pubkey, nil +} + +func restricted(c echo.Context) error { + user := c.Get("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + name := claims["name"].(string) + return c.String(http.StatusOK, "Welcome "+name+"!") +} + +func main() { + e := echo.New() + + // Middleware + e.Use(middleware.Logger()) + e.Use(middleware.Recover()) + + // Restricted group + r := e.Group("/restricted") + + config := middleware.JWTConfig{ + KeyFunc: getKey, + } + r.Use(middleware.JWTWithConfig(config)) + r.GET("", restricted) + + e.Logger.Fatal(e.Start(":1323")) +} diff --git a/website/content/cookbook/jwt.md b/website/content/cookbook/jwt.md index 8ece0493..f875f49e 100644 --- a/website/content/cookbook/jwt.md +++ b/website/content/cookbook/jwt.md @@ -56,6 +56,13 @@ curl localhost:1323/restricted -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR Welcome Jon Snow! ``` +## Server using a user-defined KeyFunc + +`server.go` + +{{< embed "jwt/user-defined-keyfunc/server.go" >}} + + ## [Source Code]({{< source "jwt" >}}) ## Maintainers diff --git a/website/content/middleware/jwt.md b/website/content/middleware/jwt.md index 4568d0c5..86e0bd39 100644 --- a/website/content/middleware/jwt.md +++ b/website/content/middleware/jwt.md @@ -34,11 +34,32 @@ JWTConfig struct { // Skipper defines a function to skip middleware. Skipper Skipper + // BeforeFunc defines a function which is executed just before the middleware. + BeforeFunc BeforeFunc + + // SuccessHandler defines a function which is executed for a valid token. + SuccessHandler JWTSuccessHandler + + // ErrorHandler defines a function which is executed for an invalid token. + // It may be used to define a custom JWT error. + ErrorHandler JWTErrorHandler + + // ErrorHandlerWithContext is almost identical to ErrorHandler, but it's passed the current context. + ErrorHandlerWithContext JWTErrorHandlerWithContext + // Signing key to validate token. - // Required. + // 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. + // Required if neither user-defined KeyFunc nor SigningKeys is provided. SigningKey interface{} - // Signing method, used to check token signing method. + // Map of signing keys to validate token with kid field usage. + // 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. + // Required if neither user-defined KeyFunc nor SigningKey is provided. + SigningKeys map[string]interface{} + + // Signing method used to check the token's signing algorithm. // Optional. Default value HS256. SigningMethod string @@ -56,12 +77,25 @@ JWTConfig struct { // Possible values: // - "header:" // - "query:" + // - "param:" // - "cookie:" + // - "form:" TokenLookup string // AuthScheme to be used in the Authorization header. // Optional. Default value "Bearer". AuthScheme string + + // KeyFunc defines a user-defined function that supplies the public key for a token validation. + // The function shall take care of verifying the signing algorithm and selecting the proper key. + // A user-defined KeyFunc can be useful if tokens are issued by an external party. + // + // When a user-defined KeyFunc is provided, SigningKey, SigningKeys, and SigningMethod are ignored. + // 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. + // Required if neither SigningKeys nor SigningKey is provided. + // Default to an internal implementation verifying the signing algorithm and selecting the proper key. + KeyFunc jwt.Keyfunc } ```