Skip to content

Commit

Permalink
Merge develop/v2 for v2.0.0-beta2 (#697)
Browse files Browse the repository at this point in the history
* disable verification/validation for benchmarking

* Introduce jwk.CachedSet to make the common operation of using cached JWKS easier (#689)

* First round implementing jwk.CachedSet

* Change jwk.Set interface

* appease linter

* add docs

* Fix example code

* fix jwk.Set usage

* Add codecov.yml

* Add more tests

* appease linter

* tweak docs, examples, go versions

* Update deps for go1.18

* run autodoc when mering to develop/v2 (#691)

* Fix autodoc (#692)

* run autodoc when mering to develop/v2

* Create a PR isntead of a push

* Pass token, fix branch name (#693)

* Add a special rule for develop/v* branch (#695)

* autodoc updates (#696)

Co-authored-by: lestrrat <[email protected]>

* Update Changes

* Update Changes

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: lestrrat <[email protected]>
  • Loading branch information
3 people authored Apr 16, 2022
1 parent 0258045 commit fd8fafc
Show file tree
Hide file tree
Showing 44 changed files with 536 additions and 188 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/autodoc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Auto-Doc
on:
pull_request:
branches:
- v2
- develop/v2
types:
- closed

Expand All @@ -17,4 +17,6 @@ jobs:
- name: Process markdown files
run: |
find . -name '*.md' | xargs perl tools/autodoc.pl
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
go_tags: [ 'stdlib', 'goccy', 'es256k', 'all']
go: [ '1.17.x', '1.16.x' ]
go: [ '1.18.x', '1.17.x' ]
name: "Test [ Go ${{ matrix.go }} / Tags ${{ matrix.go_tags }} ]"
steps:
- name: Checkout repository
Expand Down
17 changes: 16 additions & 1 deletion Changes
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,22 @@ Changes
=======

v2 has many incompatibilities with v1. To see the full list of differences between
v1 and v2, please read the Changes.v2 file (https://github.com/lestrrat-go/jwx/blob/develop/v2/Changes-v2.md)
v1 and v2, please read the Changes-v2.md file (https://github.com/lestrrat-go/jwx/blob/develop/v2/Changes-v2.md)

v2.0.0-beta2 - 16 Apr 2022
[jwk]
* Updated `jwk.Set` API and reflected pending changes from v1 which were
left over. Please see Changes-v2.md file for details.

* Added `jwk.CachedSet`, a shim over `jwk.Cache` that allows you to
have to write wrappers around `jwk.Cache` that retrieves a particular
`jwk.Set` out of it. You can use it to, for example, pass `jwk.CachedSet`
to a `jws.Verify`

cache := jwk.NewCache(ctx)
cache.Register(ctx, jwksURL)
cachedSet := jwk.NewCachedSet(cache, jwksURL)
jws.Verify(signed, jws.WithKeySet(cachedSet))

v2.0.0-beta1 - 09 Apr 2022
[Miscellaneous]
Expand Down
22 changes: 22 additions & 0 deletions Changes-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,28 @@ jwe.Verify(signed, jwe.WithKeySet(jwks), jwe.WithKeyUsed(&keyUsed))
* `jwk.New()` has been renamed to `jwk.FromRaw()`, which hopefully will
make it easier for the users what the input should be.

* `jwk.Set` has many interface changes:
* Changed methods to match jwk.Key and its semantics:
* Field is now Get() (returns values for arbitrary fields other than keys). Fetching a key is done via Key()
* Remove() now removes arbitrary fields, not keys. to remove keys, use RemoveKey()
* Iterate has been added to iterate through all non-key fields.
* Add is now AddKey(Key) string
* Get is now Key(int) (Key, bool)
* Remove is now RemoveKey(Key) error
* Iterate is now Keys(context.Context) KeyIterator
* Clear is now Clear() error

* `jwk.CachedSet` has been added. You can create a `jwk.Set` that is backed by
`jwk.Cache` so you can do this:

```go
cache := jkw.NewCache(ctx)
cachedSet := jwk.NewCachedSet(cache, jwksURI)

// cachedSet is always the refreshed, cached version from jwk.Cache
jws.Verify(signed, jws.WithKeySet(cachedSet))
```

* `jwk.NewRSAPRivateKey()`, `jwk.NewECDSAPrivateKey()`, etc have been removed.
There is no longer any way to create concrete types of `jwk.Key`

Expand Down
6 changes: 0 additions & 6 deletions bench/comparison/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,3 @@ module github.com/lestrrat-go/jwx/v2/bench/comparison
go 1.15

replace github.com/lestrrat-go/jwx/v2 => ../..

require (
github.com/golang-jwt/jwt/v4 v4.4.0
github.com/lestrrat-go/jwx/v2 v2.0.0-00010101000000-000000000000
github.com/stretchr/testify v1.7.1
)
38 changes: 0 additions & 38 deletions bench/comparison/go.sum
Original file line number Diff line number Diff line change
@@ -1,38 +0,0 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/goccy/go-json v0.9.6 h1:5/4CtRQdtsX0sal8fdVhTaiMN01Ri8BExZZ8iRmHQ6E=
github.com/goccy/go-json v0.9.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt/v4 v4.4.0 h1:EmVIxB5jzbllGIjiCV5JG4VylbK3KE400tLGLI1cdfU=
github.com/golang-jwt/jwt/v4 v4.4.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/httprc v1.0.1 h1:Cnc4NxIySph38pQPzKbjg5OkKsGR/Cf5xcWt5OlSUDI=
github.com/lestrrat-go/httprc v1.0.1/go.mod h1:5Ml+nB++j6IC0e6LzefJnrpMQDKgDwDCaIQQzhbqhJM=
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
6 changes: 3 additions & 3 deletions bench/performance/jwt_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,14 @@ func BenchmarkJWT(b *testing.B) {
Name: "jwt.ParseString",
SkipShort: true,
Test: func(b *testing.B) error {
_, err := jwt.ParseString(jsonString)
_, err := jwt.ParseString(jsonString, jwt.WithVerify(false), jwt.WithValidate(false))
return err
},
},
{
Name: "jwt.Parse",
Test: func(b *testing.B) error {
_, err := jwt.Parse(jsonBuf)
_, err := jwt.Parse(jsonBuf, jwt.WithVerify(false), jwt.WithValidate(false))
return err
},
},
Expand All @@ -165,7 +165,7 @@ func BenchmarkJWT(b *testing.B) {
return err
},
Test: func(b *testing.B) error {
_, err := jwt.ParseReader(jsonReader)
_, err := jwt.ParseReader(jsonReader, jwt.WithVerify(false), jwt.WithValidate(false))
return err
},
},
Expand Down
4 changes: 2 additions & 2 deletions cmd/jwx/jwe.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func makeJweEncryptCmd() *cli.Command {
if keyset.Len() != 1 {
return fmt.Errorf(`jwk file must contain exactly one key`)
}
key, _ := keyset.Get(0)
key, _ := keyset.Key(0)

pubkey, err := jwk.PublicKeyOf(key)
if err != nil {
Expand Down Expand Up @@ -151,7 +151,7 @@ func makeJweDecryptCmd() *cli.Command {
if keyset.Len() != 1 {
return fmt.Errorf(`jwk file must contain exactly one key`)
}
key, _ := keyset.Get(0)
key, _ := keyset.Key(0)

var decrypted []byte

Expand Down
4 changes: 2 additions & 2 deletions cmd/jwx/jwk.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func dumpJWKSet(dst io.Writer, keyset jwk.Set, format string, preserve bool) err
return fmt.Errorf(`failed to marshal keyset into JSON format: %w`, err)
}
} else {
key, _ := keyset.Get(0)
key, _ := keyset.Key(0)
if err := dumpJSON(dst, key); err != nil {
return fmt.Errorf(`failed to marshal key into JSON format: %w`, err)
}
Expand Down Expand Up @@ -203,7 +203,7 @@ func makeJwkGenerateCmd() *cli.Command {
}

keyset := jwk.NewSet()
keyset.Add(key)
keyset.AddKey(key)

if c.Bool("public-key") {
pubks, err := jwk.PublicSetOf(keyset)
Expand Down
4 changes: 2 additions & 2 deletions cmd/jwx/jws.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ func makeJwsVerifyCmd() *cli.Command {
}

ctx := context.Background()
for iter := keyset.Iterate(ctx); iter.Next(ctx); {
for iter := keyset.Keys(ctx); iter.Next(ctx); {
pair := iter.Pair()
key := pair.Value.(jwk.Key)
payload, err := jws.Verify(buf, jws.WithKey(alg, key))
Expand Down Expand Up @@ -232,7 +232,7 @@ func makeJwsSignCmd() *cli.Command {
if keyset.Len() != 1 {
return fmt.Errorf(`jwk file must contain exactly one key`)
}
key, _ := keyset.Get(0)
key, _ := keyset.Key(0)

src, err := getSource(c.Args().Get(0))
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
codecov:
allow_coverage_offsets: true
6 changes: 3 additions & 3 deletions docs/01-jwt.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,8 @@ func ExampleJWT_ParseWithKeySet() {
// all of the public keys
{
privset := jwk.NewSet()
privset.Add(realKey)
privset.Add(bogusKey)
privset.AddKey(realKey)
privset.AddKey(bogusKey)
v, err := jwk.PublicSetOf(privset)
if err != nil {
fmt.Printf("failed to create public JWKS: %s\n", err)
Expand Down Expand Up @@ -610,7 +610,7 @@ func ExampleJWT_ParseWithJKU() {
fmt.Printf("failed to create public key: %s\n", err)
return
}
set.Add(pubkey)
set.AddKey(pubkey)
}

srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand Down
8 changes: 4 additions & 4 deletions docs/02-jws.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,14 +370,14 @@ func ExampleJWS_VerifyWithJWKSet() {
set := jwk.NewSet()
// Add some bogus keys
k1, _ := jwk.FromRaw([]byte("abracadavra"))
set.Add(k1)
set.AddKey(k1)
k2, _ := jwk.FromRaw([]byte("opensasame"))
set.Add(k2)
// Add the real thing
set.AddKey(k2)
// AddKey the real thing
pubkey, _ := jwk.PublicRawKeyOf(privkey)
k3, _ := jwk.FromRaw(pubkey)
k3.Set(jwk.AlgorithmKey, jwa.RS256)
set.Add(k3)
set.AddKey(k3)

// Up to this point, you probably will replace with a simple jwk.Fetch()

Expand Down
6 changes: 3 additions & 3 deletions docs/03-jwe.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,13 +416,13 @@ func ExampleJWE_VerifyWithJWKSet() {
set := jwk.NewSet()
// Add some bogus keys
k1, _ := jwk.FromRaw([]byte("abracadavra"))
set.Add(k1)
set.AddKey(k1)
k2, _ := jwk.FromRaw([]byte("opensasame"))
set.Add(k2)
set.AddKey(k2)
// Add the real thing
k3, _ := jwk.FromRaw(privkey)
k3.Set(jwk.AlgorithmKey, jwa.RSA_OAEP)
set.Add(k3)
set.AddKey(k3)

// Up to this point, you probably will replace with a simple jwk.Fetch()

Expand Down
50 changes: 48 additions & 2 deletions docs/04-jwk.md
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ source: [examples/jwk_from_raw_example_test.go](https://github.com/lestrrat-go/j

To parse keys stored in a remote location pointed by a HTTP(s) URL, use [`jwk.Fetch()`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Fetch)

If you are going to be using this key repeatedly in a long running process, consider using [`jwk.Cache`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Cache) described elsewhere in this document.
If you are going to be using this key repeatedly in a long running process, consider using [`jwk.Cache`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Cache) or [`jwk.CachedSet`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#CachedSet) described elsewhere in this document.

<!-- INCLUDE(examples/jwk_fetch_example_test.go) -->
```go
Expand Down Expand Up @@ -590,7 +590,7 @@ Normally, you should be able to simply fetch the JWK using [`jwk.Fetch()`](https
but keys are usually routinely expired and rotated due to security reasons.
In such cases you would need to refetch the JWK periodically, which is a pain.

`github.com/lestrrat-go/jwx/v2/jwk` provides the [`jwk.Cache`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Cache) tool to do this for you.
`github.com/lestrrat-go/jwx/v2/jwk` provides the [`jwk.Cache`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#Cache) and [`jwk.CachedSet`](https://pkg.go.dev/github.com/lestrrat-go/jwx/v2/jwk#CachedSet) to do this for you.

<!-- INCLUDE(examples/jwk_cache_example_test.go) -->
```go
Expand Down Expand Up @@ -669,6 +669,52 @@ MAIN:
source: [examples/jwk_cache_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_cache_example_test.go)
<!-- END INCLUDE -->

<!-- INCLUDE(examples/jwk_cached_set_example_test.go) -->
```go
package examples_test

import (
"context"
"fmt"
"time"

"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jws"
)

func ExampleJWK_CachedSet() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

const googleCerts = `https://www.googleapis.com/oauth2/v3/certs`

// The first steps are the same as examples/jwk_cache_example_test.go
c := jwk.NewCache(ctx)
c.Register(googleCerts, jwk.WithMinRefreshInterval(15*time.Minute))
_, err := c.Refresh(ctx, googleCerts)
if err != nil {
fmt.Printf("failed to refresh google JWKS: %s\n", err)
return
}

cached := jwk.NewCachedSet(c, googleCerts)

// cached fulfills the jwk.Set interface.
var _ jwk.Set = cached

// That means you can pass it to things like jws.WithKeySet,
// allowing you to pretend as if you are using the result of
//
// jwk.Fetch(ctx, googleCerts)
//
// But you are instead using a cached (and periodically refreshed)
// for each operation.
_ = jws.WithKeySet(cached)
}
```
source: [examples/jwk_cached_set_example_test.go](https://github.com/lestrrat-go/jwx/blob/v2/examples/jwk_cached_set_example_test.go)
<!-- END INCLUDE -->

## Using Whitelists

If you are fetching JWK Sets from a possibly untrusted source such as the URL in the`"jku"` field of a JWS message,
Expand Down
6 changes: 3 additions & 3 deletions examples/jwe_decrypt_with_keyset_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ func ExampleJWE_VerifyWithJWKSet() {
set := jwk.NewSet()
// Add some bogus keys
k1, _ := jwk.FromRaw([]byte("abracadavra"))
set.Add(k1)
set.AddKey(k1)
k2, _ := jwk.FromRaw([]byte("opensasame"))
set.Add(k2)
set.AddKey(k2)
// Add the real thing
k3, _ := jwk.FromRaw(privkey)
k3.Set(jwk.AlgorithmKey, jwa.RSA_OAEP)
set.Add(k3)
set.AddKey(k3)

// Up to this point, you probably will replace with a simple jwk.Fetch()

Expand Down
40 changes: 40 additions & 0 deletions examples/jwk_cached_set_example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package examples_test

import (
"context"
"fmt"
"time"

"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jws"
)

func ExampleJWK_CachedSet() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

const googleCerts = `https://www.googleapis.com/oauth2/v3/certs`

// The first steps are the same as examples/jwk_cache_example_test.go
c := jwk.NewCache(ctx)
c.Register(googleCerts, jwk.WithMinRefreshInterval(15*time.Minute))
_, err := c.Refresh(ctx, googleCerts)
if err != nil {
fmt.Printf("failed to refresh google JWKS: %s\n", err)
return
}

cached := jwk.NewCachedSet(c, googleCerts)

// cached fulfills the jwk.Set interface.
var _ jwk.Set = cached

// That means you can pass it to things like jws.WithKeySet,
// allowing you to pretend as if you are using the result of
//
// jwk.Fetch(ctx, googleCerts)
//
// But you are instead using a cached (and periodically refreshed)
// for each operation.
_ = jws.WithKeySet(cached)
}
Loading

0 comments on commit fd8fafc

Please sign in to comment.