diff --git a/go.mod b/go.mod index 8e745fb65..67972ac18 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/CiscoM31/godata v1.0.10 github.com/KimMachineGun/automemlimit v0.6.1 github.com/Masterminds/semver v1.5.0 - github.com/MicahParks/keyfunc v1.9.0 + github.com/MicahParks/keyfunc/v2 v2.1.0 github.com/Nerzal/gocloak/v13 v13.9.0 github.com/bbalet/stopwords v1.0.0 github.com/beevik/etree v1.4.1 @@ -42,7 +42,7 @@ require ( github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0 github.com/go-playground/validator/v10 v10.22.0 github.com/gofrs/uuid v4.4.0+incompatible - github.com/golang-jwt/jwt/v4 v4.5.0 + github.com/golang-jwt/jwt/v5 v5.2.1 github.com/golang/protobuf v1.5.4 github.com/google/go-cmp v0.6.0 github.com/google/go-tika v0.3.1 @@ -220,7 +220,7 @@ require ( github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/golang-jwt/jwt/v5 v5.0.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect github.com/golang/glog v1.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect diff --git a/go.sum b/go.sum index 80e745ce6..1d25b439d 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3Q github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID3+o= -github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw= +github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k= +github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= @@ -482,11 +482,10 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo= github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= diff --git a/ocis-pkg/oidc/client.go b/ocis-pkg/oidc/client.go index 5c73f7134..741f67937 100644 --- a/ocis-pkg/oidc/client.go +++ b/ocis-pkg/oidc/client.go @@ -15,10 +15,10 @@ import ( "sync" "time" - "github.com/MicahParks/keyfunc" + "github.com/MicahParks/keyfunc/v2" goidc "github.com/coreos/go-oidc/v3/oidc" "github.com/go-jose/go-jose/v3" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/proxy/pkg/config" "golang.org/x/oauth2" @@ -296,7 +296,14 @@ func (c *oidcClient) verifyAccessTokenJWT(token string) (RegClaimsWithSID, jwt.M return claims, mapClaims, errors.New("error initializing jwks keyfunc") } - _, err := jwt.ParseWithClaims(token, &claims, jwks.Keyfunc) + issuer := c.issuer + if c.provider.AccessTokenIssuer != "" { + // AD FS .well-known/openid-configuration has an optional `access_token_issuer` which takes precedence over `issuer` + // See https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-oidce/586de7dd-3385-47c7-93a2-935d9e90441c + issuer = c.provider.AccessTokenIssuer + } + + _, err := jwt.ParseWithClaims(token, &claims, jwks.Keyfunc, jwt.WithIssuer(issuer)) if err != nil { return claims, mapClaims, err } @@ -308,20 +315,6 @@ func (c *oidcClient) verifyAccessTokenJWT(token string) (RegClaimsWithSID, jwt.M return claims, mapClaims, err } - issuer := c.issuer - if c.provider.AccessTokenIssuer != "" { - // AD FS .well-known/openid-configuration has an optional `access_token_issuer` which takes precedence over `issuer` - // See https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-oidce/586de7dd-3385-47c7-93a2-935d9e90441c - issuer = c.provider.AccessTokenIssuer - } - - if !claims.VerifyIssuer(issuer, true) { - vErr := jwt.ValidationError{} - vErr.Inner = jwt.ErrTokenInvalidIssuer - vErr.Errors |= jwt.ValidationErrorIssuer - return claims, mapClaims, vErr - } - return claims, mapClaims, nil } diff --git a/ocis-pkg/oidc/metadata.go b/ocis-pkg/oidc/metadata.go index 2f952e226..2d2708a0f 100644 --- a/ocis-pkg/oidc/metadata.go +++ b/ocis-pkg/oidc/metadata.go @@ -6,7 +6,7 @@ import ( "net/http" "strings" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" "github.com/owncloud/ocis/v2/ocis-pkg/log" ) diff --git a/ocis-pkg/oidc/mocks/oidc_client.go b/ocis-pkg/oidc/mocks/oidc_client.go index 0aa0dfdfe..450b135de 100644 --- a/ocis-pkg/oidc/mocks/oidc_client.go +++ b/ocis-pkg/oidc/mocks/oidc_client.go @@ -1,11 +1,11 @@ -// Code generated by mockery v2.40.2. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks import ( context "context" - jwt "github.com/golang-jwt/jwt/v4" + jwt "github.com/golang-jwt/jwt/v5" mock "github.com/stretchr/testify/mock" oauth2 "golang.org/x/oauth2" diff --git a/services/collaboration/pkg/middleware/claims.go b/services/collaboration/pkg/middleware/claims.go index 893e19e94..c357c8119 100644 --- a/services/collaboration/pkg/middleware/claims.go +++ b/services/collaboration/pkg/middleware/claims.go @@ -1,6 +1,6 @@ package middleware -import "github.com/golang-jwt/jwt/v4" +import "github.com/golang-jwt/jwt/v5" // Claims contains the jwt registered claims plus the used WOPI context type Claims struct { diff --git a/services/collaboration/pkg/middleware/wopicontext.go b/services/collaboration/pkg/middleware/wopicontext.go index e6b9dff0a..22dcdf008 100644 --- a/services/collaboration/pkg/middleware/wopicontext.go +++ b/services/collaboration/pkg/middleware/wopicontext.go @@ -11,7 +11,7 @@ import ( userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" "github.com/owncloud/ocis/v2/services/collaboration/pkg/config" "github.com/owncloud/ocis/v2/services/collaboration/pkg/helpers" "github.com/rs/zerolog" @@ -69,11 +69,6 @@ func WopiContextAuthMiddleware(cfg *config.Config, next http.Handler) http.Handl return } - if err := claims.Valid(); err != nil { - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - return - } - ctx := r.Context() wopiContextAccessToken, err := DecryptAES([]byte(cfg.Wopi.Secret), claims.WopiContext.AccessToken) diff --git a/services/collaboration/pkg/service/grpc/v0/service_test.go b/services/collaboration/pkg/service/grpc/v0/service_test.go index 68f5aa141..6fd77b125 100644 --- a/services/collaboration/pkg/service/grpc/v0/service_test.go +++ b/services/collaboration/pkg/service/grpc/v0/service_test.go @@ -6,7 +6,7 @@ import ( "time" "github.com/cs3org/reva/v2/pkg/utils" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/stretchr/testify/mock" diff --git a/services/proxy/pkg/middleware/authentication_test.go b/services/proxy/pkg/middleware/authentication_test.go index 25601dbcd..4b15deb59 100644 --- a/services/proxy/pkg/middleware/authentication_test.go +++ b/services/proxy/pkg/middleware/authentication_test.go @@ -10,7 +10,7 @@ import ( userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/owncloud/ocis/v2/ocis-pkg/log" diff --git a/services/proxy/pkg/middleware/oidc_auth.go b/services/proxy/pkg/middleware/oidc_auth.go index fea68dc5f..47b9c424d 100644 --- a/services/proxy/pkg/middleware/oidc_auth.go +++ b/services/proxy/pkg/middleware/oidc_auth.go @@ -8,7 +8,7 @@ import ( "strings" "time" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/oidc" "github.com/pkg/errors" diff --git a/services/proxy/pkg/middleware/oidc_auth_test.go b/services/proxy/pkg/middleware/oidc_auth_test.go index eddb5eb72..70a047706 100644 --- a/services/proxy/pkg/middleware/oidc_auth_test.go +++ b/services/proxy/pkg/middleware/oidc_auth_test.go @@ -5,7 +5,7 @@ import ( "net/http/httptest" "time" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/owncloud/ocis/v2/ocis-pkg/log" diff --git a/services/thumbnails/pkg/service/grpc/v0/service.go b/services/thumbnails/pkg/service/grpc/v0/service.go index 7049064b7..e7bde4c5d 100644 --- a/services/thumbnails/pkg/service/grpc/v0/service.go +++ b/services/thumbnails/pkg/service/grpc/v0/service.go @@ -15,7 +15,7 @@ import ( "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" "github.com/pkg/errors" merrors "go-micro.dev/v4/errors" "google.golang.org/grpc/metadata" diff --git a/services/thumbnails/pkg/service/http/v0/service.go b/services/thumbnails/pkg/service/http/v0/service.go index 175b24230..eb7446d1e 100644 --- a/services/thumbnails/pkg/service/http/v0/service.go +++ b/services/thumbnails/pkg/service/http/v0/service.go @@ -3,12 +3,13 @@ package svc import ( "context" "fmt" - "github.com/go-chi/chi/v5" - "github.com/golang-jwt/jwt/v4" - "github.com/riandyrn/otelchi" "net/http" "strconv" + "github.com/go-chi/chi/v5" + "github.com/golang-jwt/jwt/v5" + "github.com/riandyrn/otelchi" + "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/tracing" "github.com/owncloud/ocis/v2/services/thumbnails/pkg/config" diff --git a/services/thumbnails/pkg/service/jwt/jwt.go b/services/thumbnails/pkg/service/jwt/jwt.go index 86e1ff02c..14df7560b 100644 --- a/services/thumbnails/pkg/service/jwt/jwt.go +++ b/services/thumbnails/pkg/service/jwt/jwt.go @@ -1,6 +1,6 @@ package jwt -import "github.com/golang-jwt/jwt/v4" +import "github.com/golang-jwt/jwt/v5" // ThumbnailClaims defines the claims for thumb-nailing type ThumbnailClaims struct { diff --git a/vendor/github.com/MicahParks/keyfunc/LICENSE b/vendor/github.com/MicahParks/keyfunc/v2/LICENSE similarity index 100% rename from vendor/github.com/MicahParks/keyfunc/LICENSE rename to vendor/github.com/MicahParks/keyfunc/v2/LICENSE diff --git a/vendor/github.com/MicahParks/keyfunc/README.md b/vendor/github.com/MicahParks/keyfunc/v2/README.md similarity index 81% rename from vendor/github.com/MicahParks/keyfunc/README.md rename to vendor/github.com/MicahParks/keyfunc/v2/README.md index ed07eaa7f..c04b3dcff 100644 --- a/vendor/github.com/MicahParks/keyfunc/README.md +++ b/vendor/github.com/MicahParks/keyfunc/v2/README.md @@ -1,35 +1,41 @@ -[![Go Report Card](https://goreportcard.com/badge/github.com/MicahParks/keyfunc)](https://goreportcard.com/report/github.com/MicahParks/keyfunc) [![Go Reference](https://pkg.go.dev/badge/github.com/MicahParks/keyfunc.svg)](https://pkg.go.dev/github.com/MicahParks/keyfunc) +[![Go Report Card](https://goreportcard.com/badge/github.com/MicahParks/keyfunc/v2)](https://goreportcard.com/report/github.com/MicahParks/keyfunc/v2) [![Go Reference](https://pkg.go.dev/badge/github.com/MicahParks/keyfunc/v2.svg)](https://pkg.go.dev/github.com/MicahParks/keyfunc/v2) # keyfunc The purpose of this package is to provide a -[`jwt.Keyfunc`](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#Keyfunc) for the -[github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) package using a JSON Web Key Set (JWK Set or JWKS) for +[`jwt.Keyfunc`](https://pkg.go.dev/github.com/golang-jwt/jwt/v5#Keyfunc) for the +[github.com/golang-jwt/jwt/v5](https://github.com/golang-jwt/jwt) package using a JSON Web Key Set (JWK Set or JWKS) for parsing and verifying JSON Web Tokens (JWTs). +The last version to support `github.com/golang-jwt/jwt/v4` +is [`v1.9.0`](https://github.com/MicahParks/keyfunc/releases/tag/v1.9.0). + There is legacy support for `github.com/dgrijalva/jwt-go` and its popular forks. It's in a separate project to keep this project minimal. If your use case supports a legacy fork, please -see: [github.com/MicahParks/compatibility-keyfunc](https://github.com/MicahParks/compatibility-keyfunc). +see: [github.com/MicahParks/compatibility-keyfunc](https://github.com/MicahParks/compatibility-keyfunc). If an updated +to `keyfunc` is needed for `github.com/golang-jwt/jwt/v4` users, it will be placed into this separate project. It's common for an identity provider, such as [Keycloak](https://www.keycloak.org/) or [Amazon Cognito (AWS)](https://aws.amazon.com/cognito/) to expose a JWKS via an HTTPS endpoint. This package has the ability to consume that JWKS and produce a -[`jwt.Keyfunc`](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#Keyfunc). It is important that a JWKS endpoint is using +[`jwt.Keyfunc`](https://pkg.go.dev/github.com/golang-jwt/jwt/v5#Keyfunc). It is important that a JWKS endpoint is using HTTPS to ensure the keys are from the correct trusted source. -This repository only depends on: [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) +This repository only depends on: [github.com/golang-jwt/jwt/v5](https://github.com/golang-jwt/jwt) `jwt.Keyfunc` signatures are imported from these, implemented, then exported as methods. ## Supported Algorithms Currently, this package supports JWTs signed with a `kty` that matches one of the following: + * `EC` [Elliptic Curve Digital Signature Algorithm (ECDSA)](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm) * `RSA` [Rivest–Shamir–Adleman (RSA)](https://en.wikipedia.org/wiki/RSA_(cryptosystem)) * `OKP` [Edwards-curve Digital Signature Algorithm (EdDSA)](https://en.wikipedia.org/wiki/EdDSA) * `OCT` [HMAC](https://en.wikipedia.org/wiki/HMAC), [AES Key Wrap](https://en.wikipedia.org/wiki/Key_Wrap), and others Additionally, the supported `EC` elliptical curve types are below: + * `P-256` * `P-384` * `P-521` @@ -48,13 +54,14 @@ this Go package, please open an issue or pull request. For complete examples, please see the `examples` directory. ```go -import "github.com/MicahParks/keyfunc" +import "github.com/MicahParks/keyfunc/v2" ``` #### A note on read-only keys -The [`JWKS.ReadOnlyKeys`](https://pkg.go.dev/github.com/MicahParks/keyfunc#JWKS.ReadOnlyKeys) method returns a read-only + +The [`JWKS.ReadOnlyKeys`](https://pkg.go.dev/github.com/MicahParks/keyfunc/v2#JWKS.ReadOnlyKeys) method returns a read-only copy of a `map[string]interface{}`. The key to this map is the key ID, `kid`, and the value is the cryptographic key. -This is a useful map for use of keys within a JWKS outside of `github.com/golang-jwt/jwt/v4`. +This is a useful map for use of keys within a JWKS outside of `github.com/golang-jwt/jwt/v5`. The map itself is a copy. So it can be modified safely. However, the values are of type `interface{}`. If these values are modified, it may cause undefined behavior. @@ -62,7 +69,7 @@ are modified, it may cause undefined behavior. ### Preconditions: Acquire the JWKS URL, JSON, or gather cryptographic keys (given keys) A JWKS URL is not required, one can be created directly from JSON with the -[`keyfunc.NewJSON`](https://pkg.go.dev/github.com/MicahParks/keyfunc#NewJSON) function. +[`keyfunc.NewJSON`](https://pkg.go.dev/github.com/MicahParks/keyfunc/v2#NewJSON) function. ```go // Get the JWKS URL from an environment variable. @@ -77,6 +84,7 @@ if jwksURL == "" { ### Step 1: Create the JWKS Via HTTP: + ```go // Create the JWKS from the resource at the given URL. jwks, err := keyfunc.Get(jwksURL, keyfunc.Options{}) // See recommended options in the examples directory. @@ -84,7 +92,9 @@ if err != nil { log.Fatalf("Failed to get the JWKS from the given URL.\nError: %s", err) } ``` + Via JSON: + ```go // Get the JWKS as JSON. var jwksJSON = json.RawMessage(`{"keys":[{"kid":"zXew0UJ1h6Q4CCcd_9wxMzvcp5cEBifH0KWrCz2Kyxc","kty":"RSA","alg":"PS256","use":"sig","n":"wqS81x6fItPUdh1OWCT8p3AuLYgFlpmg61WXp6sp1pVijoyF29GOSaD9xE-vLtegX-5h0BnP7va0bwsOAPdh6SdeVslEifNGHCtID0xNFqHNWcXSt4eLfQKAPFUq0TsEO-8P1QHRq6yeG8JAFaxakkaagLFuV8Vd_21PGJFWhvJodJLhX_-Ym9L8XUpIPps_mQriMUOWDe-5DWjHnDtfV7mgaOxbBvVo3wj8V2Lmo5Li4HabT4MEzeJ6e9IdFo2kj_44Yy9osX-PMPtu8BQz_onPgf0wjrVWt349Rj6OkS8RxlNGYeuIxYZr0TOhP5F-yEPhSXDsKdVTwPf7zAAaKQ","e":"AQAB","x5c":["MIICmzCCAYMCBgF4HR7HNDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjEwMzEwMTcwOTE5WhcNMzEwMzEwMTcxMDU5WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCpLzXHp8i09R2HU5YJPyncC4tiAWWmaDrVZenqynWlWKOjIXb0Y5JoP3ET68u16Bf7mHQGc/u9rRvCw4A92HpJ15WyUSJ80YcK0gPTE0Woc1ZxdK3h4t9AoA8VSrROwQ77w/VAdGrrJ4bwkAVrFqSRpqAsW5XxV3/bU8YkVaG8mh0kuFf/5ib0vxdSkg+mz+ZCuIxQ5YN77kNaMecO19XuaBo7FsG9WjfCPxXYuajkuLgdptPgwTN4np70h0WjaSP/jhjL2ixf48w+27wFDP+ic+B/TCOtVa3fj1GPo6RLxHGU0Zh64jFhmvRM6E/kX7IQ+FJcOwp1VPA9/vMABopAgMBAAEwDQYJKoZIhvcNAQELBQADggEBALILq1Z4oQNJZEUt24VZcvknsWtQtvPxl3JNcBQgDR5/IMgl5VndRZ9OT56KUqrR5xRsWiCvh5Lgv4fUEzAAo9ToiPLub1SKP063zWrvfgi3YZ19bty0iXFm7l2cpQ3ejFV7WpcdLJE0lapFdPLo6QaRdgNu/1p4vbYg7zSK1fQ0OY5b3ajhAx/bhWlrN685owRbO5/r4rUOa6oo9l4Qn7jUxKUx4rcoe7zUM7qrpOPqKvn0DBp3n1/+9pOZXCjIfZGvYwP5NhzBDCkRzaXcJHlOqWzMBzyovVrzVmUilBcj+EsTYJs0gVXKzduX5zO6YWhFs23lu7AijdkxTY65YM0="],"x5t":"IYIeevIT57t8ppUejM42Bqx6f3I","x5t#S256":"TuOrBy2NcTlFSWuZ8Kh8W8AjQagb4fnfP1SlKMO8-So"},{"kid":"ebJxnm9B3QDBljB5XJWEu72qx6BawDaMAhwz4aKPkQ0","kty":"EC","alg":"ES512","use":"sig","crv":"P-521","x":"YQ95Xj8MTzcHytbU1h8YkCN2kdEQA7ThuZ1ctB9Ekiw6tlM9RwL62eQvzEt4Rz8qN69uRqgU9RzxQOkSU5xVvyo","y":"SMMuP3QnAPHtx7Go2ARsG3NBaySWBLmVvS8s2Ss7Vm_ISWenNbdjKOsY1XvtiQz5scGzWDCEUoZzgV8Ve1mLOV0"},{"kid":"TVAAet63O3xy_KK6_bxVIu7Ra3_z1wlB543Fbwi5VaU","kty":"EC","alg":"ES384","use":"sig","crv":"P-384","x":"Pik2o5as-evijFABH5p6YLXHnWw8iQ_N1ummPY1c_UgG6NO0za-gNOhTz2-tsd_w","y":"e98VSff71k19SY_mHgp3707lgQVrhfVpiGa-sGaKxOWVpxd2jWMhB0Q4RpSRuCp5"},{"kid":"arlUxX4hh56rNO-XdIPhDT7bqBMqcBwNQuP_TnZJNGs","kty":"RSA","alg":"RS512","use":"sig","n":"hhtifu8LL3ICE3BAX5l1KZv6Lni0lhlhBusSfepnpxcb4C_z2U71cQTnLY27kt8WB4bNG6e5_KMx9K3xUdd3euj9MCq8vytwEPieeHE1KXQuhJfLv017lhpK_dRMOHyc-9-50YNdgs_8KWRkrzjjuYrCiO9Iu76n5319e-SC8OPvNUglqxp2N0Sp2ltne2ZrpN8T3OEEXT62TSGmLAVopRGw5gllNVrJfmEyZJCRrBM6s5CQcz8un0FjkAAC4DI6QD-eBL0qG3_NR0hQvR1he2o4BLwjOKH45Pk_jj-eArp-DD6Xq6ABQVb5SNOSdaxl5lnmuotRoY3G5d9YSl-K3w","e":"AQAB","x5c":["MIICmzCCAYMCBgF4HSCcDzANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjEwMzEwMTcxMTE5WhcNMzEwMzEwMTcxMjU5WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCGG2J+7wsvcgITcEBfmXUpm/oueLSWGWEG6xJ96menFxvgL/PZTvVxBOctjbuS3xYHhs0bp7n8ozH0rfFR13d66P0wKry/K3AQ+J54cTUpdC6El8u/TXuWGkr91Ew4fJz737nRg12Cz/wpZGSvOOO5isKI70i7vqfnfX175ILw4+81SCWrGnY3RKnaW2d7Zmuk3xPc4QRdPrZNIaYsBWilEbDmCWU1Wsl+YTJkkJGsEzqzkJBzPy6fQWOQAALgMjpAP54EvSobf81HSFC9HWF7ajgEvCM4ofjk+T+OP54Cun4MPperoAFBVvlI05J1rGXmWea6i1Ghjcbl31hKX4rfAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAB7bpwPoL02WGCCVhCsbDkq9GeFUwF01opVyFTijZlTUoTf5RcaR2qAH9/irkLjZeFeyozzC5mGvIVruBwnx/6l4PcAMxKK4YiheFVoO/dytpGMCj6ToNmKpjlXzOLAHelieWIUDtAFSYzENjIO01PyXTGYpxebpQCocJBvppj5HqARS9iNPcqBltMhxWrWmMu81tOG3Y7yd2xsIYXk6KjaoefLeN8Was4BPJ0zR6tTSEm6ZOvSRvlppqh84kz7LmWem7gGHAsY2G3tWBUmOdO/SMNMThqV62yLf7sKsuoE1w06lfmrf6D2zGwoEyz+TT6fdSkc34Yeh7+c01X6nFWU="],"x5t":"geiCPGtT_10T8xGLUK1LA0_YQEE","x5t#S256":"dLp3_QNGwMbYll5VecnR8Q9NSeFVfqJPBTa2_8qf48I"},{"kid":"tW6ae7TomE6_2jooM-sf9N_6lWg7HNtaQXrDsElBzM4","kty":"RSA","alg":"PS512","use":"sig","n":"p32N7jqKfMUB6_dKY1uZ3wizzPlBAXg9XrntfUcwNLRPfTBnshpt4uQBf3T8fexkbzhtR18oHvim-YvcWfC5eLGQmWHYiVwACa_C7oGqx51ijK2LRbUg4TKhnZX2X3Ld9xvr3HsosKh2UXn_Ay8nuvdfH-U6S7btT6a-AIFlt3BpqZP0EOl7rY-ie8nXoA13xX6BoyzYiNcugdYCU6czQcmTIJ1JLS0zohi4aTNehRt-1VMRpIMx7q7Ouq3Zhbi7RcDo-_D8FPRhWc2eEKd-h8ebFTIxEOrkguBIomjEFTf3SfYbOB_h-14v9Q2yz-NzyId3-ujRCQGC0hn-cixe2w","e":"AQAB","x5c":["MIICmzCCAYMCBgF4BKAxqzANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjEwMzA1MjMwMDEwWhcNMzEwMzA1MjMwMTUwWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCnfY3uOop8xQHr90pjW5nfCLPM+UEBeD1eue19RzA0tE99MGeyGm3i5AF/dPx97GRvOG1HXyge+Kb5i9xZ8Ll4sZCZYdiJXAAJr8LugarHnWKMrYtFtSDhMqGdlfZfct33G+vceyiwqHZRef8DLye6918f5TpLtu1Ppr4AgWW3cGmpk/QQ6Xutj6J7ydegDXfFfoGjLNiI1y6B1gJTpzNByZMgnUktLTOiGLhpM16FG37VUxGkgzHurs66rdmFuLtFwOj78PwU9GFZzZ4Qp36Hx5sVMjEQ6uSC4EiiaMQVN/dJ9hs4H+H7Xi/1DbLP43PIh3f66NEJAYLSGf5yLF7bAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHVWNBTExqlg4LTcyhUXI5U0iNPcMIVdKDoGPDc3EPjXyYNyjURX0oZ6b1Wv5t+XGmpZRqJNYb92xraQatIzLEsRn4IrmzViP+dIyFU8BEDubixTxeqx7LSw2j6LIFnZ05XdmWknlksNTlqi4CT6KL+1c24+QU3CcmU3mkQEIPA2yC4SdAB1oXI0jh49uP6a+JrE7JREZGAdwbIpZ1cqV6acPiJW3tOYfLrHwo7KYn3KwJvIBHXgFBNwx7fl2gYNQ0VEGKub3qVwW5RO5R/6Tcla9uZEfEiamms/Pn4hFA1qbsNHtA9IRGVRSmVeBKDxRvo0fxOUXp+NuZxEnhsoP3I="],"x5t":"f1l1fxICz1fe9mI-sSrtc19EDhU","x5t#S256":"NUJWRA4ADpLEg_SMkSoE4FKQN0H1Tlz85L-i7puVcqQ"},{"kid":"Lx1FmayP2YBtxaqS1SKJRJGiXRKnw2ov5WmYIMG-BLE","kty":"RSA","alg":"PS384","use":"sig","n":"q7WM4SnrdzlFSo_A1DRhc-8Ho-pBsfs49kGRbw3O_OKFIUyZrzHaRuovW_QaEAyiO3HX8CNcGPcpHdmpl4DhTGEBLcd6xXtCaa65ct00Mq7ZHCRRCrKLh6lJ0rY9fP8vCV0RBigpkNoRfrqLQQN4VeVFTbGSrDaS0LzPbap0-q5FKXUR-OQmQEtOupXhKFQtbB73tL83YnG6Swl7nXsx54ulEoDzcCCYt7pjCVVp7L9fzI2_ucTdtQclAJVQZGKpsx7vabOJuiMUwuAIz56lOJyXRMePsW8UogwC4FA2A52STsYlhOPsDEW4iIExFVNqs-CGoDGhYLIavaCkZhXM0w","e":"AQAB","x5c":["MIICmzCCAYMCBgF4HR+9XjANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjEwMzEwMTcxMDIyWhcNMzEwMzEwMTcxMjAyWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrtYzhKet3OUVKj8DUNGFz7wej6kGx+zj2QZFvDc784oUhTJmvMdpG6i9b9BoQDKI7cdfwI1wY9ykd2amXgOFMYQEtx3rFe0Jprrly3TQyrtkcJFEKsouHqUnStj18/y8JXREGKCmQ2hF+uotBA3hV5UVNsZKsNpLQvM9tqnT6rkUpdRH45CZAS066leEoVC1sHve0vzdicbpLCXudezHni6USgPNwIJi3umMJVWnsv1/Mjb+5xN21ByUAlVBkYqmzHu9ps4m6IxTC4AjPnqU4nJdEx4+xbxSiDALgUDYDnZJOxiWE4+wMRbiIgTEVU2qz4IagMaFgshq9oKRmFczTAgMBAAEwDQYJKoZIhvcNAQELBQADggEBADTgP3SrcG3p9XUB7sM4a2IeY0J4bSEtqlZBuHgdgekYJ5DXETJ3hV/82GjitU50NBup0IJyI9KZ0KCwqHIKC2Jn/6biOpM9Ipk4BtNVzx3qKNsDac9qZmyMpm4V9QuWakajknerhwyynG3siGUntbPmLvf5UKvKtbiKlWS4dBPwfedIUnC85mYEnNKSzSI1NiM6TWHB9zQYkARXlb89sh0HBYs08BfRMyBVM+l3OczIyGeQAfhcL+pxPP/0jqPr1ctHUBj2zXkjZxDw1oJFgeD9GDtPcjc3spB20vsRtQUBlzbJElbGflqWGHJK5l5n7gNd3ZXZT0HJ+wUpPE8EUaM="],"x5t":"fjRYR1986VCLzbaZaw5r25UKahw","x5t#S256":"ZHNHpizlsjD3qSZh7gJQQBu8W9jBL2HR0y7-3u2Wb-g"},{"kid":"gnmAfvmlsi3kKH3VlM1AJ85P2hekQ8ON_XvJqs3xPD8","kty":"RSA","alg":"RS384","use":"sig","n":"qUNQewKl3APQcbpACMNJ2XphPpupt395z6OZvj5CW9tiRXY3J7dqi8U0bWoIhtmmc7Js6hjp-A5W_FVStuXlT1hLyjJsHeu9ZVPnfIl2MnYN83zQBKw8E4mFsVv0UXNvkVPBF_k0yXrz-ABleWLOgFGnkNU9csc3Z5aihHcwRmC_oS7PZ9Vc-l0xBCyF3YRHI-al8ppSHwFreOweF3-JP3poNAXd906_tjX2KlHSJmNqcUNiSfEluyCp02ALlRFKXUQ1HlfSupHcHySDlanfUyIzZgM9ysCvC1vfNdAuwZ44oUBMul_XPxxhzlewL2Y8PtSDLUDWGTIou8M8049D8Q","e":"AQAB","x5c":["MIICmzCCAYMCBgF4BJVfaDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjEwMzA1MjI0ODIxWhcNMzEwMzA1MjI1MDAxWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpQ1B7AqXcA9BxukAIw0nZemE+m6m3f3nPo5m+PkJb22JFdjcnt2qLxTRtagiG2aZzsmzqGOn4Dlb8VVK25eVPWEvKMmwd671lU+d8iXYydg3zfNAErDwTiYWxW/RRc2+RU8EX+TTJevP4AGV5Ys6AUaeQ1T1yxzdnlqKEdzBGYL+hLs9n1Vz6XTEELIXdhEcj5qXymlIfAWt47B4Xf4k/emg0Bd33Tr+2NfYqUdImY2pxQ2JJ8SW7IKnTYAuVEUpdRDUeV9K6kdwfJIOVqd9TIjNmAz3KwK8LW9810C7BnjihQEy6X9c/HGHOV7AvZjw+1IMtQNYZMii7wzzTj0PxAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABoThxhMd7Xiq4x0GJeoJFv2yDKXCL3dJEAEWtOr2+PqdeJl/ZfOxBXynIvrdtYnQdICztN5ydEgDsZ02piDsxZ+s/0SA0iqjw/MEoBYobmr8V+xwUv+WtRLpTBXqWGMuG7NEtrbjKid0iKLLAOAU4dcHQ49iOF9VLnbTkf1EXp4iphJreaubOXMwT6/JDzQPT1dRR34hlhYeKKzMSA0Cz5aYL1tI+eH12rar0MDczXykLChNS/8MlyTzreEf0siUiS9S1kj/lOZKQDg9E/z8fm5vmHEHzAVwf4ON5iO29tDsqLw7BeJqC4AESjliXIqMrdpFynfPnIsGgf3dnph5BM="],"x5t":"CmRnQVduZWtEsdOC4mauUUsSWxA","x5t#S256":"BvC0LmuM8ZIApN3TQQZWWbGO-d082Ah5d3D6vPvahGw"},{"kid":"CGt0ZWS4Lc5faiKSdi0tU0fjCAdvGROQRGU9iR7tV0A","kty":"EC","alg":"ES256","use":"sig","crv":"P-256","x":"DPW7n9yjfE6Rt-VvVmEdeu4QdW44qifocAPPDxACDDY","y":"-ejsVw8222-hg2dJWx3QV0hE4-I0Ujp7ZsWebE68JE0"},{"kid":"C65q0EKQyhpd1m4fr7SKO2He_nAxgCtAdws64d2BLt8","kty":"RSA","alg":"RS256","use":"sig","n":"ja99ybDrLvw11Z4CvNlDI-kkqJEBpSnvDf0pZF2DvBlvYmeVYL_ChqIe8E9GyHUmLMdtO_jifSgOqE5b8vILwi1kZnJR7N857uEnbWM9YTeevi_RZ-E_hr4frW2NKJ78YGvCzwLKG2GgtSjj0zuTLnSaK8fCGzqXgy6paXNhgHUSZgGwvO0YItpMlyJeqEj1wGTWz1IyA1sguF1cC7K0fojPbPoBwrhvaAeoGRPLraE0rrBsQv8iiLwnRBIez9B1j0NiUG8Iad953Y7UzaKOAw8crIEK45NIK_yxHUpxqcHLjPIcRyIyJGioRyGK7cp-_7iPLOCutQc-u46mom1_ZQ","e":"AQAB","x5c":["MIICmzCCAYMCBgF4BJRpbzANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjEwMzA1MjI0NzE4WhcNMzEwMzA1MjI0ODU4WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCNr33JsOsu/DXVngK82UMj6SSokQGlKe8N/SlkXYO8GW9iZ5Vgv8KGoh7wT0bIdSYsx207+OJ9KA6oTlvy8gvCLWRmclHs3znu4SdtYz1hN56+L9Fn4T+Gvh+tbY0onvxga8LPAsobYaC1KOPTO5MudJorx8IbOpeDLqlpc2GAdRJmAbC87Rgi2kyXIl6oSPXAZNbPUjIDWyC4XVwLsrR+iM9s+gHCuG9oB6gZE8utoTSusGxC/yKIvCdEEh7P0HWPQ2JQbwhp33ndjtTNoo4DDxysgQrjk0gr/LEdSnGpwcuM8hxHIjIkaKhHIYrtyn7/uI8s4K61Bz67jqaibX9lAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHrGJFhVNiQupIwkn2jiW/jBobm9CHUxOwQL5E7WdRz5uaOJ0v62PrynOQE9xim9Qk8bT3q7DThZs66U9bpIk3msKVRgXRfn5FZy1H5RKOlEEFZhGakPqSlC1yPbhUNhHXMs3GTzdGMLtYaGvSy6XM/8/zqVqVwgh6BpbAR9RfiSdyaiNTSBriu+n/tHW934G9J8UIzdfpVcb0Yt9y4o0UgIXt64NtGFq7zmNJijH88AxBZFB6eUUmQQCczebzoAjyYbVOes5gGFzboVWcyLe3iyD0vvsAVHJViXeiGoxhpKnc8ryISpRUBzsKngf5uZo3bnrD9PHLYBoGOHgzII1xw="],"x5t":"5GNr3LeRXHWI4YR8-QTSsF98oTI","x5t#S256":"Dgd0_wZZqvRuf4GEISPNHREX-1ixTMIsrPeGzk0bCxs"}]}`) @@ -95,7 +105,9 @@ if err != nil { log.Fatalf("Failed to create JWKS from JSON.\nError: %s", err) } ``` + Via a given key: + ```go // Get an HMAC key. key := []byte("example secret") @@ -107,11 +119,11 @@ jwks := keyfunc.NewGiven(map[string]keyfunc.GivenKey{ }) ``` -Additional options can be passed to the [`keyfunc.Get`](https://pkg.go.dev/github.com/golang-jwt/jwt/v4/keyfunc#Get) -function. See [`keyfunc.Options`](https://pkg.go.dev/github.com/golang-jwt/jwt/v4/keyfunc#Options) and the additional +Additional options can be passed to the [`keyfunc.Get`](https://pkg.go.dev/github.com/golang-jwt/jwt/v5/keyfunc#Get) +function. See [`keyfunc.Options`](https://pkg.go.dev/github.com/golang-jwt/jwt/v5/keyfunc#Options) and the additional features mentioned at the bottom of this `README.md`. -### Step 2: Use the [`JWKS.Keyfunc`](https://pkg.go.dev/github.com/golang-jwt/jwt/v4/keyfunc#JWKS.Keyfunc) method as the [`jwt.Keyfunc`](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#Keyfunc) when parsing tokens +### Step 2: Use the [`JWKS.Keyfunc`](https://pkg.go.dev/github.com/golang-jwt/jwt/v5/keyfunc#JWKS.Keyfunc) method as the [`jwt.Keyfunc`](https://pkg.go.dev/github.com/golang-jwt/jwt/v5#Keyfunc) when parsing tokens ```go // Parse the JWT. @@ -121,7 +133,7 @@ if err != nil { } ``` -The [`JWKS.Keyfunc`](https://pkg.go.dev/github.com/MicahParks/keyfunc#JWKS.Keyfunc) method will automatically select the +The [`JWKS.Keyfunc`](https://pkg.go.dev/github.com/MicahParks/keyfunc/v2#JWKS.Keyfunc) method will automatically select the key with the matching `kid` (if present) and return its public key as the correct Go type to its caller. ## Test coverage @@ -133,50 +145,54 @@ would accomplish the same purpose. There are some hard-coded JWTs which are expi coded JWTs cannot check for parsing and validation errors, just errors within the `jwt.Keyfunc` itself. ## Additional features + These features can be configured by populating fields in the -[`keyfunc.Options`](https://pkg.go.dev/github.com/MicahParks/keyfunc#Options) argument to the -[`keyfunc.Get`](https://pkg.go.dev/github.com/MicahParks/keyfunc#Get) function. +[`keyfunc.Options`](https://pkg.go.dev/github.com/MicahParks/keyfunc/v2#Options) argument to the +[`keyfunc.Get`](https://pkg.go.dev/github.com/MicahParks/keyfunc/v2#Get) function. + * A background refresh of the JWKS keys can be performed. - * A custom background refresh interval can be specified. For an example, please see the `examples/interval` - directory. - * A custom background refresh request context timeout can be specified. Defaults to one minute. For an example, - please see the `examples/ctx` directory. - * A custom background refresh error handling function can be specified. If none is specified, errors go unhandled - silently. For an example, please see the `examples/recommended_options` directory. - * A custom rate limit can be specified to prevent too many requests for a JWKS refresh. For an example, please see - the `examples/recommended_options` directory. - * JWTs with a previously unseen `kid` can prompt an automatic refresh of the remote JWKS resource. This should be - paired with `RefreshRateLimit` to prevent abuse. For an example, please see the `examples/recommended_options` - directory. + * A custom background refresh interval can be specified. For an example, please see the `examples/interval` + directory. + * A custom background refresh request context timeout can be specified. Defaults to one minute. For an example, + please see the `examples/ctx` directory. + * A custom background refresh error handling function can be specified. If none is specified, errors go unhandled + silently. For an example, please see the `examples/recommended_options` directory. + * A custom rate limit can be specified to prevent too many requests for a JWKS refresh. For an example, please see + the `examples/recommended_options` directory. + * JWTs with a previously unseen `kid` can prompt an automatic refresh of the remote JWKS resource. This should be + paired with `RefreshRateLimit` to prevent abuse. For an example, please see the `examples/recommended_options` + directory. * A custom HTTP client can be used. * A custom HTTP request factory can be provided to create HTTP requests for the remote JWKS resource. For example, an HTTP header can be added to indicate a User-Agent. * A custom HTTP response extractor can be provided to get the raw JWKS JSON from the `*http.Response`. For example, the HTTP response code could be checked. Implementations are responsible for closing the response body. - * By default, - the [`keyfunc.ResponseExtractorStatusOK`](https://pkg.go.dev/github.com/MicahParks/keyfunc#ResponseExtractorStatusOK) - function is used. The default behavior changed in `v1.4.0`. + * By default, + the [`keyfunc.ResponseExtractorStatusOK`](https://pkg.go.dev/github.com/MicahParks/keyfunc/v2#ResponseExtractorStatusOK) + function is used. The default behavior changed in `v1.4.0`. * A custom whitelist of acceptable JSON Web Key `"use"` parameter values can be specified. Values not whitelisted will - cause an error from the [`.Keyfunc`](https://pkg.go.dev/github.com/MicahParks/keyfunc#JWKS.Keyfunc) method. This + cause an error from the [`.Keyfunc`](https://pkg.go.dev/github.com/MicahParks/keyfunc/v2#JWKS.Keyfunc) method. This whitelist can be disabled with the `JWKUseNoWhitelist` option. - * By default, only JSON Web Keys with a `"use"` parameter value of `"sig"`, an empty string `""`, or a completely - omitted `"use"` parameter will be returned. The default behavior changed in `v1.5.0`. - * This `"use"` whitelisting behavior is only available with `keyfunc.Get`. It is not available with - `keyfunc.NewJSON` or `keyfunc.NewGiven`. Please open a GitHub issue if you would like this feature added to the - other creation methods. + * By default, only JSON Web Keys with a `"use"` parameter value of `"sig"`, an empty string `""`, or a completely + omitted `"use"` parameter will be returned. The default behavior changed in `v1.5.0`. + * This `"use"` whitelisting behavior is only available with `keyfunc.Get`. It is not available with + `keyfunc.NewJSON` or `keyfunc.NewGiven`. Please open a GitHub issue if you would like this feature added to the + other creation methods. * A map of JWT key IDs (`kid`) to keys can be given and used for the `jwt.Keyfunc`. For an example, see the `examples/given` directory. * A copy of the latest raw JWKS `[]byte` can be returned. * Custom cryptographic algorithms can be used. Make sure to - use [`jwt.RegisterSigningMethod`](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#RegisterSigningMethod) before + use [`jwt.RegisterSigningMethod`](https://pkg.go.dev/github.com/golang-jwt/jwt/v5#RegisterSigningMethod) before parsing JWTs. For an example, see the `examples/custom` directory. * The remote JWKS resource can be refreshed manually using the `.Refresh` method. This can bypass the rate limit, if the option is set. * There is support for creating one `jwt.Keyfunc` from multiple JWK Sets through the use of the `keyfunc.GetMultiple`. ## Notes + Trailing padding is required to be removed from base64url encoded keys inside a JWKS. This is because RFC 7517 defines base64url the same as RFC 7515 Section 2: + * https://datatracker.ietf.org/doc/html/rfc7517#section-1.1 * https://datatracker.ietf.org/doc/html/rfc7515#section-2 @@ -188,8 +204,29 @@ before returning the key for signature verification. If the `alg`s do not match, prevent the key being used for signature verification. If the `alg` is not present in the JWK, this check will not occur. +## Related projects + +### [`github.com/MicahParks/jwkset`](https://github.com/MicahParks/jwkset): + +A JWK Set implementation. Currently, it is only server-side assets. `keyfunc` is my JWK Set client-side implementation. +This project has not had a stable release yet. + +### [`github.com/MicahParks/jcp`](https://github.com/MicahParks/jcp): + +A JWK Set client proxy. JCP for short. This project is a standalone service that uses `keyfunc` under the hood. It +primarily exists for these use cases: + +1. The language or shell a program is written in does not have an adequate JWK Set client. Validate JWTs with `curl`? + Why not? +2. Restrictive networking policies prevent a program from accessing the remote JWK Set directly. +3. Many co-located services need to validate JWTs that were signed by a key that lives in a remote JWK Set. + +If you can integrate `keyfunc` directly into your program, you likely don't need JCP. + ## References + This project was built and tested using various RFCs and services. The services are listed below: + * [Keycloak](https://www.keycloak.org/) * [Sample JWKS Service](https://jwks-service.appspot.com/) * connect2id's [Server JWKSet Gen](https://connect2id.com/products/server/docs/config/jwk-set) diff --git a/vendor/github.com/MicahParks/keyfunc/ecdsa.go b/vendor/github.com/MicahParks/keyfunc/v2/ecdsa.go similarity index 100% rename from vendor/github.com/MicahParks/keyfunc/ecdsa.go rename to vendor/github.com/MicahParks/keyfunc/v2/ecdsa.go diff --git a/vendor/github.com/MicahParks/keyfunc/eddsa.go b/vendor/github.com/MicahParks/keyfunc/v2/eddsa.go similarity index 100% rename from vendor/github.com/MicahParks/keyfunc/eddsa.go rename to vendor/github.com/MicahParks/keyfunc/v2/eddsa.go diff --git a/vendor/github.com/MicahParks/keyfunc/example_jwks.json b/vendor/github.com/MicahParks/keyfunc/v2/example_jwks.json similarity index 100% rename from vendor/github.com/MicahParks/keyfunc/example_jwks.json rename to vendor/github.com/MicahParks/keyfunc/v2/example_jwks.json diff --git a/vendor/github.com/MicahParks/keyfunc/get.go b/vendor/github.com/MicahParks/keyfunc/v2/get.go similarity index 94% rename from vendor/github.com/MicahParks/keyfunc/get.go rename to vendor/github.com/MicahParks/keyfunc/v2/get.go index 5dd754b74..7ba613f0b 100644 --- a/vendor/github.com/MicahParks/keyfunc/get.go +++ b/vendor/github.com/MicahParks/keyfunc/v2/get.go @@ -49,11 +49,21 @@ func Get(jwksURL string, options Options) (jwks *JWKS, err error) { err = jwks.refresh() if err != nil { - return nil, err + if options.TolerateInitialJWKHTTPError { + if jwks.refreshErrorHandler != nil { + jwks.refreshErrorHandler(err) + } + jwks.keys = make(map[string]parsedJWK) + } else { + return nil, err + } } if jwks.refreshInterval != 0 || jwks.refreshUnknownKID { - jwks.ctx, jwks.cancel = context.WithCancel(context.Background()) + if jwks.ctx == nil { + jwks.ctx = context.Background() + } + jwks.ctx, jwks.cancel = context.WithCancel(jwks.ctx) jwks.refreshRequests = make(chan refreshRequest, 1) go jwks.backgroundRefresh() } diff --git a/vendor/github.com/MicahParks/keyfunc/given.go b/vendor/github.com/MicahParks/keyfunc/v2/given.go similarity index 54% rename from vendor/github.com/MicahParks/keyfunc/given.go rename to vendor/github.com/MicahParks/keyfunc/v2/given.go index 68c8abd7f..f66df814d 100644 --- a/vendor/github.com/MicahParks/keyfunc/given.go +++ b/vendor/github.com/MicahParks/keyfunc/v2/given.go @@ -42,28 +42,14 @@ func NewGiven(givenKeys map[string]GivenKey) (jwks *JWKS) { } } -// NewGivenCustom creates a new GivenKey given an untyped variable. The key argument is expected to be a supported +// NewGivenCustom creates a new GivenKey given an untyped variable. The key argument is expected to be a type supported // by the jwt package used. // -// See the https://pkg.go.dev/github.com/golang-jwt/jwt/v4#RegisterSigningMethod function for registering an unsupported -// signing method. -// -// Deprecated: This function does not allow the user to specify the JWT's signing algorithm. Use -// NewGivenCustomWithOptions instead. -func NewGivenCustom(key interface{}) (givenKey GivenKey) { - return GivenKey{ - inter: key, - } -} - -// NewGivenCustomWithOptions creates a new GivenKey given an untyped variable. The key argument is expected to be a type -// supported by the jwt package used. -// // Consider the options carefully as each field may have a security implication. // -// See the https://pkg.go.dev/github.com/golang-jwt/jwt/v4#RegisterSigningMethod function for registering an unsupported +// See the https://pkg.go.dev/github.com/golang-jwt/jwt/v5#RegisterSigningMethod function for registering an unsupported // signing method. -func NewGivenCustomWithOptions(key interface{}, options GivenKeyOptions) (givenKey GivenKey) { +func NewGivenCustom(key interface{}, options GivenKeyOptions) (givenKey GivenKey) { return GivenKey{ algorithm: options.Algorithm, inter: key, @@ -72,18 +58,8 @@ func NewGivenCustomWithOptions(key interface{}, options GivenKeyOptions) (givenK // NewGivenECDSA creates a new GivenKey given an ECDSA public key. // -// Deprecated: This function does not allow the user to specify the JWT's signing algorithm. Use -// NewGivenECDSACustomWithOptions instead. -func NewGivenECDSA(key *ecdsa.PublicKey) (givenKey GivenKey) { - return GivenKey{ - inter: key, - } -} - -// NewGivenECDSACustomWithOptions creates a new GivenKey given an ECDSA public key. -// // Consider the options carefully as each field may have a security implication. -func NewGivenECDSACustomWithOptions(key *ecdsa.PublicKey, options GivenKeyOptions) (givenKey GivenKey) { +func NewGivenECDSA(key *ecdsa.PublicKey, options GivenKeyOptions) (givenKey GivenKey) { return GivenKey{ algorithm: options.Algorithm, inter: key, @@ -92,18 +68,8 @@ func NewGivenECDSACustomWithOptions(key *ecdsa.PublicKey, options GivenKeyOption // NewGivenEdDSA creates a new GivenKey given an EdDSA public key. // -// Deprecated: This function does not allow the user to specify the JWT's signing algorithm. Use -// NewGivenEdDSACustomWithOptions instead. -func NewGivenEdDSA(key ed25519.PublicKey) (givenKey GivenKey) { - return GivenKey{ - inter: key, - } -} - -// NewGivenEdDSACustomWithOptions creates a new GivenKey given an EdDSA public key. -// // Consider the options carefully as each field may have a security implication. -func NewGivenEdDSACustomWithOptions(key ed25519.PublicKey, options GivenKeyOptions) (givenKey GivenKey) { +func NewGivenEdDSA(key ed25519.PublicKey, options GivenKeyOptions) (givenKey GivenKey) { return GivenKey{ algorithm: options.Algorithm, inter: key, @@ -112,18 +78,8 @@ func NewGivenEdDSACustomWithOptions(key ed25519.PublicKey, options GivenKeyOptio // NewGivenHMAC creates a new GivenKey given an HMAC key in a byte slice. // -// Deprecated: This function does not allow the user to specify the JWT's signing algorithm. Use -// NewGivenHMACCustomWithOptions instead. -func NewGivenHMAC(key []byte) (givenKey GivenKey) { - return GivenKey{ - inter: key, - } -} - -// NewGivenHMACCustomWithOptions creates a new GivenKey given an HMAC key in a byte slice. -// // Consider the options carefully as each field may have a security implication. -func NewGivenHMACCustomWithOptions(key []byte, options GivenKeyOptions) (givenKey GivenKey) { +func NewGivenHMAC(key []byte, options GivenKeyOptions) (givenKey GivenKey) { return GivenKey{ algorithm: options.Algorithm, inter: key, @@ -132,18 +88,8 @@ func NewGivenHMACCustomWithOptions(key []byte, options GivenKeyOptions) (givenKe // NewGivenRSA creates a new GivenKey given an RSA public key. // -// Deprecated: This function does not allow the user to specify the JWT's signing algorithm. Use -// NewGivenRSACustomWithOptions instead. -func NewGivenRSA(key *rsa.PublicKey) (givenKey GivenKey) { - return GivenKey{ - inter: key, - } -} - -// NewGivenRSACustomWithOptions creates a new GivenKey given an RSA public key. -// // Consider the options carefully as each field may have a security implication. -func NewGivenRSACustomWithOptions(key *rsa.PublicKey, options GivenKeyOptions) (givenKey GivenKey) { +func NewGivenRSA(key *rsa.PublicKey, options GivenKeyOptions) (givenKey GivenKey) { return GivenKey{ algorithm: options.Algorithm, inter: key, diff --git a/vendor/github.com/MicahParks/keyfunc/jwks.go b/vendor/github.com/MicahParks/keyfunc/v2/jwks.go similarity index 100% rename from vendor/github.com/MicahParks/keyfunc/jwks.go rename to vendor/github.com/MicahParks/keyfunc/v2/jwks.go diff --git a/vendor/github.com/MicahParks/keyfunc/keyfunc.go b/vendor/github.com/MicahParks/keyfunc/v2/keyfunc.go similarity index 90% rename from vendor/github.com/MicahParks/keyfunc/keyfunc.go rename to vendor/github.com/MicahParks/keyfunc/v2/keyfunc.go index 1f082bda0..ae62503f7 100644 --- a/vendor/github.com/MicahParks/keyfunc/keyfunc.go +++ b/vendor/github.com/MicahParks/keyfunc/v2/keyfunc.go @@ -6,7 +6,7 @@ import ( "fmt" "strings" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" ) var ( @@ -14,7 +14,7 @@ var ( ErrKID = errors.New("the JWT has an invalid kid") ) -// Keyfunc matches the signature of github.com/golang-jwt/jwt/v4's jwt.Keyfunc function. +// Keyfunc matches the signature of github.com/golang-jwt/jwt/v5's jwt.Keyfunc function. func (j *JWKS) Keyfunc(token *jwt.Token) (interface{}, error) { kid, alg, err := kidAlg(token) if err != nil { @@ -23,6 +23,7 @@ func (j *JWKS) Keyfunc(token *jwt.Token) (interface{}, error) { return j.getKey(alg, kid) } +// Keyfunc matches the signature of github.com/golang-jwt/jwt/v5's jwt.Keyfunc function. func (m *MultipleJWKS) Keyfunc(token *jwt.Token) (interface{}, error) { return m.keySelector(m, token) } diff --git a/vendor/github.com/MicahParks/keyfunc/multiple.go b/vendor/github.com/MicahParks/keyfunc/v2/multiple.go similarity index 78% rename from vendor/github.com/MicahParks/keyfunc/multiple.go rename to vendor/github.com/MicahParks/keyfunc/v2/multiple.go index 61ea30b80..a7ff6fab8 100644 --- a/vendor/github.com/MicahParks/keyfunc/multiple.go +++ b/vendor/github.com/MicahParks/keyfunc/v2/multiple.go @@ -4,11 +4,11 @@ import ( "errors" "fmt" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" ) // ErrMultipleJWKSSize is returned when the number of JWKS given are not enough to make a MultipleJWKS. -var ErrMultipleJWKSSize = errors.New("multiple JWKS must have two or more remote JWK Set resources") +var ErrMultipleJWKSSize = errors.New("multiple JWKS must have one or more remote JWK Set resources") // MultipleJWKS manages multiple JWKS and has a field for jwt.Keyfunc. type MultipleJWKS struct { @@ -16,14 +16,14 @@ type MultipleJWKS struct { sets map[string]*JWKS // No lock is required because this map is read-only after initialization. } -// GetMultiple creates a new MultipleJWKS. A map of length two or more JWKS URLs to Options is required. +// GetMultiple creates a new MultipleJWKS. A map of length one or more JWKS URLs to Options is required. // // Be careful when choosing Options for each JWKS in the map. If RefreshUnknownKID is set to true for all JWKS in the // map then many refresh requests would take place each time a JWT is processed, this should be rate limited by // RefreshRateLimit. func GetMultiple(multiple map[string]Options, options MultipleOptions) (multiJWKS *MultipleJWKS, err error) { - if multiple == nil || len(multiple) < 2 { - return nil, fmt.Errorf("multiple JWKS must have two or more remote JWK Set resources: %w", ErrMultipleJWKSSize) + if len(multiple) < 1 { + return nil, fmt.Errorf("multiple JWKS must have one or more remote JWK Set resources: %w", ErrMultipleJWKSSize) } if options.KeySelector == nil { @@ -46,6 +46,8 @@ func GetMultiple(multiple map[string]Options, options MultipleOptions) (multiJWK return multiJWKS, nil } +// JWKSets returns a copy of the map of JWK Sets. The map itself is a copy, but the JWKS are not and should be treated +// as read-only. func (m *MultipleJWKS) JWKSets() map[string]*JWKS { sets := make(map[string]*JWKS, len(m.sets)) for u, jwks := range m.sets { @@ -54,6 +56,7 @@ func (m *MultipleJWKS) JWKSets() map[string]*JWKS { return sets } +// KeySelectorFirst returns the first key found in the multiple JWK Sets. func KeySelectorFirst(multiJWKS *MultipleJWKS, token *jwt.Token) (key interface{}, err error) { kid, alg, err := kidAlg(token) if err != nil { diff --git a/vendor/github.com/MicahParks/keyfunc/oct.go b/vendor/github.com/MicahParks/keyfunc/v2/oct.go similarity index 100% rename from vendor/github.com/MicahParks/keyfunc/oct.go rename to vendor/github.com/MicahParks/keyfunc/v2/oct.go diff --git a/vendor/github.com/MicahParks/keyfunc/options.go b/vendor/github.com/MicahParks/keyfunc/v2/options.go similarity index 90% rename from vendor/github.com/MicahParks/keyfunc/options.go rename to vendor/github.com/MicahParks/keyfunc/v2/options.go index cc4cf5efe..6291ffb8f 100644 --- a/vendor/github.com/MicahParks/keyfunc/options.go +++ b/vendor/github.com/MicahParks/keyfunc/v2/options.go @@ -9,7 +9,7 @@ import ( "net/http" "time" - "github.com/golang-jwt/jwt/v4" + "github.com/golang-jwt/jwt/v5" ) // ErrInvalidHTTPStatusCode indicates that the HTTP status code is invalid. @@ -17,7 +17,7 @@ var ErrInvalidHTTPStatusCode = errors.New("invalid HTTP status code") // Options represents the configuration options for a JWKS. // -// If RefreshInterval and or RefreshUnknownKID is not nil, then a background goroutine will be launched to refresh the +// If either RefreshInterval is non-zero or RefreshUnknownKID is true, then a background goroutine will be launched to refresh the // remote JWKS under the specified circumstances. // // When using a background refresh goroutine, make sure to use RefreshRateLimit if paired with RefreshUnknownKID. Also @@ -54,7 +54,7 @@ type Options struct { // if a background refresh goroutine is active. RefreshErrorHandler ErrorHandler - // RefreshInterval is the duration to refresh the JWKS in the background via a new HTTP request. If this is not nil, + // RefreshInterval is the duration to refresh the JWKS in the background via a new HTTP request. If this is not zero, // then a background goroutine will be used to refresh the JWKS once per the given interval. Make sure to call the // JWKS.EndBackground method to end this goroutine when it's no longer needed. RefreshInterval time.Duration @@ -84,6 +84,14 @@ type Options struct { // ResponseExtractor consumes a *http.Response and produces the raw JSON for the JWKS. By default, the // ResponseExtractorStatusOK function is used. The default behavior changed in v1.4.0. ResponseExtractor func(ctx context.Context, resp *http.Response) (json.RawMessage, error) + + // TolerateInitialJWKHTTPError will tolerate any error from the initial HTTP JWKS request. If an error occurs, + // the RefreshErrorHandler will be given the error. The program will continue to run as if the error did not occur + // and a valid JWK Set with no keys was received in the response. This allows for the background goroutine to + // request the JWKS at a later time. + // + // It does not make sense to mark this field as true unless the background refresh goroutine is active. + TolerateInitialJWKHTTPError bool } // MultipleOptions is used to configure the behavior when multiple JWKS are used by MultipleJWKS. diff --git a/vendor/github.com/MicahParks/keyfunc/rsa.go b/vendor/github.com/MicahParks/keyfunc/v2/rsa.go similarity index 100% rename from vendor/github.com/MicahParks/keyfunc/rsa.go rename to vendor/github.com/MicahParks/keyfunc/v2/rsa.go diff --git a/vendor/github.com/golang-jwt/jwt/v5/MIGRATION_GUIDE.md b/vendor/github.com/golang-jwt/jwt/v5/MIGRATION_GUIDE.md index 6ad1c22bb..ff9c57e1d 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/MIGRATION_GUIDE.md +++ b/vendor/github.com/golang-jwt/jwt/v5/MIGRATION_GUIDE.md @@ -17,7 +17,7 @@ and corresponding updates for existing programs. ## Parsing and Validation Options -Under the hood, a new `validator` struct takes care of validating the claims. A +Under the hood, a new `Validator` struct takes care of validating the claims. A long awaited feature has been the option to fine-tune the validation of tokens. This is now possible with several `ParserOption` functions that can be appended to most `Parse` functions, such as `ParseWithClaims`. The most important options @@ -68,6 +68,16 @@ type Claims interface { } ``` +Users that previously directly called the `Valid` function on their claims, +e.g., to perform validation independently of parsing/verifying a token, can now +use the `jwt.NewValidator` function to create a `Validator` independently of the +`Parser`. + +```go +var v = jwt.NewValidator(jwt.WithLeeway(5*time.Second)) +v.Validate(myClaims) +``` + ### Supported Claim Types and Removal of `StandardClaims` The two standard claim types supported by this library, `MapClaims` and @@ -169,7 +179,7 @@ be a drop-in replacement, if you're having troubles migrating, please open an issue. You can replace all occurrences of `github.com/dgrijalva/jwt-go` or -`github.com/golang-jwt/jwt` with `github.com/golang-jwt/jwt/v5`, either manually +`github.com/golang-jwt/jwt` with `github.com/golang-jwt/jwt/v4`, either manually or by using tools such as `sed` or `gofmt`. And then you'd typically run: diff --git a/vendor/github.com/golang-jwt/jwt/v5/ecdsa.go b/vendor/github.com/golang-jwt/jwt/v5/ecdsa.go index 4ccae2a85..c929e4a02 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/ecdsa.go +++ b/vendor/github.com/golang-jwt/jwt/v5/ecdsa.go @@ -62,7 +62,7 @@ func (m *SigningMethodECDSA) Verify(signingString string, sig []byte, key interf case *ecdsa.PublicKey: ecdsaKey = k default: - return ErrInvalidKeyType + return newError("ECDSA verify expects *ecdsa.PublicKey", ErrInvalidKeyType) } if len(sig) != 2*m.KeySize { @@ -96,7 +96,7 @@ func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) ([]byte case *ecdsa.PrivateKey: ecdsaKey = k default: - return nil, ErrInvalidKeyType + return nil, newError("ECDSA sign expects *ecdsa.PrivateKey", ErrInvalidKeyType) } // Create the hasher diff --git a/vendor/github.com/golang-jwt/jwt/v5/ed25519.go b/vendor/github.com/golang-jwt/jwt/v5/ed25519.go index 3db00e4a2..c2138119e 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/ed25519.go +++ b/vendor/github.com/golang-jwt/jwt/v5/ed25519.go @@ -1,11 +1,10 @@ package jwt import ( - "errors" - "crypto" "crypto/ed25519" "crypto/rand" + "errors" ) var ( @@ -39,7 +38,7 @@ func (m *SigningMethodEd25519) Verify(signingString string, sig []byte, key inte var ok bool if ed25519Key, ok = key.(ed25519.PublicKey); !ok { - return ErrInvalidKeyType + return newError("Ed25519 verify expects ed25519.PublicKey", ErrInvalidKeyType) } if len(ed25519Key) != ed25519.PublicKeySize { @@ -61,7 +60,7 @@ func (m *SigningMethodEd25519) Sign(signingString string, key interface{}) ([]by var ok bool if ed25519Key, ok = key.(crypto.Signer); !ok { - return nil, ErrInvalidKeyType + return nil, newError("Ed25519 sign expects crypto.Signer", ErrInvalidKeyType) } if _, ok := ed25519Key.Public().(ed25519.PublicKey); !ok { diff --git a/vendor/github.com/golang-jwt/jwt/v5/errors_go_other.go b/vendor/github.com/golang-jwt/jwt/v5/errors_go_other.go index 3afb04e64..2ad542f00 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/errors_go_other.go +++ b/vendor/github.com/golang-jwt/jwt/v5/errors_go_other.go @@ -22,7 +22,7 @@ func (je joinedError) Is(err error) bool { // wrappedErrors is a workaround for wrapping multiple errors in environments // where Go 1.20 is not available. It basically uses the already implemented -// functionatlity of joinedError to handle multiple errors with supplies a +// functionality of joinedError to handle multiple errors with supplies a // custom error message that is identical to the one we produce in Go 1.20 using // multiple %w directives. type wrappedErrors struct { diff --git a/vendor/github.com/golang-jwt/jwt/v5/hmac.go b/vendor/github.com/golang-jwt/jwt/v5/hmac.go index 91b688ba9..aca600ce1 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/hmac.go +++ b/vendor/github.com/golang-jwt/jwt/v5/hmac.go @@ -59,7 +59,7 @@ func (m *SigningMethodHMAC) Verify(signingString string, sig []byte, key interfa // Verify the key is the right type keyBytes, ok := key.([]byte) if !ok { - return ErrInvalidKeyType + return newError("HMAC verify expects []byte", ErrInvalidKeyType) } // Can we use the specified hashing method? @@ -100,5 +100,5 @@ func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) ([]byte, return hasher.Sum(nil), nil } - return nil, ErrInvalidKeyType + return nil, newError("HMAC sign expects []byte", ErrInvalidKeyType) } diff --git a/vendor/github.com/golang-jwt/jwt/v5/none.go b/vendor/github.com/golang-jwt/jwt/v5/none.go index c93daa584..685c2ea30 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/none.go +++ b/vendor/github.com/golang-jwt/jwt/v5/none.go @@ -32,7 +32,7 @@ func (m *signingMethodNone) Verify(signingString string, sig []byte, key interfa return NoneSignatureTypeDisallowedError } // If signing method is none, signature must be an empty string - if string(sig) != "" { + if len(sig) != 0 { return newError("'none' signing method with non-empty signature", ErrTokenUnverifiable) } diff --git a/vendor/github.com/golang-jwt/jwt/v5/parser.go b/vendor/github.com/golang-jwt/jwt/v5/parser.go index f4386fbaa..ecf99af78 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/parser.go +++ b/vendor/github.com/golang-jwt/jwt/v5/parser.go @@ -18,7 +18,7 @@ type Parser struct { // Skip claims validation during token parsing. skipClaimsValidation bool - validator *validator + validator *Validator decodeStrict bool @@ -28,7 +28,7 @@ type Parser struct { // NewParser creates a new Parser with the specified options func NewParser(options ...ParserOption) *Parser { p := &Parser{ - validator: &validator{}, + validator: &Validator{}, } // Loop through our parsing options and apply them @@ -74,24 +74,40 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf } } - // Lookup key - var key interface{} - if keyFunc == nil { - // keyFunc was not provided. short circuiting validation - return token, newError("no keyfunc was provided", ErrTokenUnverifiable) - } - if key, err = keyFunc(token); err != nil { - return token, newError("error while executing keyfunc", ErrTokenUnverifiable, err) - } - // Decode signature token.Signature, err = p.DecodeSegment(parts[2]) if err != nil { return token, newError("could not base64 decode signature", ErrTokenMalformed, err) } + text := strings.Join(parts[0:2], ".") - // Perform signature validation - if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil { + // Lookup key(s) + if keyFunc == nil { + // keyFunc was not provided. short circuiting validation + return token, newError("no keyfunc was provided", ErrTokenUnverifiable) + } + + got, err := keyFunc(token) + if err != nil { + return token, newError("error while executing keyfunc", ErrTokenUnverifiable, err) + } + + switch have := got.(type) { + case VerificationKeySet: + if len(have.Keys) == 0 { + return token, newError("keyfunc returned empty verification key set", ErrTokenUnverifiable) + } + // Iterate through keys and verify signature, skipping the rest when a match is found. + // Return the last error if no match is found. + for _, key := range have.Keys { + if err = token.Method.Verify(text, token.Signature, key); err == nil { + break + } + } + default: + err = token.Method.Verify(text, token.Signature, have) + } + if err != nil { return token, newError("", ErrTokenSignatureInvalid, err) } @@ -99,7 +115,7 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf if !p.skipClaimsValidation { // Make sure we have at least a default validator if p.validator == nil { - p.validator = newValidator() + p.validator = NewValidator() } if err := p.validator.Validate(claims); err != nil { @@ -117,8 +133,8 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf // // WARNING: Don't use this method unless you know what you're doing. // -// It's only ever useful in cases where you know the signature is valid (because it has -// been checked previously in the stack) and you want to extract values from it. +// It's only ever useful in cases where you know the signature is valid (since it has already +// been or will be checked elsewhere in the stack) and you want to extract values from it. func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) { parts = strings.Split(tokenString, ".") if len(parts) != 3 { @@ -130,9 +146,6 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke // parse Header var headerBytes []byte if headerBytes, err = p.DecodeSegment(parts[0]); err != nil { - if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") { - return token, parts, newError("tokenstring should not contain 'bearer '", ErrTokenMalformed) - } return token, parts, newError("could not base64 decode header", ErrTokenMalformed, err) } if err = json.Unmarshal(headerBytes, &token.Header); err != nil { @@ -140,23 +153,33 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke } // parse Claims - var claimBytes []byte token.Claims = claims - if claimBytes, err = p.DecodeSegment(parts[1]); err != nil { + claimBytes, err := p.DecodeSegment(parts[1]) + if err != nil { return token, parts, newError("could not base64 decode claim", ErrTokenMalformed, err) } - dec := json.NewDecoder(bytes.NewBuffer(claimBytes)) - if p.useJSONNumber { - dec.UseNumber() - } - // JSON Decode. Special case for map type to avoid weird pointer behavior - if c, ok := token.Claims.(MapClaims); ok { - err = dec.Decode(&c) + + // If `useJSONNumber` is enabled then we must use *json.Decoder to decode + // the claims. However, this comes with a performance penalty so only use + // it if we must and, otherwise, simple use json.Unmarshal. + if !p.useJSONNumber { + // JSON Unmarshal. Special case for map type to avoid weird pointer behavior. + if c, ok := token.Claims.(MapClaims); ok { + err = json.Unmarshal(claimBytes, &c) + } else { + err = json.Unmarshal(claimBytes, &claims) + } } else { - err = dec.Decode(&claims) + dec := json.NewDecoder(bytes.NewBuffer(claimBytes)) + dec.UseNumber() + // JSON Decode. Special case for map type to avoid weird pointer behavior. + if c, ok := token.Claims.(MapClaims); ok { + err = dec.Decode(&c) + } else { + err = dec.Decode(&claims) + } } - // Handle decode error if err != nil { return token, parts, newError("could not JSON decode claim", ErrTokenMalformed, err) } diff --git a/vendor/github.com/golang-jwt/jwt/v5/parser_option.go b/vendor/github.com/golang-jwt/jwt/v5/parser_option.go index 1b5af970f..88a780fbd 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/parser_option.go +++ b/vendor/github.com/golang-jwt/jwt/v5/parser_option.go @@ -58,6 +58,14 @@ func WithIssuedAt() ParserOption { } } +// WithExpirationRequired returns the ParserOption to make exp claim required. +// By default exp claim is optional. +func WithExpirationRequired() ParserOption { + return func(p *Parser) { + p.validator.requireExp = true + } +} + // WithAudience configures the validator to require the specified audience in // the `aud` claim. Validation will fail if the audience is not listed in the // token or the `aud` claim is missing. diff --git a/vendor/github.com/golang-jwt/jwt/v5/rsa.go b/vendor/github.com/golang-jwt/jwt/v5/rsa.go index daff09431..83cbee6ae 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/rsa.go +++ b/vendor/github.com/golang-jwt/jwt/v5/rsa.go @@ -51,7 +51,7 @@ func (m *SigningMethodRSA) Verify(signingString string, sig []byte, key interfac var ok bool if rsaKey, ok = key.(*rsa.PublicKey); !ok { - return ErrInvalidKeyType + return newError("RSA verify expects *rsa.PublicKey", ErrInvalidKeyType) } // Create hasher @@ -73,7 +73,7 @@ func (m *SigningMethodRSA) Sign(signingString string, key interface{}) ([]byte, // Validate type of key if rsaKey, ok = key.(*rsa.PrivateKey); !ok { - return nil, ErrInvalidKey + return nil, newError("RSA sign expects *rsa.PrivateKey", ErrInvalidKeyType) } // Create the hasher diff --git a/vendor/github.com/golang-jwt/jwt/v5/rsa_pss.go b/vendor/github.com/golang-jwt/jwt/v5/rsa_pss.go index 9599f0a46..28c386ec4 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/rsa_pss.go +++ b/vendor/github.com/golang-jwt/jwt/v5/rsa_pss.go @@ -88,7 +88,7 @@ func (m *SigningMethodRSAPSS) Verify(signingString string, sig []byte, key inter case *rsa.PublicKey: rsaKey = k default: - return ErrInvalidKey + return newError("RSA-PSS verify expects *rsa.PublicKey", ErrInvalidKeyType) } // Create hasher @@ -115,7 +115,7 @@ func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) ([]byt case *rsa.PrivateKey: rsaKey = k default: - return nil, ErrInvalidKeyType + return nil, newError("RSA-PSS sign expects *rsa.PrivateKey", ErrInvalidKeyType) } // Create the hasher diff --git a/vendor/github.com/golang-jwt/jwt/v5/token.go b/vendor/github.com/golang-jwt/jwt/v5/token.go index c8ad7c783..352873a2d 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/token.go +++ b/vendor/github.com/golang-jwt/jwt/v5/token.go @@ -1,6 +1,7 @@ package jwt import ( + "crypto" "encoding/base64" "encoding/json" ) @@ -9,8 +10,21 @@ import ( // the key for verification. The function receives the parsed, but unverified // Token. This allows you to use properties in the Header of the token (such as // `kid`) to identify which key to use. +// +// The returned interface{} may be a single key or a VerificationKeySet containing +// multiple keys. type Keyfunc func(*Token) (interface{}, error) +// VerificationKey represents a public or secret key for verifying a token's signature. +type VerificationKey interface { + crypto.PublicKey | []uint8 +} + +// VerificationKeySet is a set of public or secret keys. It is used by the parser to verify a token. +type VerificationKeySet struct { + Keys []VerificationKey +} + // Token represents a JWT Token. Different fields will be used depending on // whether you're creating or parsing/verifying a token. type Token struct { diff --git a/vendor/github.com/golang-jwt/jwt/v5/types.go b/vendor/github.com/golang-jwt/jwt/v5/types.go index b82b38867..b2655a9e6 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/types.go +++ b/vendor/github.com/golang-jwt/jwt/v5/types.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "math" - "reflect" "strconv" "time" ) @@ -121,14 +120,14 @@ func (s *ClaimStrings) UnmarshalJSON(data []byte) (err error) { for _, vv := range v { vs, ok := vv.(string) if !ok { - return &json.UnsupportedTypeError{Type: reflect.TypeOf(vv)} + return ErrInvalidType } aud = append(aud, vs) } case nil: return nil default: - return &json.UnsupportedTypeError{Type: reflect.TypeOf(v)} + return ErrInvalidType } *s = aud diff --git a/vendor/github.com/golang-jwt/jwt/v5/validator.go b/vendor/github.com/golang-jwt/jwt/v5/validator.go index 385043893..008ecd871 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/validator.go +++ b/vendor/github.com/golang-jwt/jwt/v5/validator.go @@ -28,13 +28,12 @@ type ClaimsValidator interface { Validate() error } -// validator is the core of the new Validation API. It is automatically used by +// Validator is the core of the new Validation API. It is automatically used by // a [Parser] during parsing and can be modified with various parser options. // -// Note: This struct is intentionally not exported (yet) as we want to -// internally finalize its API. In the future, we might make it publicly -// available. -type validator struct { +// The [NewValidator] function should be used to create an instance of this +// struct. +type Validator struct { // leeway is an optional leeway that can be provided to account for clock skew. leeway time.Duration @@ -42,6 +41,9 @@ type validator struct { // validation. If unspecified, this defaults to time.Now. timeFunc func() time.Time + // requireExp specifies whether the exp claim is required + requireExp bool + // verifyIat specifies whether the iat (Issued At) claim will be verified. // According to https://www.rfc-editor.org/rfc/rfc7519#section-4.1.6 this // only specifies the age of the token, but no validation check is @@ -62,16 +64,28 @@ type validator struct { expectedSub string } -// newValidator can be used to create a stand-alone validator with the supplied +// NewValidator can be used to create a stand-alone validator with the supplied // options. This validator can then be used to validate already parsed claims. -func newValidator(opts ...ParserOption) *validator { +// +// Note: Under normal circumstances, explicitly creating a validator is not +// needed and can potentially be dangerous; instead functions of the [Parser] +// class should be used. +// +// The [Validator] is only checking the *validity* of the claims, such as its +// expiration time, but it does NOT perform *signature verification* of the +// token. +func NewValidator(opts ...ParserOption) *Validator { p := NewParser(opts...) return p.validator } // Validate validates the given claims. It will also perform any custom // validation if claims implements the [ClaimsValidator] interface. -func (v *validator) Validate(claims Claims) error { +// +// Note: It will NOT perform any *signature verification* on the token that +// contains the claims and expects that the [Claim] was already successfully +// verified. +func (v *Validator) Validate(claims Claims) error { var ( now time.Time errs []error = make([]error, 0, 6) @@ -86,8 +100,9 @@ func (v *validator) Validate(claims Claims) error { } // We always need to check the expiration time, but usage of the claim - // itself is OPTIONAL. - if err = v.verifyExpiresAt(claims, now, false); err != nil { + // itself is OPTIONAL by default. requireExp overrides this behavior + // and makes the exp claim mandatory. + if err = v.verifyExpiresAt(claims, now, v.requireExp); err != nil { errs = append(errs, err) } @@ -149,7 +164,7 @@ func (v *validator) Validate(claims Claims) error { // // Additionally, if any error occurs while retrieving the claim, e.g., when its // the wrong type, an ErrTokenUnverifiable error will be returned. -func (v *validator) verifyExpiresAt(claims Claims, cmp time.Time, required bool) error { +func (v *Validator) verifyExpiresAt(claims Claims, cmp time.Time, required bool) error { exp, err := claims.GetExpirationTime() if err != nil { return err @@ -170,7 +185,7 @@ func (v *validator) verifyExpiresAt(claims Claims, cmp time.Time, required bool) // // Additionally, if any error occurs while retrieving the claim, e.g., when its // the wrong type, an ErrTokenUnverifiable error will be returned. -func (v *validator) verifyIssuedAt(claims Claims, cmp time.Time, required bool) error { +func (v *Validator) verifyIssuedAt(claims Claims, cmp time.Time, required bool) error { iat, err := claims.GetIssuedAt() if err != nil { return err @@ -191,7 +206,7 @@ func (v *validator) verifyIssuedAt(claims Claims, cmp time.Time, required bool) // // Additionally, if any error occurs while retrieving the claim, e.g., when its // the wrong type, an ErrTokenUnverifiable error will be returned. -func (v *validator) verifyNotBefore(claims Claims, cmp time.Time, required bool) error { +func (v *Validator) verifyNotBefore(claims Claims, cmp time.Time, required bool) error { nbf, err := claims.GetNotBefore() if err != nil { return err @@ -211,7 +226,7 @@ func (v *validator) verifyNotBefore(claims Claims, cmp time.Time, required bool) // // Additionally, if any error occurs while retrieving the claim, e.g., when its // the wrong type, an ErrTokenUnverifiable error will be returned. -func (v *validator) verifyAudience(claims Claims, cmp string, required bool) error { +func (v *Validator) verifyAudience(claims Claims, cmp string, required bool) error { aud, err := claims.GetAudience() if err != nil { return err @@ -247,7 +262,7 @@ func (v *validator) verifyAudience(claims Claims, cmp string, required bool) err // // Additionally, if any error occurs while retrieving the claim, e.g., when its // the wrong type, an ErrTokenUnverifiable error will be returned. -func (v *validator) verifyIssuer(claims Claims, cmp string, required bool) error { +func (v *Validator) verifyIssuer(claims Claims, cmp string, required bool) error { iss, err := claims.GetIssuer() if err != nil { return err @@ -267,7 +282,7 @@ func (v *validator) verifyIssuer(claims Claims, cmp string, required bool) error // // Additionally, if any error occurs while retrieving the claim, e.g., when its // the wrong type, an ErrTokenUnverifiable error will be returned. -func (v *validator) verifySubject(claims Claims, cmp string, required bool) error { +func (v *Validator) verifySubject(claims Claims, cmp string, required bool) error { sub, err := claims.GetSubject() if err != nil { return err diff --git a/vendor/modules.txt b/vendor/modules.txt index f99100307..576d75cd2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -27,9 +27,9 @@ github.com/Masterminds/semver # github.com/Masterminds/sprig v2.22.0+incompatible ## explicit github.com/Masterminds/sprig -# github.com/MicahParks/keyfunc v1.9.0 -## explicit; go 1.16 -github.com/MicahParks/keyfunc +# github.com/MicahParks/keyfunc/v2 v2.1.0 +## explicit; go 1.18 +github.com/MicahParks/keyfunc/v2 # github.com/Microsoft/go-winio v0.6.2 ## explicit; go 1.21 github.com/Microsoft/go-winio @@ -1069,7 +1069,7 @@ github.com/golang-jwt/jwt # github.com/golang-jwt/jwt/v4 v4.5.0 ## explicit; go 1.16 github.com/golang-jwt/jwt/v4 -# github.com/golang-jwt/jwt/v5 v5.0.0 +# github.com/golang-jwt/jwt/v5 v5.2.1 ## explicit; go 1.18 github.com/golang-jwt/jwt/v5 # github.com/golang/geo v0.0.0-20210211234256-740aa86cb551