diff options
Diffstat (limited to 'vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go')
-rw-r--r-- | vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go | 480 |
1 files changed, 0 insertions, 480 deletions
diff --git a/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go b/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go deleted file mode 100644 index 3e1d80e08..000000000 --- a/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go +++ /dev/null @@ -1,480 +0,0 @@ -// Package oidc implements OpenID Connect client logic for the golang.org/x/oauth2 package. -package oidc - -import ( - "context" - "crypto/sha256" - "crypto/sha512" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "hash" - "io/ioutil" - "mime" - "net/http" - "strings" - "time" - - "golang.org/x/oauth2" -) - -const ( - // ScopeOpenID is the mandatory scope for all OpenID Connect OAuth2 requests. - ScopeOpenID = "openid" - - // ScopeOfflineAccess is an optional scope defined by OpenID Connect for requesting - // OAuth2 refresh tokens. - // - // Support for this scope differs between OpenID Connect providers. For instance - // Google rejects it, favoring appending "access_type=offline" as part of the - // authorization request instead. - // - // See: https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess - ScopeOfflineAccess = "offline_access" -) - -var ( - errNoAtHash = errors.New("id token did not have an access token hash") - errInvalidAtHash = errors.New("access token hash does not match value in ID token") -) - -type contextKey int - -var issuerURLKey contextKey - -// ClientContext returns a new Context that carries the provided HTTP client. -// -// This method sets the same context key used by the golang.org/x/oauth2 package, -// so the returned context works for that package too. -// -// myClient := &http.Client{} -// ctx := oidc.ClientContext(parentContext, myClient) -// -// // This will use the custom client -// provider, err := oidc.NewProvider(ctx, "https://accounts.example.com") -// -func ClientContext(ctx context.Context, client *http.Client) context.Context { - return context.WithValue(ctx, oauth2.HTTPClient, client) -} - -// cloneContext copies a context's bag-of-values into a new context that isn't -// associated with its cancellation. This is used to initialize remote keys sets -// which run in the background and aren't associated with the initial context. -func cloneContext(ctx context.Context) context.Context { - cp := context.Background() - if c, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok { - cp = ClientContext(cp, c) - } - return cp -} - -// InsecureIssuerURLContext allows discovery to work when the issuer_url reported -// by upstream is mismatched with the discovery URL. This is meant for integration -// with off-spec providers such as Azure. -// -// discoveryBaseURL := "https://login.microsoftonline.com/organizations/v2.0" -// issuerURL := "https://login.microsoftonline.com/my-tenantid/v2.0" -// -// ctx := oidc.InsecureIssuerURLContext(parentContext, issuerURL) -// -// // Provider will be discovered with the discoveryBaseURL, but use issuerURL -// // for future issuer validation. -// provider, err := oidc.NewProvider(ctx, discoveryBaseURL) -// -// This is insecure because validating the correct issuer is critical for multi-tenant -// proivders. Any overrides here MUST be carefully reviewed. -func InsecureIssuerURLContext(ctx context.Context, issuerURL string) context.Context { - return context.WithValue(ctx, issuerURLKey, issuerURL) -} - -func doRequest(ctx context.Context, req *http.Request) (*http.Response, error) { - client := http.DefaultClient - if c, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok { - client = c - } - return client.Do(req.WithContext(ctx)) -} - -// Provider represents an OpenID Connect server's configuration. -type Provider struct { - issuer string - authURL string - tokenURL string - userInfoURL string - algorithms []string - - // Raw claims returned by the server. - rawClaims []byte - - remoteKeySet KeySet -} - -type providerJSON struct { - Issuer string `json:"issuer"` - AuthURL string `json:"authorization_endpoint"` - TokenURL string `json:"token_endpoint"` - JWKSURL string `json:"jwks_uri"` - UserInfoURL string `json:"userinfo_endpoint"` - Algorithms []string `json:"id_token_signing_alg_values_supported"` -} - -// supportedAlgorithms is a list of algorithms explicitly supported by this -// package. If a provider supports other algorithms, such as HS256 or none, -// those values won't be passed to the IDTokenVerifier. -var supportedAlgorithms = map[string]bool{ - RS256: true, - RS384: true, - RS512: true, - ES256: true, - ES384: true, - ES512: true, - PS256: true, - PS384: true, - PS512: true, -} - -// 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" -// or "https://login.salesforce.com". -func NewProvider(ctx context.Context, issuer string) (*Provider, error) { - wellKnown := strings.TrimSuffix(issuer, "/") + "/.well-known/openid-configuration" - req, err := http.NewRequest("GET", wellKnown, nil) - if err != nil { - return nil, err - } - resp, err := doRequest(ctx, req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("unable to read response body: %v", err) - } - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%s: %s", resp.Status, body) - } - - var p providerJSON - err = unmarshalResp(resp, body, &p) - if err != nil { - return nil, fmt.Errorf("oidc: failed to decode provider discovery object: %v", err) - } - - issuerURL, skipIssuerValidation := ctx.Value(issuerURLKey).(string) - if !skipIssuerValidation { - issuerURL = issuer - } - if p.Issuer != issuerURL && !skipIssuerValidation { - return nil, fmt.Errorf("oidc: issuer did not match the issuer returned by provider, expected %q got %q", issuer, p.Issuer) - } - var algs []string - for _, a := range p.Algorithms { - if supportedAlgorithms[a] { - algs = append(algs, a) - } - } - return &Provider{ - issuer: issuerURL, - authURL: p.AuthURL, - tokenURL: p.TokenURL, - userInfoURL: p.UserInfoURL, - algorithms: algs, - rawClaims: body, - remoteKeySet: NewRemoteKeySet(cloneContext(ctx), p.JWKSURL), - }, nil -} - -// Claims unmarshals raw fields returned by the server during discovery. -// -// var claims struct { -// ScopesSupported []string `json:"scopes_supported"` -// ClaimsSupported []string `json:"claims_supported"` -// } -// -// if err := provider.Claims(&claims); err != nil { -// // handle unmarshaling error -// } -// -// For a list of fields defined by the OpenID Connect spec see: -// https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata -func (p *Provider) Claims(v interface{}) error { - if p.rawClaims == nil { - return errors.New("oidc: claims not set") - } - return json.Unmarshal(p.rawClaims, v) -} - -// Endpoint returns the OAuth2 auth and token endpoints for the given provider. -func (p *Provider) Endpoint() oauth2.Endpoint { - return oauth2.Endpoint{AuthURL: p.authURL, TokenURL: p.tokenURL} -} - -// UserInfo represents the OpenID Connect userinfo claims. -type UserInfo struct { - Subject string `json:"sub"` - Profile string `json:"profile"` - Email string `json:"email"` - EmailVerified bool `json:"email_verified"` - - claims []byte -} - -type userInfoRaw struct { - Subject string `json:"sub"` - Profile string `json:"profile"` - Email string `json:"email"` - // Handle providers that return email_verified as a string - // https://forums.aws.amazon.com/thread.jspa?messageID=949441󧳁 and - // https://discuss.elastic.co/t/openid-error-after-authenticating-against-aws-cognito/206018/11 - EmailVerified stringAsBool `json:"email_verified"` -} - -// Claims unmarshals the raw JSON object claims into the provided object. -func (u *UserInfo) Claims(v interface{}) error { - if u.claims == nil { - return errors.New("oidc: claims not set") - } - return json.Unmarshal(u.claims, v) -} - -// UserInfo uses the token source to query the provider's user info endpoint. -func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource) (*UserInfo, error) { - if p.userInfoURL == "" { - return nil, errors.New("oidc: user info endpoint is not supported by this provider") - } - - req, err := http.NewRequest("GET", p.userInfoURL, nil) - if err != nil { - return nil, fmt.Errorf("oidc: create GET request: %v", err) - } - - token, err := tokenSource.Token() - if err != nil { - return nil, fmt.Errorf("oidc: get access token: %v", err) - } - token.SetAuthHeader(req) - - resp, err := doRequest(ctx, req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%s: %s", resp.Status, body) - } - - ct := resp.Header.Get("Content-Type") - mediaType, _, parseErr := mime.ParseMediaType(ct) - if parseErr == nil && mediaType == "application/jwt" { - payload, err := p.remoteKeySet.VerifySignature(ctx, string(body)) - if err != nil { - return nil, fmt.Errorf("oidc: invalid userinfo jwt signature %v", err) - } - body = payload - } - - var userInfo userInfoRaw - if err := json.Unmarshal(body, &userInfo); err != nil { - return nil, fmt.Errorf("oidc: failed to decode userinfo: %v", err) - } - return &UserInfo{ - Subject: userInfo.Subject, - Profile: userInfo.Profile, - Email: userInfo.Email, - EmailVerified: bool(userInfo.EmailVerified), - claims: body, - }, nil -} - -// IDToken is an OpenID Connect extension that provides a predictable representation -// of an authorization event. -// -// The ID Token only holds fields OpenID Connect requires. To access additional -// claims returned by the server, use the Claims method. -type IDToken struct { - // The URL of the server which issued this token. OpenID Connect - // requires this value always be identical to the URL used for - // initial discovery. - // - // Note: Because of a known issue with Google Accounts' implementation - // this value may differ when using Google. - // - // See: https://developers.google.com/identity/protocols/OpenIDConnect#obtainuserinfo - Issuer string - - // The client ID, or set of client IDs, that this token is issued for. For - // common uses, this is the client that initialized the auth flow. - // - // This package ensures the audience contains an expected value. - Audience []string - - // A unique string which identifies the end user. - Subject string - - // Expiry of the token. Ths package will not process tokens that have - // expired unless that validation is explicitly turned off. - Expiry time.Time - // When the token was issued by the provider. - IssuedAt time.Time - - // Initial nonce provided during the authentication redirect. - // - // This package does NOT provided verification on the value of this field - // and it's the user's responsibility to ensure it contains a valid value. - Nonce string - - // at_hash claim, if set in the ID token. Callers can verify an access token - // that corresponds to the ID token using the VerifyAccessToken method. - AccessTokenHash string - - // signature algorithm used for ID token, needed to compute a verification hash of an - // access token - sigAlgorithm string - - // Raw payload of the id_token. - claims []byte - - // Map of distributed claim names to claim sources - distributedClaims map[string]claimSource -} - -// Claims unmarshals the raw JSON payload of the ID Token into a provided struct. -// -// idToken, err := idTokenVerifier.Verify(rawIDToken) -// if err != nil { -// // handle error -// } -// var claims struct { -// Email string `json:"email"` -// EmailVerified bool `json:"email_verified"` -// } -// if err := idToken.Claims(&claims); err != nil { -// // handle error -// } -// -func (i *IDToken) Claims(v interface{}) error { - if i.claims == nil { - return errors.New("oidc: claims not set") - } - return json.Unmarshal(i.claims, v) -} - -// VerifyAccessToken verifies that the hash of the access token that corresponds to the iD token -// matches the hash in the id token. It returns an error if the hashes don't match. -// It is the caller's responsibility to ensure that the optional access token hash is present for the ID token -// before calling this method. See https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken -func (i *IDToken) VerifyAccessToken(accessToken string) error { - if i.AccessTokenHash == "" { - return errNoAtHash - } - var h hash.Hash - switch i.sigAlgorithm { - case RS256, ES256, PS256: - h = sha256.New() - case RS384, ES384, PS384: - h = sha512.New384() - case RS512, ES512, PS512: - h = sha512.New() - default: - return fmt.Errorf("oidc: unsupported signing algorithm %q", i.sigAlgorithm) - } - h.Write([]byte(accessToken)) // hash documents that Write will never return an error - sum := h.Sum(nil)[:h.Size()/2] - actual := base64.RawURLEncoding.EncodeToString(sum) - if actual != i.AccessTokenHash { - return errInvalidAtHash - } - return nil -} - -type idToken struct { - Issuer string `json:"iss"` - Subject string `json:"sub"` - Audience audience `json:"aud"` - Expiry jsonTime `json:"exp"` - IssuedAt jsonTime `json:"iat"` - NotBefore *jsonTime `json:"nbf"` - Nonce string `json:"nonce"` - AtHash string `json:"at_hash"` - ClaimNames map[string]string `json:"_claim_names"` - ClaimSources map[string]claimSource `json:"_claim_sources"` -} - -type claimSource struct { - Endpoint string `json:"endpoint"` - AccessToken string `json:"access_token"` -} - -type stringAsBool bool - -func (sb *stringAsBool) UnmarshalJSON(b []byte) error { - switch string(b) { - case "true", `"true"`: - *sb = true - case "false", `"false"`: - *sb = false - default: - return errors.New("invalid value for boolean") - } - return nil -} - -type audience []string - -func (a *audience) UnmarshalJSON(b []byte) error { - var s string - if json.Unmarshal(b, &s) == nil { - *a = audience{s} - return nil - } - var auds []string - if err := json.Unmarshal(b, &auds); err != nil { - return err - } - *a = auds - return nil -} - -type jsonTime time.Time - -func (j *jsonTime) UnmarshalJSON(b []byte) error { - var n json.Number - if err := json.Unmarshal(b, &n); err != nil { - return err - } - var unix int64 - - if t, err := n.Int64(); err == nil { - unix = t - } else { - f, err := n.Float64() - if err != nil { - return err - } - unix = int64(f) - } - *j = jsonTime(time.Unix(unix, 0)) - return nil -} - -func unmarshalResp(r *http.Response, body []byte, v interface{}) error { - err := json.Unmarshal(body, &v) - if err == nil { - return nil - } - ct := r.Header.Get("Content-Type") - mediaType, _, parseErr := mime.ParseMediaType(ct) - if parseErr == nil && mediaType == "application/json" { - return fmt.Errorf("got Content-Type = application/json, but could not unmarshal as JSON: %v", err) - } - return fmt.Errorf("expected Content-Type = application/json, got %q: %v", ct, err) -} |