Skip to content

Commit

Permalink
docs(jwt): add a user-defined KeyFunc
Browse files Browse the repository at this point in the history
Documentation for exposing KeyFunc:
labstack/echo#1756
  • Loading branch information
antonindrawan committed Mar 13, 2021
1 parent 3dafe29 commit 0d5448a
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 2 deletions.
79 changes: 79 additions & 0 deletions cookbook/jwt/user-defined-keyfunc/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
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) {

// For a demonstration purpose, Google Sign-in is used.
// https://developers.google.com/identity/sign-in/web/backend-auth
//
// This user-defined KeyFunc verifies tokens issued by Google Sign-In.
//
// Note: In this example, it downloads the keyset every time the restricted route is accessed.
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 accessible(c echo.Context) error {
return c.String(http.StatusOK, "Accessible")
}

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())

// Unauthenticated route
e.GET("/", accessible)

// 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"))
}
6 changes: 6 additions & 0 deletions website/content/cookbook/jwt.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ description = "JWT recipe for Echo"

{{< embed "jwt/custom-claims/server.go" >}}

## Server using a user-defined KeyFunc

`server.go`

{{< embed "jwt/user-defined-keyfunc/server.go" >}}

## Client

`curl`
Expand Down
38 changes: 36 additions & 2 deletions website/content/middleware/jwt.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -56,12 +77,25 @@ JWTConfig struct {
// Possible values:
// - "header:<name>"
// - "query:<name>"
// - "param:<name>"
// - "cookie:<name>"
// - "form:<name>"
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
}
```

Expand Down

0 comments on commit 0d5448a

Please sign in to comment.