From 06ca5fb9966e171bb46033d3e67c4bcdc8a15cbf Mon Sep 17 00:00:00 2001 From: Nico Muerdter Date: Wed, 1 Jul 2020 15:54:53 +0200 Subject: [PATCH] add alternativeIssuer to verifier --- oidc/oidc.go | 24 +++++++++++++----------- oidc/verify.go | 18 ++++++++++-------- oidc/verify_test.go | 16 +++++++++++++++- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/oidc/oidc.go b/oidc/oidc.go index 42425cb3..77c26ba2 100644 --- a/oidc/oidc.go +++ b/oidc/oidc.go @@ -96,7 +96,9 @@ func doRequest(ctx context.Context, req *http.Request) (*http.Response, error) { // Provider represents an OpenID Connect server's configuration. type Provider struct { - issuer string + issuer string + alternativeIssuer []string + authURL string tokenURL string deviceAuthURL string @@ -164,7 +166,7 @@ var supportedAlgorithms = map[string]bool{ // parsing. // // // Directly fetch the metadata document. -// resp, err := http.Get("https://login.example.com/custom-metadata-path") +// resp, err := http.Get("https://login.example.com/custom-metadata-path") // if err != nil { // // ... // } @@ -280,15 +282,15 @@ func NewProvider(ctx context.Context, issuer string, alternativeIssuer ...string } } return &Provider{ - issuer: issuerURL, - authURL: p.AuthURL, - tokenURL: p.TokenURL, - deviceAuthURL: p.DeviceAuthURL, - userInfoURL: p.UserInfoURL, - jwksURL: p.JWKSURL, - algorithms: algs, - rawClaims: body, - client: getClient(ctx), + issuer: p.Issuer, + alternativeIssuer: alternativeIssuer, + authURL: p.AuthURL, + tokenURL: p.TokenURL, + userInfoURL: p.UserInfoURL, + algorithms: algs, + rawClaims: body, + remoteKeySet: NewRemoteKeySet(ctx, p.JWKSURL), + client: getClient(ctx), }, nil } diff --git a/oidc/verify.go b/oidc/verify.go index 52b27b74..25b1f101 100644 --- a/oidc/verify.go +++ b/oidc/verify.go @@ -50,9 +50,10 @@ type KeySet interface { // IDTokenVerifier provides verification for ID Tokens. type IDTokenVerifier struct { - keySet KeySet - config *Config - issuer string + keySet KeySet + config *Config + issuer string + alternativeIssuer []string } // NewVerifier returns a verifier manually constructed from a key set and issuer URL. @@ -71,8 +72,8 @@ type IDTokenVerifier struct { // // 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} +func NewVerifier(issuerURL string, keySet KeySet, config *Config, alternativeIssuer ...string) *IDTokenVerifier { + return &IDTokenVerifier{keySet: keySet, config: config, issuer: issuerURL, alternativeIssuer: alternativeIssuer} } // Config is the configuration for an IDTokenVerifier. @@ -142,7 +143,7 @@ func (p *Provider) newVerifier(keySet KeySet, config *Config) *IDTokenVerifier { cp.SupportedSigningAlgs = p.algorithms config = cp } - return NewVerifier(p.issuer, keySet, config) + return NewVerifier(p.issuer, p.remoteKeySet, config, p.alternativeIssuer...) } func parseJWT(p string) ([]byte, error) { @@ -257,14 +258,15 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDTok } // Check issuer. - if !v.config.SkipIssuerCheck && t.Issuer != v.issuer { + issuerStr := strings.Join(append(v.alternativeIssuer, v.issuer), " ") + if !v.config.SkipIssuerCheck && !strings.Contains(issuerStr, t.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) + return nil, fmt.Errorf("oidc: id token issued by a different provider, expected one of %q got %q", issuerStr, t.Issuer) } } diff --git a/oidc/verify_test.go b/oidc/verify_test.go index f2e2433b..ae3c3a4a 100644 --- a/oidc/verify_test.go +++ b/oidc/verify_test.go @@ -46,6 +46,18 @@ func TestVerify(t *testing.T) { signKey: newRSAKey(t), wantErr: true, }, + { + name: "alternative issuer", + issuer: "https://bar", + alternativeIssuer: []string{"https://bar", "https://baz"}, + idToken: `{"iss":"https://foo"}`, + config: Config{ + SkipClientIDCheck: true, + SkipExpiryCheck: true, + }, + signKey: newRSAKey(t), + wantErr: true, + }, { name: "skip issuer check", issuer: "https://bar", @@ -561,6 +573,8 @@ type verificationTest struct { // If not provided defaults to "https://foo" issuer string + alternativeIssuer []string + // JWT payload (just the claims). idToken string @@ -598,7 +612,7 @@ func (v verificationTest) runGetToken(t *testing.T) (*IDToken, error) { } else if v.signKey != nil { ks = &StaticKeySet{PublicKeys: []crypto.PublicKey{v.signKey.pub}} } - verifier := NewVerifier(issuer, ks, &v.config) + verifier := NewVerifier(issuer, ks, &v.config, v.alternativeIssuer...) return verifier.Verify(ctx, token) }