summaryrefslogtreecommitdiff
path: root/vendor/github.com/coreos/go-oidc
diff options
context:
space:
mode:
authorLibravatar Tobi Smethurst <31960611+tsmethurst@users.noreply.github.com>2021-08-12 21:03:24 +0200
committerLibravatar GitHub <noreply@github.com>2021-08-12 21:03:24 +0200
commit98263a7de64269898a2f81207e38943b5c8e8653 (patch)
tree743c90f109a6c5d27832d1dcef2388d939f0f77a /vendor/github.com/coreos/go-oidc
parentText duplication fix (#137) (diff)
downloadgotosocial-98263a7de64269898a2f81207e38943b5c8e8653.tar.xz
Grand test fixup (#138)
* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
Diffstat (limited to 'vendor/github.com/coreos/go-oidc')
-rw-r--r--vendor/github.com/coreos/go-oidc/v3/LICENSE202
-rw-r--r--vendor/github.com/coreos/go-oidc/v3/NOTICE5
-rw-r--r--vendor/github.com/coreos/go-oidc/v3/oidc/jose.go16
-rw-r--r--vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go208
-rw-r--r--vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go459
-rw-r--r--vendor/github.com/coreos/go-oidc/v3/oidc/verify.go336
6 files changed, 1226 insertions, 0 deletions
diff --git a/vendor/github.com/coreos/go-oidc/v3/LICENSE b/vendor/github.com/coreos/go-oidc/v3/LICENSE
new file mode 100644
index 000000000..e06d20818
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/v3/LICENSE
@@ -0,0 +1,202 @@
+Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/vendor/github.com/coreos/go-oidc/v3/NOTICE b/vendor/github.com/coreos/go-oidc/v3/NOTICE
new file mode 100644
index 000000000..b39ddfa5c
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/v3/NOTICE
@@ -0,0 +1,5 @@
+CoreOS Project
+Copyright 2014 CoreOS, Inc
+
+This product includes software developed at CoreOS, Inc.
+(http://www.coreos.com/).
diff --git a/vendor/github.com/coreos/go-oidc/v3/oidc/jose.go b/vendor/github.com/coreos/go-oidc/v3/oidc/jose.go
new file mode 100644
index 000000000..8afa895c1
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/v3/oidc/jose.go
@@ -0,0 +1,16 @@
+package oidc
+
+// JOSE asymmetric signing algorithm values as defined by RFC 7518
+//
+// see: https://tools.ietf.org/html/rfc7518#section-3.1
+const (
+ RS256 = "RS256" // RSASSA-PKCS-v1.5 using SHA-256
+ RS384 = "RS384" // RSASSA-PKCS-v1.5 using SHA-384
+ RS512 = "RS512" // RSASSA-PKCS-v1.5 using SHA-512
+ ES256 = "ES256" // ECDSA using P-256 and SHA-256
+ ES384 = "ES384" // ECDSA using P-384 and SHA-384
+ ES512 = "ES512" // ECDSA using P-521 and SHA-512
+ PS256 = "PS256" // RSASSA-PSS using SHA256 and MGF1-SHA256
+ PS384 = "PS384" // RSASSA-PSS using SHA384 and MGF1-SHA384
+ PS512 = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512
+)
diff --git a/vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go b/vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go
new file mode 100644
index 000000000..6a162689b
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go
@@ -0,0 +1,208 @@
+package oidc
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "sync"
+ "time"
+
+ jose "gopkg.in/square/go-jose.v2"
+)
+
+// 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
+// exposed for providers that don't support discovery or to prevent round trips to the
+// discovery URL.
+//
+// The returned KeySet is a long lived verifier that caches keys based on cache-control
+// headers. Reuse a common remote key set instead of creating new ones as needed.
+func NewRemoteKeySet(ctx context.Context, jwksURL string) *RemoteKeySet {
+ return newRemoteKeySet(ctx, jwksURL, time.Now)
+}
+
+func newRemoteKeySet(ctx context.Context, jwksURL string, now func() time.Time) *RemoteKeySet {
+ if now == nil {
+ now = time.Now
+ }
+ return &RemoteKeySet{jwksURL: jwksURL, ctx: cloneContext(ctx), now: now}
+}
+
+// RemoteKeySet is a KeySet implementation that validates JSON web tokens against
+// a jwks_uri endpoint.
+type RemoteKeySet struct {
+ jwksURL string
+ ctx context.Context
+ now func() time.Time
+
+ // guard all other fields
+ mu sync.Mutex
+
+ // inflight suppresses parallel execution of updateKeys and allows
+ // multiple goroutines to wait for its result.
+ inflight *inflight
+
+ // A set of cached keys.
+ cachedKeys []jose.JSONWebKey
+}
+
+// inflight is used to wait on some in-flight request from multiple goroutines.
+type inflight struct {
+ doneCh chan struct{}
+
+ keys []jose.JSONWebKey
+ err error
+}
+
+func newInflight() *inflight {
+ return &inflight{doneCh: make(chan struct{})}
+}
+
+// wait returns a channel that multiple goroutines can receive on. Once it returns
+// a value, the inflight request is done and result() can be inspected.
+func (i *inflight) wait() <-chan struct{} {
+ return i.doneCh
+}
+
+// done can only be called by a single goroutine. It records the result of the
+// inflight request and signals other goroutines that the result is safe to
+// inspect.
+func (i *inflight) done(keys []jose.JSONWebKey, err error) {
+ i.keys = keys
+ i.err = err
+ close(i.doneCh)
+}
+
+// result cannot be called until the wait() channel has returned a value.
+func (i *inflight) result() ([]jose.JSONWebKey, error) {
+ return i.keys, i.err
+}
+
+// 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)
+ }
+ return r.verify(ctx, jws)
+}
+
+func (r *RemoteKeySet) verify(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) {
+ // We don't support JWTs signed with multiple signatures.
+ keyID := ""
+ for _, sig := range jws.Signatures {
+ keyID = sig.Header.KeyID
+ break
+ }
+
+ keys := r.keysFromCache()
+ for _, key := range keys {
+ if keyID == "" || key.KeyID == keyID {
+ if payload, err := jws.Verify(&key); err == nil {
+ return payload, nil
+ }
+ }
+ }
+
+ // If the kid doesn't match, check for new keys from the remote. This is the
+ // strategy recommended by the spec.
+ //
+ // https://openid.net/specs/openid-connect-core-1_0.html#RotateSigKeys
+ keys, err := r.keysFromRemote(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("fetching keys %v", err)
+ }
+
+ for _, key := range keys {
+ if keyID == "" || key.KeyID == keyID {
+ if payload, err := jws.Verify(&key); err == nil {
+ return payload, nil
+ }
+ }
+ }
+ return nil, errors.New("failed to verify id token signature")
+}
+
+func (r *RemoteKeySet) keysFromCache() (keys []jose.JSONWebKey) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ return r.cachedKeys
+}
+
+// keysFromRemote syncs the key set from the remote set, records the values in the
+// cache, and returns the key set.
+func (r *RemoteKeySet) keysFromRemote(ctx context.Context) ([]jose.JSONWebKey, error) {
+ // Need to lock to inspect the inflight request field.
+ r.mu.Lock()
+ // If there's not a current inflight request, create one.
+ if r.inflight == nil {
+ r.inflight = newInflight()
+
+ // This goroutine has exclusive ownership over the current inflight
+ // request. It releases the resource by nil'ing the inflight field
+ // once the goroutine is done.
+ go func() {
+ // Sync keys and finish inflight when that's done.
+ keys, err := r.updateKeys()
+
+ r.inflight.done(keys, err)
+
+ // Lock to update the keys and indicate that there is no longer an
+ // inflight request.
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if err == nil {
+ r.cachedKeys = keys
+ }
+
+ // Free inflight so a different request can run.
+ r.inflight = nil
+ }()
+ }
+ inflight := r.inflight
+ r.mu.Unlock()
+
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ case <-inflight.wait():
+ return inflight.result()
+ }
+}
+
+func (r *RemoteKeySet) updateKeys() ([]jose.JSONWebKey, error) {
+ req, err := http.NewRequest("GET", r.jwksURL, nil)
+ if err != nil {
+ return nil, fmt.Errorf("oidc: can't create request: %v", err)
+ }
+
+ resp, err := doRequest(r.ctx, req)
+ if err != nil {
+ return nil, fmt.Errorf("oidc: get keys failed %v", 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("oidc: get keys failed: %s %s", resp.Status, body)
+ }
+
+ var keySet jose.JSONWebKeySet
+ err = unmarshalResp(resp, body, &keySet)
+ if err != nil {
+ return nil, fmt.Errorf("oidc: failed to decode keys: %v %s", err, body)
+ }
+ return keySet.Keys, nil
+}
diff --git a/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go b/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go
new file mode 100644
index 000000000..9726f13bd
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go
@@ -0,0 +1,459 @@
+// 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"
+ jose "gopkg.in/square/go-jose.v2"
+)
+
+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")
+)
+
+// 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 cancelation. 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
+}
+
+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 cachedKeys struct {
+ keys []jose.JSONWebKey
+ expiry time.Time
+}
+
+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)
+ }
+
+ if p.Issuer != issuer {
+ 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: p.Issuer,
+ 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&#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 = stringAsBool(true)
+ case "false", `"false"`:
+ *sb = stringAsBool(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 = audience(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)
+}
diff --git a/vendor/github.com/coreos/go-oidc/v3/oidc/verify.go b/vendor/github.com/coreos/go-oidc/v3/oidc/verify.go
new file mode 100644
index 000000000..5c4d6582c
--- /dev/null
+++ b/vendor/github.com/coreos/go-oidc/v3/oidc/verify.go
@@ -0,0 +1,336 @@
+package oidc
+
+import (
+ "bytes"
+ "context"
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "strings"
+ "time"
+
+ "golang.org/x/oauth2"
+ jose "gopkg.in/square/go-jose.v2"
+)
+
+const (
+ issuerGoogleAccounts = "https://accounts.google.com"
+ issuerGoogleAccountsNoScheme = "accounts.google.com"
+)
+
+// 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.
+type KeySet interface {
+ // VerifySignature parses the JSON web token, verifies the signature, and returns
+ // the raw payload. Header and claim fields are validated by other parts of the
+ // package. For example, the KeySet does not need to check values such as signature
+ // algorithm, issuer, and audience since the IDTokenVerifier validates these values
+ // independently.
+ //
+ // If VerifySignature makes HTTP requests to verify the token, it's expected to
+ // use any HTTP client associated with the context through ClientContext.
+ VerifySignature(ctx context.Context, jwt string) (payload []byte, err error)
+}
+
+// IDTokenVerifier provides verification for ID Tokens.
+type IDTokenVerifier struct {
+ keySet KeySet
+ config *Config
+ issuer string
+}
+
+// NewVerifier returns a verifier manually constructed from a key set and issuer URL.
+//
+// It's easier to use provider discovery to construct an IDTokenVerifier than creating
+// one directly. This method is intended to be used with provider that don't support
+// metadata discovery, or avoiding round trips when the key set URL is already known.
+//
+// This constructor can be used to create a verifier directly using the issuer URL and
+// JSON Web Key Set URL without using discovery:
+//
+// 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...)
+//
+// // Verifier uses the custom KeySet implementation.
+// verifier := oidc.NewVerifier("https://auth.example.com", keySet, config)
+//
+func NewVerifier(issuerURL string, keySet KeySet, config *Config) *IDTokenVerifier {
+ return &IDTokenVerifier{keySet: keySet, config: config, issuer: issuerURL}
+}
+
+// Config is the configuration for an IDTokenVerifier.
+type Config struct {
+ // Expected audience of the token. For a majority of the cases this is expected to be
+ // the ID of the client that initialized the login flow. It may occasionally differ if
+ // the provider supports the authorizing party (azp) claim.
+ //
+ // If not provided, users must explicitly set SkipClientIDCheck.
+ ClientID string
+ // If specified, only this set of algorithms may be used to sign the JWT.
+ //
+ // If the IDTokenVerifier is created from a provider with (*Provider).Verifier, this
+ // defaults to the set of algorithms the provider supports. Otherwise this values
+ // defaults to RS256.
+ SupportedSigningAlgs []string
+
+ // If true, no ClientID check performed. Must be true if ClientID field is empty.
+ SkipClientIDCheck bool
+ // If true, token expiry is not checked.
+ SkipExpiryCheck bool
+
+ // SkipIssuerCheck is intended for specialized cases where the the caller wishes to
+ // defer issuer validation. When enabled, callers MUST independently verify the Token's
+ // Issuer is a known good value.
+ //
+ // Mismatched issuers often indicate client mis-configuration. If mismatches are
+ // unexpected, evaluate if the provided issuer URL is incorrect instead of enabling
+ // this option.
+ SkipIssuerCheck bool
+
+ // Time function to check Token expiry. Defaults to time.Now
+ Now func() time.Time
+}
+
+// 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.
+ cp := &Config{}
+ *cp = *config
+ cp.SupportedSigningAlgs = p.algorithms
+ config = cp
+ }
+ return NewVerifier(p.issuer, p.remoteKeySet, config)
+}
+
+func parseJWT(p string) ([]byte, error) {
+ parts := strings.Split(p, ".")
+ if len(parts) < 2 {
+ return nil, fmt.Errorf("oidc: malformed jwt, expected 3 parts got %d", len(parts))
+ }
+ payload, err := base64.RawURLEncoding.DecodeString(parts[1])
+ if err != nil {
+ return nil, fmt.Errorf("oidc: malformed jwt payload: %v", err)
+ }
+ return payload, nil
+}
+
+func contains(sli []string, ele string) bool {
+ for _, s := range sli {
+ if s == ele {
+ return true
+ }
+ }
+ return false
+}
+
+// Returns the Claims from the distributed JWT token
+func resolveDistributedClaim(ctx context.Context, verifier *IDTokenVerifier, src claimSource) ([]byte, error) {
+ req, err := http.NewRequest("GET", src.Endpoint, nil)
+ if err != nil {
+ return nil, fmt.Errorf("malformed request: %v", err)
+ }
+ if src.AccessToken != "" {
+ req.Header.Set("Authorization", "Bearer "+src.AccessToken)
+ }
+
+ resp, err := doRequest(ctx, req)
+ if err != nil {
+ return nil, fmt.Errorf("oidc: Request to endpoint failed: %v", 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("oidc: request failed: %v", resp.StatusCode)
+ }
+
+ token, err := verifier.Verify(ctx, string(body))
+ if err != nil {
+ return nil, fmt.Errorf("malformed response body: %v", err)
+ }
+
+ return token.claims, nil
+}
+
+func parseClaim(raw []byte, name string, v interface{}) error {
+ var parsed map[string]json.RawMessage
+ if err := json.Unmarshal(raw, &parsed); err != nil {
+ return err
+ }
+
+ val, ok := parsed[name]
+ if !ok {
+ return fmt.Errorf("claim doesn't exist: %s", name)
+ }
+
+ return json.Unmarshal([]byte(val), v)
+}
+
+// Verify parses a raw ID Token, verifies it's been signed by the provider, performs
+// any additional checks depending on the Config, and returns the payload.
+//
+// Verify does NOT do nonce validation, which is the callers responsibility.
+//
+// See: https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
+//
+// oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
+// if err != nil {
+// // handle error
+// }
+//
+// // Extract the ID Token from oauth2 token.
+// rawIDToken, ok := oauth2Token.Extra("id_token").(string)
+// if !ok {
+// // handle error
+// }
+//
+// 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)
+ if err != nil {
+ return nil, fmt.Errorf("oidc: malformed jwt: %v", err)
+ }
+ var token idToken
+ if err := json.Unmarshal(payload, &token); err != nil {
+ return nil, fmt.Errorf("oidc: failed to unmarshal claims: %v", err)
+ }
+
+ distributedClaims := make(map[string]claimSource)
+
+ //step through the token to map claim names to claim sources"
+ for cn, src := range token.ClaimNames {
+ if src == "" {
+ return nil, fmt.Errorf("oidc: failed to obtain source from claim name")
+ }
+ s, ok := token.ClaimSources[src]
+ if !ok {
+ return nil, fmt.Errorf("oidc: source does not exist")
+ }
+ distributedClaims[cn] = s
+ }
+
+ t := &IDToken{
+ Issuer: token.Issuer,
+ Subject: token.Subject,
+ Audience: []string(token.Audience),
+ Expiry: time.Time(token.Expiry),
+ IssuedAt: time.Time(token.IssuedAt),
+ Nonce: token.Nonce,
+ AccessTokenHash: token.AtHash,
+ claims: payload,
+ distributedClaims: distributedClaims,
+ }
+
+ // Check issuer.
+ if !v.config.SkipIssuerCheck && t.Issuer != v.issuer {
+ // Google sometimes returns "accounts.google.com" as the issuer claim instead of
+ // the required "https://accounts.google.com". Detect this case and allow it only
+ // for Google.
+ //
+ // We will not add hooks to let other providers go off spec like this.
+ if !(v.issuer == issuerGoogleAccounts && t.Issuer == issuerGoogleAccountsNoScheme) {
+ return nil, fmt.Errorf("oidc: id token issued by a different provider, expected %q got %q", v.issuer, t.Issuer)
+ }
+ }
+
+ // If a client ID has been provided, make sure it's part of the audience. SkipClientIDCheck must be true if ClientID is empty.
+ //
+ // This check DOES NOT ensure that the ClientID is the party to which the ID Token was issued (i.e. Authorized party).
+ if !v.config.SkipClientIDCheck {
+ if v.config.ClientID != "" {
+ if !contains(t.Audience, v.config.ClientID) {
+ return nil, fmt.Errorf("oidc: expected audience %q got %q", v.config.ClientID, t.Audience)
+ }
+ } else {
+ return nil, fmt.Errorf("oidc: invalid configuration, clientID must be provided or SkipClientIDCheck must be set")
+ }
+ }
+
+ // If a SkipExpiryCheck is false, make sure token is not expired.
+ if !v.config.SkipExpiryCheck {
+ now := time.Now
+ if v.config.Now != nil {
+ now = v.config.Now
+ }
+ nowTime := now()
+
+ if t.Expiry.Before(nowTime) {
+ return nil, fmt.Errorf("oidc: token is expired (Token Expiry: %v)", 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
+
+ if nowTime.Add(leeway).Before(nbfTime) {
+ return nil, fmt.Errorf("oidc: current time %v before the nbf (not before) time: %v", nowTime, nbfTime)
+ }
+ }
+ }
+
+ switch len(jws.Signatures) {
+ case 0:
+ return nil, fmt.Errorf("oidc: id token not signed")
+ case 1:
+ default:
+ return nil, fmt.Errorf("oidc: multiple signatures on id token not supported")
+ }
+
+ sig := jws.Signatures[0]
+ supportedSigAlgs := v.config.SupportedSigningAlgs
+ if len(supportedSigAlgs) == 0 {
+ supportedSigAlgs = []string{RS256}
+ }
+
+ if !contains(supportedSigAlgs, sig.Header.Algorithm) {
+ return nil, fmt.Errorf("oidc: id token signed with unsupported algorithm, expected %q got %q", supportedSigAlgs, sig.Header.Algorithm)
+ }
+
+ t.sigAlgorithm = sig.Header.Algorithm
+
+ gotPayload, err := v.keySet.VerifySignature(ctx, rawIDToken)
+ if err != nil {
+ return nil, fmt.Errorf("failed to verify signature: %v", err)
+ }
+
+ // Ensure that the payload returned by the square actually matches the payload parsed earlier.
+ if !bytes.Equal(gotPayload, payload) {
+ return nil, errors.New("oidc: internal error, payload parsed did not match previous payload")
+ }
+
+ return t, nil
+}
+
+// Nonce returns an auth code option which requires the ID Token created by the
+// OpenID Connect provider to contain the specified nonce.
+func Nonce(nonce string) oauth2.AuthCodeOption {
+ return oauth2.SetAuthURLParam("nonce", nonce)
+}