github.com/lestrrat-go/jwx/v2@v2.0.21/jwk/README.md (about) 1 # JWK [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/jwx/v2/jwk.svg)](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk) 2 3 Package jwk implements JWK as described in [RFC7517](https://tools.ietf.org/html/rfc7517). 4 If you are looking to use JWT wit JWKs, look no further than [github.com/lestrrat-go/jwx](../jwt). 5 6 * Parse and work with RSA/EC/Symmetric/OKP JWK types 7 * Convert to and from JSON 8 * Convert to and from raw key types (e.g. *rsa.PrivateKey) 9 * Ability to keep a JWKS fresh using *jwk.AutoRefersh 10 11 ## Supported key types: 12 13 | kty | Curve | Go Key Type | 14 |:----|:------------------------|:----------------------------------------------| 15 | RSA | N/A | rsa.PrivateKey / rsa.PublicKey (2) | 16 | EC | P-256<br>P-384<br>P-521<br>secp256k1 (1) | ecdsa.PrivateKey / ecdsa.PublicKey (2) | 17 | oct | N/A | []byte | 18 | OKP | Ed25519 (1) | ed25519.PrivateKey / ed25519.PublicKey (2) | 19 | | X25519 (1) | (jwx/)x25519.PrivateKey / x25519.PublicKey (2)| 20 21 * Note 1: Experimental 22 * Note 2: Either value or pointers accepted (e.g. rsa.PrivateKey or *rsa.PrivateKey) 23 24 # Documentation 25 26 Please read the [API reference](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk), or 27 the how-to style documentation on how to use JWK can be found in the [docs directory](../docs/04-jwk.md). 28 29 # Auto-Refresh a key during a long running process 30 31 <!-- INCLUDE(examples/jwk_cache_example_test.go) --> 32 ```go 33 package examples_test 34 35 import ( 36 "context" 37 "fmt" 38 "time" 39 40 "github.com/lestrrat-go/jwx/v2/jwk" 41 ) 42 43 func ExampleJWK_Cache() { 44 ctx, cancel := context.WithCancel(context.Background()) 45 46 const googleCerts = `https://www.googleapis.com/oauth2/v3/certs` 47 48 // First, set up the `jwk.Cache` object. You need to pass it a 49 // `context.Context` object to control the lifecycle of the background fetching goroutine. 50 // 51 // Note that by default refreshes only happen very 15 minutes at the 52 // earliest. If you need to control this, use `jwk.WithRefreshWindow()` 53 c := jwk.NewCache(ctx) 54 55 // Tell *jwk.Cache that we only want to refresh this JWKS 56 // when it needs to (based on Cache-Control or Expires header from 57 // the HTTP response). If the calculated minimum refresh interval is less 58 // than 15 minutes, don't go refreshing any earlier than 15 minutes. 59 c.Register(googleCerts, jwk.WithMinRefreshInterval(15*time.Minute)) 60 61 // Refresh the JWKS once before getting into the main loop. 62 // This allows you to check if the JWKS is available before we start 63 // a long-running program 64 _, err := c.Refresh(ctx, googleCerts) 65 if err != nil { 66 fmt.Printf("failed to refresh google JWKS: %s\n", err) 67 return 68 } 69 70 // Pretend that this is your program's main loop 71 MAIN: 72 for { 73 select { 74 case <-ctx.Done(): 75 break MAIN 76 default: 77 } 78 keyset, err := c.Get(ctx, googleCerts) 79 if err != nil { 80 fmt.Printf("failed to fetch google JWKS: %s\n", err) 81 return 82 } 83 _ = keyset 84 // The returned `keyset` will always be "reasonably" new. 85 // 86 // By "reasonably" we mean that we cannot guarantee that the keys will be refreshed 87 // immediately after it has been rotated in the remote source. But it should be close\ 88 // enough, and should you need to forcefully refresh the token using the `(jwk.Cache).Refresh()` method. 89 // 90 // If re-fetching the keyset fails, a cached version will be returned from the previous successful 91 // fetch upon calling `(jwk.Cache).Fetch()`. 92 93 // Do interesting stuff with the keyset... but here, we just 94 // sleep for a bit 95 time.Sleep(time.Second) 96 97 // Because we're a dummy program, we just cancel the loop now. 98 // If this were a real program, you prosumably loop forever 99 cancel() 100 } 101 // OUTPUT: 102 } 103 ``` 104 source: [examples/jwk_cache_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_cache_example_test.go) 105 <!-- END INCLUDE --> 106 107 Parse and use a JWK key: 108 109 <!-- INCLUDE(examples/jwk_example_test.go) --> 110 ```go 111 package examples_test 112 113 import ( 114 "context" 115 "fmt" 116 "log" 117 118 "github.com/lestrrat-go/jwx/v2/internal/json" 119 "github.com/lestrrat-go/jwx/v2/jwk" 120 ) 121 122 func ExampleJWK_Usage() { 123 // Use jwk.Cache if you intend to keep reuse the JWKS over and over 124 set, err := jwk.Fetch(context.Background(), "https://www.googleapis.com/oauth2/v3/certs") 125 if err != nil { 126 log.Printf("failed to parse JWK: %s", err) 127 return 128 } 129 130 // Key sets can be serialized back to JSON 131 { 132 jsonbuf, err := json.Marshal(set) 133 if err != nil { 134 log.Printf("failed to marshal key set into JSON: %s", err) 135 return 136 } 137 log.Printf("%s", jsonbuf) 138 } 139 140 for it := set.Keys(context.Background()); it.Next(context.Background()); { 141 pair := it.Pair() 142 key := pair.Value.(jwk.Key) 143 144 var rawkey interface{} // This is the raw key, like *rsa.PrivateKey or *ecdsa.PrivateKey 145 if err := key.Raw(&rawkey); err != nil { 146 log.Printf("failed to create public key: %s", err) 147 return 148 } 149 // Use rawkey for jws.Verify() or whatever. 150 _ = rawkey 151 152 // You can create jwk.Key from a raw key, too 153 fromRawKey, err := jwk.FromRaw(rawkey) 154 if err != nil { 155 log.Printf("failed to acquire raw key from jwk.Key: %s", err) 156 return 157 } 158 159 // Keys can be serialized back to JSON 160 jsonbuf, err := json.Marshal(key) 161 if err != nil { 162 log.Printf("failed to marshal key into JSON: %s", err) 163 return 164 } 165 166 fromJSONKey, err := jwk.Parse(jsonbuf) 167 if err != nil { 168 log.Printf("failed to parse json: %s", err) 169 return 170 } 171 _ = fromJSONKey 172 _ = fromRawKey 173 } 174 // OUTPUT: 175 } 176 177 //nolint:govet 178 func ExampleJWK_MarshalJSON() { 179 // JWKs that inherently involve randomness such as RSA and EC keys are 180 // not used in this example, because they may produce different results 181 // depending on the environment. 182 // 183 // (In fact, even if you use a static source of randomness, tests may fail 184 // because of internal changes in the Go runtime). 185 186 raw := []byte("01234567890123456789012345678901234567890123456789ABCDEF") 187 188 // This would create a symmetric key 189 key, err := jwk.FromRaw(raw) 190 if err != nil { 191 fmt.Printf("failed to create symmetric key: %s\n", err) 192 return 193 } 194 if _, ok := key.(jwk.SymmetricKey); !ok { 195 fmt.Printf("expected jwk.SymmetricKey, got %T\n", key) 196 return 197 } 198 199 key.Set(jwk.KeyIDKey, "mykey") 200 201 buf, err := json.MarshalIndent(key, "", " ") 202 if err != nil { 203 fmt.Printf("failed to marshal key into JSON: %s\n", err) 204 return 205 } 206 fmt.Printf("%s\n", buf) 207 208 // OUTPUT: 209 // { 210 // "k": "MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODlBQkNERUY", 211 // "kid": "mykey", 212 // "kty": "oct" 213 // } 214 } 215 ``` 216 source: [examples/jwk_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_example_test.go) 217 <!-- END INCLUDE -->