Skip to content

Commit 9f595c0

Browse files
antonindrawanaldas
authored andcommitted
docs(jwt): add a user-defined KeyFunc
Documentation for exposing KeyFunc: labstack/echo#1756
1 parent 5ee8c42 commit 9f595c0

File tree

3 files changed

+121
-2
lines changed

3 files changed

+121
-2
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"net/http"
8+
9+
jwt "github.com/dgrijalva/jwt-go"
10+
echo "github.com/labstack/echo/v4"
11+
middleware "github.com/labstack/echo/v4/middleware"
12+
jwk "github.com/lestrrat-go/jwx/jwk"
13+
)
14+
15+
func getKey(token *jwt.Token) (interface{}, error) {
16+
17+
// For a demonstration purpose, Google Sign-in is used.
18+
// https://developers.google.com/identity/sign-in/web/backend-auth
19+
//
20+
// This user-defined KeyFunc verifies tokens issued by Google Sign-In.
21+
//
22+
// Note: In this example, it downloads the keyset every time the restricted route is accessed.
23+
keySet, err := jwk.Fetch(context.Background(), "https://www.googleapis.com/oauth2/v3/certs")
24+
if err != nil {
25+
return nil, err
26+
}
27+
28+
keyID, ok := token.Header["kid"].(string)
29+
if !ok {
30+
return nil, errors.New("expecting JWT header to have a key ID in the kid field")
31+
}
32+
33+
key, found := keySet.LookupKeyID(keyID)
34+
35+
if !found {
36+
return nil, fmt.Errorf("unable to find key %q", keyID)
37+
}
38+
39+
var pubkey interface{}
40+
if err := key.Raw(&pubkey); err != nil {
41+
return nil, fmt.Errorf("Unable to get the public key. Error: %s", err.Error())
42+
}
43+
44+
return pubkey, nil
45+
}
46+
47+
func accessible(c echo.Context) error {
48+
return c.String(http.StatusOK, "Accessible")
49+
}
50+
51+
func restricted(c echo.Context) error {
52+
user := c.Get("user").(*jwt.Token)
53+
claims := user.Claims.(jwt.MapClaims)
54+
name := claims["name"].(string)
55+
return c.String(http.StatusOK, "Welcome "+name+"!")
56+
}
57+
58+
func main() {
59+
e := echo.New()
60+
61+
// Middleware
62+
e.Use(middleware.Logger())
63+
e.Use(middleware.Recover())
64+
65+
// Unauthenticated route
66+
e.GET("/", accessible)
67+
68+
// Restricted group
69+
r := e.Group("/restricted")
70+
{
71+
config := middleware.JWTConfig{
72+
KeyFunc: getKey,
73+
}
74+
r.Use(middleware.JWTWithConfig(config))
75+
r.GET("", restricted)
76+
}
77+
78+
e.Logger.Fatal(e.Start(":1323"))
79+
}

website/content/cookbook/jwt.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ description = "JWT recipe for Echo"
2222

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

25+
## Server using a user-defined KeyFunc
26+
27+
`server.go`
28+
29+
{{< embed "jwt/user-defined-keyfunc/server.go" >}}
30+
2531
## Client
2632

2733
`curl`

website/content/middleware/jwt.md

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,32 @@ JWTConfig struct {
3434
// Skipper defines a function to skip middleware.
3535
Skipper Skipper
3636

37+
// BeforeFunc defines a function which is executed just before the middleware.
38+
BeforeFunc BeforeFunc
39+
40+
// SuccessHandler defines a function which is executed for a valid token.
41+
SuccessHandler JWTSuccessHandler
42+
43+
// ErrorHandler defines a function which is executed for an invalid token.
44+
// It may be used to define a custom JWT error.
45+
ErrorHandler JWTErrorHandler
46+
47+
// ErrorHandlerWithContext is almost identical to ErrorHandler, but it's passed the current context.
48+
ErrorHandlerWithContext JWTErrorHandlerWithContext
49+
3750
// Signing key to validate token.
38-
// Required.
51+
// This is one of the three options to provide a token validation key.
52+
// The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey.
53+
// Required if neither user-defined KeyFunc nor SigningKeys is provided.
3954
SigningKey interface{}
4055

41-
// Signing method, used to check token signing method.
56+
// Map of signing keys to validate token with kid field usage.
57+
// This is one of the three options to provide a token validation key.
58+
// The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey.
59+
// Required if neither user-defined KeyFunc nor SigningKey is provided.
60+
SigningKeys map[string]interface{}
61+
62+
// Signing method used to check the token's signing algorithm.
4263
// Optional. Default value HS256.
4364
SigningMethod string
4465

@@ -56,12 +77,25 @@ JWTConfig struct {
5677
// Possible values:
5778
// - "header:<name>"
5879
// - "query:<name>"
80+
// - "param:<name>"
5981
// - "cookie:<name>"
82+
// - "form:<name>"
6083
TokenLookup string
6184

6285
// AuthScheme to be used in the Authorization header.
6386
// Optional. Default value "Bearer".
6487
AuthScheme string
88+
89+
// KeyFunc defines a user-defined function that supplies the public key for a token validation.
90+
// The function shall take care of verifying the signing algorithm and selecting the proper key.
91+
// A user-defined KeyFunc can be useful if tokens are issued by an external party.
92+
//
93+
// When a user-defined KeyFunc is provided, SigningKey, SigningKeys, and SigningMethod are ignored.
94+
// This is one of the three options to provide a token validation key.
95+
// The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey.
96+
// Required if neither SigningKeys nor SigningKey is provided.
97+
// Default to an internal implementation verifying the signing algorithm and selecting the proper key.
98+
KeyFunc jwt.Keyfunc
6599
}
66100
```
67101

0 commit comments

Comments
 (0)