summaryrefslogtreecommitdiff
path: root/vendor/github.com/coreos/go-oidc
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/coreos/go-oidc')
-rw-r--r--vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go46
-rw-r--r--vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go42
-rw-r--r--vendor/github.com/coreos/go-oidc/v3/oidc/verify.go58
3 files changed, 125 insertions, 21 deletions
diff --git a/vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go b/vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go
index a272b7ab2..fdcfba81e 100644
--- a/vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go
+++ b/vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go
@@ -2,6 +2,9 @@ package oidc
import (
"context"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/rsa"
"errors"
"fmt"
"io/ioutil"
@@ -12,6 +15,35 @@ import (
jose "gopkg.in/square/go-jose.v2"
)
+// StaticKeySet is a verifier that validates JWT against a static set of public keys.
+type StaticKeySet struct {
+ // PublicKeys used to verify the JWT. Supported types are *rsa.PublicKey and
+ // *ecdsa.PublicKey.
+ PublicKeys []crypto.PublicKey
+}
+
+// VerifySignature compares the signature against a static set of public keys.
+func (s *StaticKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, error) {
+ jws, err := jose.ParseSigned(jwt)
+ if err != nil {
+ return nil, fmt.Errorf("parsing jwt: %v", err)
+ }
+ for _, pub := range s.PublicKeys {
+ switch pub.(type) {
+ case *rsa.PublicKey:
+ case *ecdsa.PublicKey:
+ default:
+ return nil, fmt.Errorf("invalid public key type provided: %T", pub)
+ }
+ payload, err := jws.Verify(pub)
+ if err != nil {
+ continue
+ }
+ return payload, nil
+ }
+ return nil, fmt.Errorf("no public keys able to verify jwt")
+}
+
// NewRemoteKeySet returns a KeySet that can validate JSON web tokens by using HTTP
// GETs to fetch JSON web token sets hosted at a remote URL. This is automatically
// used by NewProvider using the URLs returned by OpenID Connect discovery, but is
@@ -81,15 +113,23 @@ func (i *inflight) result() ([]jose.JSONWebKey, error) {
return i.keys, i.err
}
+// paresdJWTKey is a context key that allows common setups to avoid parsing the
+// JWT twice. It holds a *jose.JSONWebSignature value.
+var parsedJWTKey contextKey
+
// VerifySignature validates a payload against a signature from the jwks_uri.
//
// Users MUST NOT call this method directly and should use an IDTokenVerifier
// instead. This method skips critical validations such as 'alg' values and is
// only exported to implement the KeySet interface.
func (r *RemoteKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, error) {
- jws, err := jose.ParseSigned(jwt)
- if err != nil {
- return nil, fmt.Errorf("oidc: malformed jwt: %v", err)
+ jws, ok := ctx.Value(parsedJWTKey).(*jose.JSONWebSignature)
+ if !ok {
+ var err error
+ jws, err = jose.ParseSigned(jwt)
+ if err != nil {
+ return nil, fmt.Errorf("oidc: malformed jwt: %v", err)
+ }
}
return r.verify(ctx, jws)
}
diff --git a/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go b/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go
index 3e1d80e08..ae73eb028 100644
--- a/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go
+++ b/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go
@@ -134,6 +134,48 @@ var supportedAlgorithms = map[string]bool{
PS512: true,
}
+// ProviderConfig allows creating providers when discovery isn't supported. It's
+// generally easier to use NewProvider directly.
+type ProviderConfig struct {
+ // IssuerURL is the identity of the provider, and the string it uses to sign
+ // ID tokens with. For example "https://accounts.google.com". This value MUST
+ // match ID tokens exactly.
+ IssuerURL string
+ // AuthURL is the endpoint used by the provider to support the OAuth 2.0
+ // authorization endpoint.
+ AuthURL string
+ // TokenURL is the endpoint used by the provider to support the OAuth 2.0
+ // token endpoint.
+ TokenURL string
+ // UserInfoURL is the endpoint used by the provider to support the OpenID
+ // Connect UserInfo flow.
+ //
+ // https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
+ UserInfoURL string
+ // JWKSURL is the endpoint used by the provider to advertise public keys to
+ // verify issued ID tokens. This endpoint is polled as new keys are made
+ // available.
+ JWKSURL string
+
+ // Algorithms, if provided, indicate a list of JWT algorithms allowed to sign
+ // ID tokens. If not provided, this defaults to the algorithms advertised by
+ // the JWK endpoint, then the set of algorithms supported by this package.
+ Algorithms []string
+}
+
+// NewProvider initializes a provider from a set of endpoints, rather than
+// through discovery.
+func (p *ProviderConfig) NewProvider(ctx context.Context) *Provider {
+ return &Provider{
+ issuer: p.IssuerURL,
+ authURL: p.AuthURL,
+ tokenURL: p.TokenURL,
+ userInfoURL: p.UserInfoURL,
+ algorithms: p.Algorithms,
+ remoteKeySet: NewRemoteKeySet(cloneContext(ctx), p.JWKSURL),
+ }
+}
+
// NewProvider uses the OpenID Connect discovery mechanism to construct a Provider.
//
// The issuer is the URL identifier for the service. For example: "https://accounts.google.com"
diff --git a/vendor/github.com/coreos/go-oidc/v3/oidc/verify.go b/vendor/github.com/coreos/go-oidc/v3/oidc/verify.go
index dc6b56dfe..b0dd60fa9 100644
--- a/vendor/github.com/coreos/go-oidc/v3/oidc/verify.go
+++ b/vendor/github.com/coreos/go-oidc/v3/oidc/verify.go
@@ -21,6 +21,18 @@ const (
issuerGoogleAccountsNoScheme = "accounts.google.com"
)
+// TokenExpiredError indicates that Verify failed because the token was expired. This
+// error does NOT indicate that the token is not also invalid for other reasons. Other
+// checks might have failed if the expiration check had not failed.
+type TokenExpiredError struct {
+ // Expiry is the time when the token expired.
+ Expiry time.Time
+}
+
+func (e *TokenExpiredError) Error() string {
+ return fmt.Sprintf("oidc: token is expired (Token Expiry: %v)", e.Expiry)
+}
+
// KeySet is a set of publc JSON Web Keys that can be used to validate the signature
// of JSON web tokens. This is expected to be backed by a remote key set through
// provider metadata discovery or an in-memory set of keys delivered out-of-band.
@@ -55,15 +67,10 @@ type IDTokenVerifier struct {
// keySet := oidc.NewRemoteKeySet(ctx, "https://www.googleapis.com/oauth2/v3/certs")
// verifier := oidc.NewVerifier("https://accounts.google.com", keySet, config)
//
-// Since KeySet is an interface, this constructor can also be used to supply custom
-// public key sources. For example, if a user wanted to supply public keys out-of-band
-// and hold them statically in-memory:
-//
-// // Custom KeySet implementation.
-// keySet := newStatisKeySet(publicKeys...)
+// Or a static key set (e.g. for testing):
//
-// // Verifier uses the custom KeySet implementation.
-// verifier := oidc.NewVerifier("https://auth.example.com", keySet, config)
+// keySet := &oidc.StaticKeySet{PublicKeys: []crypto.PublicKey{pub1, pub2}}
+// verifier := oidc.NewVerifier("https://accounts.google.com", keySet, config)
//
func NewVerifier(issuerURL string, keySet KeySet, config *Config) *IDTokenVerifier {
return &IDTokenVerifier{keySet: keySet, config: config, issuer: issuerURL}
@@ -100,12 +107,20 @@ type Config struct {
// Time function to check Token expiry. Defaults to time.Now
Now func() time.Time
+
+ // InsecureSkipSignatureCheck causes this package to skip JWT signature validation.
+ // It's intended for special cases where providers (such as Azure), use the "none"
+ // algorithm.
+ //
+ // This option can only be enabled safely when the ID Token is received directly
+ // from the provider after the token exchange.
+ //
+ // This option MUST NOT be used when receiving an ID Token from sources other
+ // than the token endpoint.
+ InsecureSkipSignatureCheck bool
}
// Verifier returns an IDTokenVerifier that uses the provider's key set to verify JWTs.
-//
-// The returned IDTokenVerifier is tied to the Provider's context and its behavior is
-// undefined once the Provider's context is canceled.
func (p *Provider) Verifier(config *Config) *IDTokenVerifier {
if len(config.SupportedSigningAlgs) == 0 && len(p.algorithms) > 0 {
// Make a copy so we don't modify the config values.
@@ -192,11 +207,6 @@ func resolveDistributedClaim(ctx context.Context, verifier *IDTokenVerifier, src
// token, err := verifier.Verify(ctx, rawIDToken)
//
func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDToken, error) {
- jws, err := jose.ParseSigned(rawIDToken)
- if err != nil {
- return nil, fmt.Errorf("oidc: malformed jwt: %v", err)
- }
-
// Throw out tokens with invalid claims before trying to verify the token. This lets
// us do cheap checks before possibly re-syncing keys.
payload, err := parseJWT(rawIDToken)
@@ -268,13 +278,15 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDTok
nowTime := now()
if t.Expiry.Before(nowTime) {
- return nil, fmt.Errorf("oidc: token is expired (Token Expiry: %v)", t.Expiry)
+ return nil, &TokenExpiredError{Expiry: t.Expiry}
}
// If nbf claim is provided in token, ensure that it is indeed in the past.
if token.NotBefore != nil {
nbfTime := time.Time(*token.NotBefore)
- leeway := 1 * time.Minute
+ // Set to 5 minutes since this is what other OpenID Connect providers do to deal with clock skew.
+ // https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/6.12.2/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs#L149-L153
+ leeway := 5 * time.Minute
if nowTime.Add(leeway).Before(nbfTime) {
return nil, fmt.Errorf("oidc: current time %v before the nbf (not before) time: %v", nowTime, nbfTime)
@@ -282,6 +294,15 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDTok
}
}
+ if v.config.InsecureSkipSignatureCheck {
+ return t, nil
+ }
+
+ jws, err := jose.ParseSigned(rawIDToken)
+ if err != nil {
+ return nil, fmt.Errorf("oidc: malformed jwt: %v", err)
+ }
+
switch len(jws.Signatures) {
case 0:
return nil, fmt.Errorf("oidc: id token not signed")
@@ -302,6 +323,7 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDTok
t.sigAlgorithm = sig.Header.Algorithm
+ ctx = context.WithValue(ctx, parsedJWTKey, jws)
gotPayload, err := v.keySet.VerifySignature(ctx, rawIDToken)
if err != nil {
return nil, fmt.Errorf("failed to verify signature: %v", err)