Skip to content

Commit abf430b

Browse files
committed
JWKS support: Fetch, parse JWKS to Keys and encode Keys to JWKS. AWS Cognito JWKS with ease, one-liner: FetchAWSCognitoPublicKeys
1 parent 90bfed9 commit abf430b

24 files changed

+462
-271
lines changed

README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![build status](https://img.shields.io/github/actions/workflow/status/kataras/jwt/ci.yml?style=for-the-badge)](https://github.com/kataras/jwt/actions) [![gocov](https://img.shields.io/badge/Go%20Coverage-92%25-brightgreen.svg?style=for-the-badge)](https://travis-ci.org/github/kataras/jwt/jobs/740739405#L322) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=for-the-badge)](https://goreportcard.com/report/github.com/kataras/jwt) [![godocs](https://img.shields.io/badge/go-%20docs-488AC7.svg?style=for-the-badge)](https://pkg.go.dev/github.com/kataras/jwt)
44

5-
Fast and simple [JWT](https://jwt.io/#libraries-io) implementation written in [Go](https://go.dev/dl/). This package was designed with security, performance and simplicity in mind, it protects your tokens from [critical vulnerabilities that you may find in other libraries](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries).
5+
Fast and simple [JWT](https://jwt.io/#libraries-io) & JWKS implementation written in [Go](https://go.dev/dl/). This package was designed with security, performance and simplicity in mind, it protects your tokens from [critical vulnerabilities that you may find in other libraries](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries).
66

77
[![Benchmarks Total Repetitions - higher is better](http://iris-go.com/images/jwt/benchmarks.png)](_benchmarks)
88

@@ -133,7 +133,7 @@ Example Code to manually set all claims using a standard `map`:
133133

134134
```go
135135
now := time.Now()
136-
claims := map[string]interface{}{
136+
claims := map[string]any{
137137
"iat": now.Unix(),
138138
"exp": now.Add(15 * time.Minute).Unix(),
139139
"foo": "bar",
@@ -159,7 +159,7 @@ standardClaims := jwt.Claims{
159159
token, err := jwt.Sign(jwt.HS256, sharedKey, customClaims, standardClaims)
160160
```
161161

162-
> The `jwt.Map` is just a _type alias_, a _shortcut_, of `map[string]interface{}`.
162+
> The `jwt.Map` is just a _type alias_, a _shortcut_, of `map[string]any`.
163163
164164
At all cases, the `iat(IssuedAt)` and `exp(Expiry/MaxAge)` (and `nbf(NotBefore)`) values will be validated automatically on the [`Verify`](#verify-a-token) method.
165165

@@ -260,7 +260,7 @@ type VerifiedToken struct {
260260

261261
### Decode custom Claims
262262

263-
To extract any custom claims, given on the `Sign` method, we use the result of the `Verify` method, which is a `VerifiedToken` pointer. This VerifiedToken has a single method, the `Claims(dest interface{}) error` one, which can be used to decode the claims (payload part) to a value of our choice. Again, that value can be a `map` or any `struct`.
263+
To extract any custom claims, given on the `Sign` method, we use the result of the `Verify` method, which is a `VerifiedToken` pointer. This VerifiedToken has a single method, the `Claims(dest any) error` one, which can be used to decode the claims (payload part) to a value of our choice. Again, that value can be a `map` or any `struct`.
264264

265265
```go
266266
var claims = struct {

_benchmarks/sign_benchmark_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func BenchmarkSign_Map(b *testing.B) {
2323
for i := 0; i < b.N; i++ {
2424
// supports custom types and expiration helper.
2525
now := time.Now()
26-
claims := map[string]interface{}{
26+
claims := map[string]any{
2727
"foo": "bar",
2828
"exp": now.Add(15 * time.Minute).Unix(),
2929
"iat": now.Unix(),
@@ -159,7 +159,7 @@ func BenchmarkSign_go_jose_Map(b *testing.B) {
159159
// unlike kataras/jwt which u can use already defined structs.
160160
// We will benchmark it with structs (see below test).
161161
now := time.Now()
162-
claims := map[string]interface{}{
162+
claims := map[string]any{
163163
"foo": "bar",
164164
"exp": now.Add(15 * time.Minute).Unix(),
165165
"iat": now.Unix(),

_benchmarks/verify_benchmark_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func BenchmarkVerify_jwt_go(b *testing.B) {
4545
b.ResetTimer()
4646

4747
for i := 0; i < b.N; i++ {
48-
_, err := jwtgo.Parse(tokenString, func(token *jwtgo.Token) (interface{}, error) {
48+
_, err := jwtgo.Parse(tokenString, func(token *jwtgo.Token) (any, error) {
4949
return testSecret, nil
5050
})
5151
if err != nil {

_examples/aws-cognito-verify/main.go

+1-13
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,7 @@ import (
1111
// |=================================================================================|
1212

1313
func main() {
14-
/*
15-
cognitoConfig := jwt.AWSKeysConfiguration{
16-
Region: "us-west-2",
17-
UserPoolID: "us-west-2_xxx",
18-
}
19-
20-
keys, err := cognitoConfig.Load()
21-
if err != nil {
22-
panic(err)
23-
}
24-
OR:
25-
*/
26-
keys, err := jwt.LoadAWSCognitoKeys("us-west-2" /* region */, "us-west-2_xxx" /* user pool id */)
14+
keys, err := jwt.FetchAWSCognitoPublicKeys("us-west-2" /* region */, "us-west-2_xxx" /* user pool id */)
2715
if err != nil {
2816
panic(err) // handle error, e.g. pool does not exist in the region.
2917
}

_examples/basic/main.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ var sharedKey = []byte("sercrethatmaycontainch@r$32chars") // OR jwt.MustGenerat
5050
// generate token to use.
5151
func getTokenHandler(w http.ResponseWriter, r *http.Request) {
5252
// now := time.Now()
53-
// token, err := jwt.Sign(jwt.HS256, sharedKey, map[string]interface{}{
53+
// token, err := jwt.Sign(jwt.HS256, sharedKey, map[string]any{
5454
// "iat": now.Unix(),
5555
// "exp": now.Add(15 * time.Minute).Unix(),
5656
// "foo": "bar",
@@ -94,7 +94,7 @@ func verifyTokenHandler(w http.ResponseWriter, r *http.Request) {
9494
}
9595

9696
// Parse custom claims...
97-
var claims map[string]interface{}
97+
var claims map[string]any
9898
// ^ can be any type, e.g.
9999
// var claims = struct {
100100
// Foo string `json:"foo"`

_examples/basic/rs512-verify/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func verifyTokenHandler(w http.ResponseWriter, r *http.Request) {
5454
}
5555

5656
// Parse custom claims...
57-
var claims map[string]interface{}
57+
var claims map[string]any
5858
// ^ can be any type, e.g.
5959
// var claims = struct {
6060
// Foo string `json:"foo"`

_examples/blocklist/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ Navigate to http://localhost:8080/protected?token=%s and you should see an ErrBl
8585
}
8686

8787
// Parse custom claims...
88-
var claims map[string]interface{}
88+
var claims map[string]any
8989
// ^ can be any type, e.g.
9090
// var claims = struct {
9191
// Foo string `json:"foo"`

_examples/generate-ed25519/main.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package main
22

33
import (
44
"crypto/ed25519"
5-
"io/ioutil"
65
"log"
6+
"os"
77

88
"github.com/kataras/jwt"
99
)
@@ -14,12 +14,12 @@ func main() {
1414
log.Fatal(err)
1515
}
1616

17-
err = ioutil.WriteFile("ed25519_private.pem", priv, 0600)
17+
err = os.WriteFile("ed25519_private.pem", priv, 0600)
1818
if err != nil {
1919
log.Fatalf("ed25519: private: write file: %w", err)
2020
}
2121

22-
err = ioutil.WriteFile("ed25519_public.pem", pub, 0600)
22+
err = os.WriteFile("ed25519_public.pem", pub, 0600)
2323
if err != nil {
2424
log.Fatalf("ed25519: public: write file: %w", err)
2525
}

_examples/middleware/main.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func main() {
2424
func getTokenHandler(w http.ResponseWriter, r *http.Request) {
2525
now := time.Now()
2626

27-
token, err := jwt.Sign(jwt.HS256, sharedKey, map[string]interface{}{
27+
token, err := jwt.Sign(jwt.HS256, sharedKey, map[string]any{
2828
"iat": now.Unix(),
2929
"exp": now.Add(15 * time.Minute).Unix(),
3030
"foo": "bar",
@@ -47,7 +47,7 @@ func getTokenHandler(w http.ResponseWriter, r *http.Request) {
4747
func protectedHandler(w http.ResponseWriter, r *http.Request) {
4848
verifiedToken := r.Context().Value(tokenContextKey).(*jwt.VerifiedToken)
4949

50-
var claims map[string]interface{}
50+
var claims map[string]any
5151
// ^ can be any type, e.g.
5252
// var claims = struct {
5353
// Foo string `json:"foo"`
@@ -108,14 +108,14 @@ func verify2(next http.HandlerFunc) http.HandlerFunc {
108108
/*
109109
Another idea, when you want a single middleware to support different
110110
Go structs (benefit: type safety when access the claims fields):
111-
verify2(getClaimsPtr func() interface{}, next http.HandlerFunc) {
111+
verify2(getClaimsPtr func() any, next http.HandlerFunc) {
112112
// [...]
113113
claimsPtr := getClaimsPtr()
114114
verifiedToken.Claims(claimsPtr)
115115
// [...]
116116
}
117117
Another idea's usage:
118-
verify2(func() interface{} {
118+
verify2(func() any {
119119
return &userClaims{}
120120
}, routeHandler)
121121
Inside the handler:

claims.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ type claimsSecondChance struct {
6868
Expiry json.Number `json:"exp,omitempty"`
6969
ID string `json:"jti,omitempty"`
7070
OriginID string `json:"origin_jti,omitempty"`
71-
Issuer interface{} `json:"iss,omitempty"`
72-
Subject interface{} `json:"sub,omitempty"`
71+
Issuer any `json:"iss,omitempty"`
72+
Subject any `json:"sub,omitempty"`
7373
Audience Audience `json:"aud,omitempty"`
7474
}
7575

@@ -90,7 +90,7 @@ func (c claimsSecondChance) toClaims() Claims {
9090
}
9191
}
9292

93-
func getStr(v interface{}) string {
93+
func getStr(v any) string {
9494
if v == nil {
9595
return ""
9696
}
@@ -246,7 +246,7 @@ func MaxAge(maxAge time.Duration) SignOptionFunc {
246246

247247
// MaxAgeMap is a helper to set "exp" and "iat" claims to a map claims.
248248
// Usage:
249-
// claims := map[string]interface{}{"foo": "bar"}
249+
// claims := map[string]any{"foo": "bar"}
250250
// MaxAgeMap(15 * time.Minute, claims)
251251
// Sign(alg, key, claims)
252252
func MaxAgeMap(maxAge time.Duration, claims Map) {
@@ -270,7 +270,7 @@ func MaxAgeMap(maxAge time.Duration, claims Map) {
270270
//
271271
// Usage:
272272
//
273-
// claims := Merge(map[string]interface{}{"foo":"bar"}, Claims{
273+
// claims := Merge(map[string]any{"foo":"bar"}, Claims{
274274
// MaxAge: 15 * time.Minute,
275275
// Issuer: "an-issuer",
276276
// })
@@ -280,7 +280,7 @@ func MaxAgeMap(maxAge time.Duration, claims Map) {
280280
//
281281
// Sign(alg, key, claims, MaxAge(time.Duration))
282282
// Sign(alg, key, claims, Claims{...})
283-
func Merge(claims interface{}, other interface{}) []byte {
283+
func Merge(claims any, other any) []byte {
284284
claimsB, err := Marshal(claims)
285285
if err != nil {
286286
return nil

claims_merge_benchmark_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func BenchmarkStructToMapReflection(b *testing.B) {
117117
}
118118
}
119119

120-
func structToMapJSON(i interface{}) (Map, error) {
120+
func structToMapJSON(i any) (Map, error) {
121121
if m, ok := i.(Map); ok {
122122
return m, nil
123123
}
@@ -136,7 +136,7 @@ func structToMapJSON(i interface{}) (Map, error) {
136136
return m, nil
137137
}
138138

139-
func structToMapReflection(i interface{}) (Map, error) {
139+
func structToMapReflection(i any) (Map, error) {
140140
if m, ok := i.(Map); ok {
141141
return m, nil
142142
}
@@ -147,7 +147,7 @@ func structToMapReflection(i interface{}) (Map, error) {
147147
}
148148

149149
n := v.NumField()
150-
m := make(map[string]interface{}, n)
150+
m := make(map[string]any, n)
151151

152152
typ := v.Type()
153153
for i := 0; i < n; i++ {

doc.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ Getting Started:
3131
3232
func main() {
3333
// Generate a token:
34-
myClaims := map[string]interface{}{
34+
myClaims := map[string]any{
3535
"foo": "bar",
3636
}
3737
token, err := jwt.Sign(jwt.HS256, sharedKey, myClaims, jwt.MaxAge(15 * time.Minute))
3838
3939
// Verify and extract claims from a token:
4040
verifiedToken, err := jwt.Verify(jwt.HS256, sharedKey, token)
4141
42-
var claims map[string]interface{}
42+
var claims map[string]any
4343
err = verifiedToken.Claims(&claims)
4444
}
4545
*/

hmac.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func (a *algHMAC) Verify(key PublicKey, headerAndPayload []byte, signature []byt
5757

5858
// Key Helper.
5959

60-
var panicHandler = func(v interface{}) {
60+
var panicHandler = func(v any) {
6161
panic(v)
6262
}
6363

hmac_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ func catchPanic(t *testing.T, shouldPanic bool, fn func()) {
2323
t.Helper()
2424

2525
got := false
26-
var val interface{}
26+
var val any
2727
prevHandler := panicHandler
28-
panicHandler = func(v interface{}) {
28+
panicHandler = func(v any) {
2929
got = true
3030
val = v
3131
}

0 commit comments

Comments
 (0)