mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2025-12-31 01:10:20 -06:00
Bump github.com/MicahParks/keyfunc from 1.5.1 to 1.9.0
Bumps [github.com/MicahParks/keyfunc](https://github.com/MicahParks/keyfunc) from 1.5.1 to 1.9.0. - [Release notes](https://github.com/MicahParks/keyfunc/releases) - [Commits](https://github.com/MicahParks/keyfunc/compare/v1.5.1...v1.9.0) --- updated-dependencies: - dependency-name: github.com/MicahParks/keyfunc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
committed by
Ralf Haferkamp
parent
3f40a42bb2
commit
71e548189d
2
go.mod
2
go.mod
@@ -6,7 +6,7 @@ require (
|
||||
github.com/CiscoM31/godata v1.0.8
|
||||
github.com/KimMachineGun/automemlimit v0.3.0
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/MicahParks/keyfunc v1.5.1
|
||||
github.com/MicahParks/keyfunc v1.9.0
|
||||
github.com/Nerzal/gocloak/v13 v13.8.0
|
||||
github.com/bbalet/stopwords v1.0.0
|
||||
github.com/blevesearch/bleve/v2 v2.3.9
|
||||
|
||||
4
go.sum
4
go.sum
@@ -651,8 +651,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.5.1 h1:RlyyYgKQI/adkIw1yXYtPvTAOb7hBhSX42aH23d8N0Q=
|
||||
github.com/MicahParks/keyfunc v1.5.1/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw=
|
||||
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/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
|
||||
18
vendor/github.com/MicahParks/keyfunc/README.md
generated
vendored
18
vendor/github.com/MicahParks/keyfunc/README.md
generated
vendored
@@ -70,7 +70,7 @@ jwksURL := os.Getenv("JWKS_URL")
|
||||
|
||||
// Confirm the environment variable is not empty.
|
||||
if jwksURL == "" {
|
||||
log.Fatalln("JWKS_URL environment variable must be populated.")
|
||||
log.Fatalln("JWKS_URL environment variable must be populated.")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -81,7 +81,7 @@ Via HTTP:
|
||||
// Create the JWKS from the resource at the given URL.
|
||||
jwks, err := keyfunc.Get(jwksURL, keyfunc.Options{}) // See recommended options in the examples directory.
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get the JWKS from the given URL.\nError: %s", err)
|
||||
log.Fatalf("Failed to get the JWKS from the given URL.\nError: %s", err)
|
||||
}
|
||||
```
|
||||
Via JSON:
|
||||
@@ -92,7 +92,7 @@ var jwksJSON = json.RawMessage(`{"keys":[{"kid":"zXew0UJ1h6Q4CCcd_9wxMzvcp5cEBif
|
||||
// Create the JWKS from the resource at the given URL.
|
||||
jwks, err := keyfunc.NewJSON(jwksJSON)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create JWKS from JSON.\nError: %s", err)
|
||||
log.Fatalf("Failed to create JWKS from JSON.\nError: %s", err)
|
||||
}
|
||||
```
|
||||
Via a given key:
|
||||
@@ -103,7 +103,7 @@ uniqueKeyID := "myKeyID"
|
||||
|
||||
// Create the JWKS from the HMAC key.
|
||||
jwks := keyfunc.NewGiven(map[string]keyfunc.GivenKey{
|
||||
uniqueKeyID: keyfunc.NewGivenHMAC(key),
|
||||
uniqueKeyID: keyfunc.NewGivenHMAC(key),
|
||||
})
|
||||
```
|
||||
|
||||
@@ -117,7 +117,7 @@ features mentioned at the bottom of this `README.md`.
|
||||
// Parse the JWT.
|
||||
token, err := jwt.Parse(jwtB64, jwks.Keyfunc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse token: %w", err)
|
||||
return nil, fmt.Errorf("failed to parse token: %w", err)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -170,6 +170,9 @@ These features can be configured by populating fields in the
|
||||
* Custom cryptographic algorithms can be used. Make sure to
|
||||
use [`jwt.RegisterSigningMethod`](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#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
|
||||
@@ -180,6 +183,11 @@ base64url the same as RFC 7515 Section 2:
|
||||
However, this package will remove trailing padding on base64url encoded keys to account for improper implementations of
|
||||
JWKS.
|
||||
|
||||
This package will check the `alg` in each JWK. If present, it will confirm the same `alg` is in a given JWT's header
|
||||
before returning the key for signature verification. If the `alg`s do not match, `keyfunc.ErrJWKAlgMismatch` will
|
||||
prevent the key being used for signature verification. If the `alg` is not present in the JWK, this check will not
|
||||
occur.
|
||||
|
||||
## References
|
||||
This project was built and tested using various RFCs and services. The services are listed below:
|
||||
* [Keycloak](https://www.keycloak.org/)
|
||||
|
||||
8
vendor/github.com/MicahParks/keyfunc/ecdsa.go
generated
vendored
8
vendor/github.com/MicahParks/keyfunc/ecdsa.go
generated
vendored
@@ -3,6 +3,7 @@ package keyfunc
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
@@ -21,6 +22,11 @@ const (
|
||||
p521 = "P-521"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrECDSACurve indicates an error with the ECDSA curve.
|
||||
ErrECDSACurve = errors.New("invalid ECDSA curve")
|
||||
)
|
||||
|
||||
// ECDSA parses a jsonWebKey and turns it into an ECDSA public key.
|
||||
func (j *jsonWebKey) ECDSA() (publicKey *ecdsa.PublicKey, err error) {
|
||||
if j.X == "" || j.Y == "" || j.Curve == "" {
|
||||
@@ -48,6 +54,8 @@ func (j *jsonWebKey) ECDSA() (publicKey *ecdsa.PublicKey, err error) {
|
||||
publicKey.Curve = elliptic.P384()
|
||||
case p521:
|
||||
publicKey.Curve = elliptic.P521()
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: unknown curve: %s", ErrECDSACurve, j.Curve)
|
||||
}
|
||||
|
||||
// Turn the X coordinate into *big.Int.
|
||||
|
||||
86
vendor/github.com/MicahParks/keyfunc/get.go
generated
vendored
86
vendor/github.com/MicahParks/keyfunc/get.go
generated
vendored
@@ -3,6 +3,7 @@ package keyfunc
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
@@ -10,6 +11,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrRefreshImpossible is returned when a refresh is attempted on a JWKS that was not created from a remote
|
||||
// resource.
|
||||
ErrRefreshImpossible = errors.New("refresh impossible: JWKS was not created from a remote resource")
|
||||
|
||||
// defaultRefreshTimeout is the default duration for the context used to create the HTTP request for a refresh of
|
||||
// the JWKS.
|
||||
defaultRefreshTimeout = time.Minute
|
||||
@@ -49,13 +54,53 @@ func Get(jwksURL string, options Options) (jwks *JWKS, err error) {
|
||||
|
||||
if jwks.refreshInterval != 0 || jwks.refreshUnknownKID {
|
||||
jwks.ctx, jwks.cancel = context.WithCancel(context.Background())
|
||||
jwks.refreshRequests = make(chan context.CancelFunc, 1)
|
||||
jwks.refreshRequests = make(chan refreshRequest, 1)
|
||||
go jwks.backgroundRefresh()
|
||||
}
|
||||
|
||||
return jwks, nil
|
||||
}
|
||||
|
||||
// Refresh manually refreshes the JWKS with the remote resource. It can bypass the rate limit if configured to do so.
|
||||
// This function will return an ErrRefreshImpossible if the JWKS was created from a static source like given keys or raw
|
||||
// JSON, because there is no remote resource to refresh from.
|
||||
//
|
||||
// This function will block until the refresh is finished or an error occurs.
|
||||
func (j *JWKS) Refresh(ctx context.Context, options RefreshOptions) error {
|
||||
if j.jwksURL == "" {
|
||||
return ErrRefreshImpossible
|
||||
}
|
||||
|
||||
// Check if the background goroutine was launched.
|
||||
if j.refreshInterval != 0 || j.refreshUnknownKID {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
req := refreshRequest{
|
||||
cancel: cancel,
|
||||
ignoreRateLimit: options.IgnoreRateLimit,
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("failed to send request refresh to background goroutine: %w", j.ctx.Err())
|
||||
case j.refreshRequests <- req:
|
||||
}
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
if !errors.Is(ctx.Err(), context.Canceled) {
|
||||
return fmt.Errorf("unexpected keyfunc background refresh context error: %w", ctx.Err())
|
||||
}
|
||||
} else {
|
||||
err := j.refresh()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to refresh JWKS: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// backgroundRefresh is meant to be a separate goroutine that will update the keys in a JWKS over a given interval of
|
||||
// time.
|
||||
func (j *JWKS) backgroundRefresh() {
|
||||
@@ -69,6 +114,14 @@ func (j *JWKS) backgroundRefresh() {
|
||||
// Create a channel that will never send anything unless there is a refresh interval.
|
||||
refreshInterval := make(<-chan time.Time)
|
||||
|
||||
refresh := func() {
|
||||
err := j.refresh()
|
||||
if err != nil && j.refreshErrorHandler != nil {
|
||||
j.refreshErrorHandler(err)
|
||||
}
|
||||
lastRefresh = time.Now()
|
||||
}
|
||||
|
||||
// Enter an infinite loop that ends when the background ends.
|
||||
for {
|
||||
if j.refreshInterval != 0 {
|
||||
@@ -80,16 +133,15 @@ func (j *JWKS) backgroundRefresh() {
|
||||
select {
|
||||
case <-j.ctx.Done():
|
||||
return
|
||||
case j.refreshRequests <- func() {}:
|
||||
case j.refreshRequests <- refreshRequest{}:
|
||||
default: // If the j.refreshRequests channel is full, don't send another request.
|
||||
}
|
||||
|
||||
case cancel := <-j.refreshRequests:
|
||||
case req := <-j.refreshRequests:
|
||||
refreshMux.Lock()
|
||||
if j.refreshRateLimit != 0 && lastRefresh.Add(j.refreshRateLimit).After(time.Now()) {
|
||||
// Don't make the JWT parsing goroutine wait for the JWKS to refresh.
|
||||
cancel()
|
||||
|
||||
if req.ignoreRateLimit {
|
||||
refresh()
|
||||
} else if j.refreshRateLimit != 0 && lastRefresh.Add(j.refreshRateLimit).After(time.Now()) {
|
||||
// Launch a goroutine that will get a reservation for a JWKS refresh or fail to and immediately return.
|
||||
queueOnce.Do(func() {
|
||||
go func() {
|
||||
@@ -104,25 +156,15 @@ func (j *JWKS) backgroundRefresh() {
|
||||
|
||||
refreshMux.Lock()
|
||||
defer refreshMux.Unlock()
|
||||
err := j.refresh()
|
||||
if err != nil && j.refreshErrorHandler != nil {
|
||||
j.refreshErrorHandler(err)
|
||||
}
|
||||
|
||||
lastRefresh = time.Now()
|
||||
refresh()
|
||||
queueOnce = sync.Once{}
|
||||
}()
|
||||
})
|
||||
} else {
|
||||
err := j.refresh()
|
||||
if err != nil && j.refreshErrorHandler != nil {
|
||||
j.refreshErrorHandler(err)
|
||||
}
|
||||
|
||||
lastRefresh = time.Now()
|
||||
|
||||
// Allow the JWT parsing goroutine to continue with the refreshed JWKS.
|
||||
cancel()
|
||||
refresh()
|
||||
}
|
||||
if req.cancel != nil {
|
||||
req.cancel()
|
||||
}
|
||||
refreshMux.Unlock()
|
||||
|
||||
|
||||
109
vendor/github.com/MicahParks/keyfunc/given.go
generated
vendored
109
vendor/github.com/MicahParks/keyfunc/given.go
generated
vendored
@@ -4,11 +4,26 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// GivenKey represents a cryptographic key that resides in a JWKS. In conjuncture with Options.
|
||||
type GivenKey struct {
|
||||
inter interface{}
|
||||
algorithm string
|
||||
inter interface{}
|
||||
}
|
||||
|
||||
// GivenKeyOptions represents the configuration options for a GivenKey.
|
||||
type GivenKeyOptions struct {
|
||||
// Algorithm is the given key's signing algorithm. Its value will be compared to unverified tokens' "alg" header.
|
||||
//
|
||||
// See RFC 8725 Section 3.1 for details.
|
||||
// https://www.rfc-editor.org/rfc/rfc8725#section-3.1
|
||||
//
|
||||
// For a list of possible values, please see:
|
||||
// https://www.rfc-editor.org/rfc/rfc7518#section-3.1
|
||||
// https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms
|
||||
Algorithm string
|
||||
}
|
||||
|
||||
// NewGiven creates a JWKS from a map of given keys.
|
||||
@@ -16,7 +31,10 @@ func NewGiven(givenKeys map[string]GivenKey) (jwks *JWKS) {
|
||||
keys := make(map[string]parsedJWK)
|
||||
|
||||
for kid, given := range givenKeys {
|
||||
keys[kid] = parsedJWK{public: given.inter}
|
||||
keys[kid] = parsedJWK{
|
||||
algorithm: given.algorithm,
|
||||
public: given.inter,
|
||||
}
|
||||
}
|
||||
|
||||
return &JWKS{
|
||||
@@ -29,36 +47,123 @@ func NewGiven(givenKeys map[string]GivenKey) (jwks *JWKS) {
|
||||
//
|
||||
// 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
|
||||
// signing method.
|
||||
func NewGivenCustomWithOptions(key interface{}, options GivenKeyOptions) (givenKey GivenKey) {
|
||||
return GivenKey{
|
||||
algorithm: options.Algorithm,
|
||||
inter: key,
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return GivenKey{
|
||||
algorithm: options.Algorithm,
|
||||
inter: key,
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return GivenKey{
|
||||
algorithm: options.Algorithm,
|
||||
inter: key,
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return GivenKey{
|
||||
algorithm: options.Algorithm,
|
||||
inter: key,
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return GivenKey{
|
||||
algorithm: options.Algorithm,
|
||||
inter: key,
|
||||
}
|
||||
}
|
||||
|
||||
// NewGivenKeysFromJSON parses a raw JSON message into a map of key IDs (`kid`) to GivenKeys. The returned map is
|
||||
// suitable for passing to `NewGiven()` or as `Options.GivenKeys` to `Get()`
|
||||
func NewGivenKeysFromJSON(jwksBytes json.RawMessage) (map[string]GivenKey, error) {
|
||||
// Parse by making a temporary JWKS instance. No need to lock its map since it doesn't escape this function.
|
||||
j, err := NewJSON(jwksBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keys := make(map[string]GivenKey, len(j.keys))
|
||||
for kid, cryptoKey := range j.keys {
|
||||
keys[kid] = GivenKey{
|
||||
algorithm: cryptoKey.algorithm,
|
||||
inter: cryptoKey.public,
|
||||
}
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
46
vendor/github.com/MicahParks/keyfunc/jwks.go
generated
vendored
46
vendor/github.com/MicahParks/keyfunc/jwks.go
generated
vendored
@@ -11,6 +11,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrJWKAlgMismatch indicates that the given JWK was found, but its "alg" parameter's value did not match that of
|
||||
// the JWT.
|
||||
ErrJWKAlgMismatch = errors.New(`the given JWK was found, but its "alg" parameter's value did not match the expected algorithm`)
|
||||
|
||||
// ErrJWKUseWhitelist indicates that the given JWK was found, but its "use" parameter's value was not whitelisted.
|
||||
ErrJWKUseWhitelist = errors.New(`the given JWK was found, but its "use" parameter's value was not whitelisted`)
|
||||
|
||||
@@ -39,21 +43,23 @@ type JWKUse string
|
||||
|
||||
// jsonWebKey represents a JSON Web Key inside a JWKS.
|
||||
type jsonWebKey struct {
|
||||
Curve string `json:"crv"`
|
||||
Exponent string `json:"e"`
|
||||
K string `json:"k"`
|
||||
ID string `json:"kid"`
|
||||
Modulus string `json:"n"`
|
||||
Type string `json:"kty"`
|
||||
Use string `json:"use"`
|
||||
X string `json:"x"`
|
||||
Y string `json:"y"`
|
||||
Algorithm string `json:"alg"`
|
||||
Curve string `json:"crv"`
|
||||
Exponent string `json:"e"`
|
||||
K string `json:"k"`
|
||||
ID string `json:"kid"`
|
||||
Modulus string `json:"n"`
|
||||
Type string `json:"kty"`
|
||||
Use string `json:"use"`
|
||||
X string `json:"x"`
|
||||
Y string `json:"y"`
|
||||
}
|
||||
|
||||
// parsedJWK represents a JSON Web Key parsed with fields as the correct Go types.
|
||||
type parsedJWK struct {
|
||||
use JWKUse
|
||||
public interface{}
|
||||
algorithm string
|
||||
public interface{}
|
||||
use JWKUse
|
||||
}
|
||||
|
||||
// JWKS represents a JSON Web Key Set (JWK Set).
|
||||
@@ -71,7 +77,7 @@ type JWKS struct {
|
||||
refreshErrorHandler ErrorHandler
|
||||
refreshInterval time.Duration
|
||||
refreshRateLimit time.Duration
|
||||
refreshRequests chan context.CancelFunc
|
||||
refreshRequests chan refreshRequest
|
||||
refreshTimeout time.Duration
|
||||
refreshUnknownKID bool
|
||||
requestFactory func(ctx context.Context, url string) (*http.Request, error)
|
||||
@@ -124,8 +130,9 @@ func NewJSON(jwksBytes json.RawMessage) (jwks *JWKS, err error) {
|
||||
}
|
||||
|
||||
jwks.keys[key.ID] = parsedJWK{
|
||||
use: JWKUse(key.Use),
|
||||
public: keyInter,
|
||||
algorithm: key.Algorithm,
|
||||
use: JWKUse(key.Use),
|
||||
public: keyInter,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +188,7 @@ func (j *JWKS) ReadOnlyKeys() map[string]interface{} {
|
||||
}
|
||||
|
||||
// getKey gets the jsonWebKey from the given KID from the JWKS. It may refresh the JWKS if configured to.
|
||||
func (j *JWKS) getKey(kid string) (jsonKey interface{}, err error) {
|
||||
func (j *JWKS) getKey(alg, kid string) (jsonKey interface{}, err error) {
|
||||
j.mux.RLock()
|
||||
pubKey, ok := j.keys[kid]
|
||||
j.mux.RUnlock()
|
||||
@@ -192,12 +199,15 @@ func (j *JWKS) getKey(kid string) (jsonKey interface{}, err error) {
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(j.ctx)
|
||||
req := refreshRequest{
|
||||
cancel: cancel,
|
||||
}
|
||||
|
||||
// Refresh the JWKS.
|
||||
select {
|
||||
case <-j.ctx.Done():
|
||||
return
|
||||
case j.refreshRequests <- cancel:
|
||||
case j.refreshRequests <- req:
|
||||
default:
|
||||
// If the j.refreshRequests channel is full, return the error early.
|
||||
return nil, ErrKIDNotFound
|
||||
@@ -221,5 +231,9 @@ func (j *JWKS) getKey(kid string) (jsonKey interface{}, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
if pubKey.algorithm != "" && pubKey.algorithm != alg {
|
||||
return nil, fmt.Errorf(`%w: JWK "alg" parameter value %q does not match token "alg" parameter value %q`, ErrJWKAlgMismatch, pubKey.algorithm, alg)
|
||||
}
|
||||
|
||||
return pubKey.public, nil
|
||||
}
|
||||
|
||||
27
vendor/github.com/MicahParks/keyfunc/keyfunc.go
generated
vendored
27
vendor/github.com/MicahParks/keyfunc/keyfunc.go
generated
vendored
@@ -16,16 +16,33 @@ var (
|
||||
|
||||
// Keyfunc matches the signature of github.com/golang-jwt/jwt/v4's jwt.Keyfunc function.
|
||||
func (j *JWKS) Keyfunc(token *jwt.Token) (interface{}, error) {
|
||||
kid, alg, err := kidAlg(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return j.getKey(alg, kid)
|
||||
}
|
||||
|
||||
func (m *MultipleJWKS) Keyfunc(token *jwt.Token) (interface{}, error) {
|
||||
return m.keySelector(m, token)
|
||||
}
|
||||
|
||||
func kidAlg(token *jwt.Token) (kid, alg string, err error) {
|
||||
kidInter, ok := token.Header["kid"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: could not find kid in JWT header", ErrKID)
|
||||
return "", "", fmt.Errorf("%w: could not find kid in JWT header", ErrKID)
|
||||
}
|
||||
kid, ok := kidInter.(string)
|
||||
kid, ok = kidInter.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: could not convert kid in JWT header to string", ErrKID)
|
||||
return "", "", fmt.Errorf("%w: could not convert kid in JWT header to string", ErrKID)
|
||||
}
|
||||
|
||||
return j.getKey(kid)
|
||||
alg, ok = token.Header["alg"].(string)
|
||||
if !ok {
|
||||
// For test coverage purposes, this should be impossible to reach because the JWT package rejects a token
|
||||
// without an alg parameter in the header before calling jwt.Keyfunc.
|
||||
return "", "", fmt.Errorf(`%w: the JWT header did not contain the "alg" parameter, which is required by RFC 7515 section 4.1.1`, ErrJWKAlgMismatch)
|
||||
}
|
||||
return kid, alg, nil
|
||||
}
|
||||
|
||||
// base64urlTrailingPadding removes trailing padding before decoding a string from base64url. Some non-RFC compliant
|
||||
|
||||
69
vendor/github.com/MicahParks/keyfunc/multiple.go
generated
vendored
Normal file
69
vendor/github.com/MicahParks/keyfunc/multiple.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
package keyfunc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
|
||||
// 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")
|
||||
|
||||
// MultipleJWKS manages multiple JWKS and has a field for jwt.Keyfunc.
|
||||
type MultipleJWKS struct {
|
||||
keySelector func(multiJWKS *MultipleJWKS, token *jwt.Token) (key interface{}, err error)
|
||||
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.
|
||||
//
|
||||
// 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 options.KeySelector == nil {
|
||||
options.KeySelector = KeySelectorFirst
|
||||
}
|
||||
|
||||
multiJWKS = &MultipleJWKS{
|
||||
sets: make(map[string]*JWKS, len(multiple)),
|
||||
keySelector: options.KeySelector,
|
||||
}
|
||||
|
||||
for u, opts := range multiple {
|
||||
jwks, err := Get(u, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get JWKS from %q: %w", u, err)
|
||||
}
|
||||
multiJWKS.sets[u] = jwks
|
||||
}
|
||||
|
||||
return multiJWKS, nil
|
||||
}
|
||||
|
||||
func (m *MultipleJWKS) JWKSets() map[string]*JWKS {
|
||||
sets := make(map[string]*JWKS, len(m.sets))
|
||||
for u, jwks := range m.sets {
|
||||
sets[u] = jwks
|
||||
}
|
||||
return sets
|
||||
}
|
||||
|
||||
func KeySelectorFirst(multiJWKS *MultipleJWKS, token *jwt.Token) (key interface{}, err error) {
|
||||
kid, alg, err := kidAlg(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, jwks := range multiJWKS.sets {
|
||||
key, err = jwks.getKey(alg, kid)
|
||||
if err == nil {
|
||||
return key, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("failed to find key ID in multiple JWKS: %w", ErrKIDNotFound)
|
||||
}
|
||||
25
vendor/github.com/MicahParks/keyfunc/options.go
generated
vendored
25
vendor/github.com/MicahParks/keyfunc/options.go
generated
vendored
@@ -8,6 +8,8 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
|
||||
// ErrInvalidHTTPStatusCode indicates that the HTTP status code is invalid.
|
||||
@@ -70,6 +72,9 @@ type Options struct {
|
||||
// This is done through a background goroutine. Without specifying a RefreshInterval a malicious client could
|
||||
// self-sign X JWTs, send them to this service, then cause potentially high network usage proportional to X. Make
|
||||
// sure to call the JWKS.EndBackground method to end this goroutine when it's no longer needed.
|
||||
//
|
||||
// It is recommended this option is not used when in MultipleJWKS. This is because KID collisions SHOULD be uncommon
|
||||
// meaning nearly any JWT SHOULD trigger a refresh for the number of JWKS in the MultipleJWKS minus one.
|
||||
RefreshUnknownKID bool
|
||||
|
||||
// RequestFactory creates HTTP requests for the remote JWKS resource located at the given url. For example, an
|
||||
@@ -81,6 +86,26 @@ type Options struct {
|
||||
ResponseExtractor func(ctx context.Context, resp *http.Response) (json.RawMessage, error)
|
||||
}
|
||||
|
||||
// MultipleOptions is used to configure the behavior when multiple JWKS are used by MultipleJWKS.
|
||||
type MultipleOptions struct {
|
||||
// KeySelector is a function that selects the key to use for a given token. It will be used in the implementation
|
||||
// for jwt.Keyfunc. If implementing this custom selector extract the key ID and algorithm from the token's header.
|
||||
// Use the key ID to select a token and confirm the key's algorithm before returning it.
|
||||
//
|
||||
// This value defaults to KeySelectorFirst.
|
||||
KeySelector func(multiJWKS *MultipleJWKS, token *jwt.Token) (key interface{}, err error)
|
||||
}
|
||||
|
||||
// RefreshOptions are used to specify manual refresh behavior.
|
||||
type RefreshOptions struct {
|
||||
IgnoreRateLimit bool
|
||||
}
|
||||
|
||||
type refreshRequest struct {
|
||||
cancel context.CancelFunc
|
||||
ignoreRateLimit bool
|
||||
}
|
||||
|
||||
// ResponseExtractorStatusOK is meant to be used as the ResponseExtractor field for Options. It confirms that response
|
||||
// status code is 200 OK and returns the raw JSON from the response body.
|
||||
func ResponseExtractorStatusOK(ctx context.Context, resp *http.Response) (json.RawMessage, error) {
|
||||
|
||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -24,7 +24,7 @@ github.com/Masterminds/semver
|
||||
# github.com/Masterminds/sprig v2.22.0+incompatible
|
||||
## explicit
|
||||
github.com/Masterminds/sprig
|
||||
# github.com/MicahParks/keyfunc v1.5.1
|
||||
# github.com/MicahParks/keyfunc v1.9.0
|
||||
## explicit; go 1.16
|
||||
github.com/MicahParks/keyfunc
|
||||
# github.com/Microsoft/go-winio v0.6.0
|
||||
|
||||
Reference in New Issue
Block a user