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