Skip to content

Commit d42db69

Browse files
authored
Merge pull request #315 from ericchiang/issuer
oidc: add option to override discovered issuer URL
2 parents 15b94d9 + 916b64f commit d42db69

File tree

2 files changed

+63
-10
lines changed

2 files changed

+63
-10
lines changed

oidc/oidc.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ var (
3939
errInvalidAtHash = errors.New("access token hash does not match value in ID token")
4040
)
4141

42+
type contextKey int
43+
44+
var issuerURLKey contextKey
45+
4246
// ClientContext returns a new Context that carries the provided HTTP client.
4347
//
4448
// This method sets the same context key used by the golang.org/x/oauth2 package,
@@ -65,6 +69,25 @@ func cloneContext(ctx context.Context) context.Context {
6569
return cp
6670
}
6771

72+
// InsecureIssuerURLContext allows discovery to work when the issuer_url reported
73+
// by upstream is mismatched with the discovery URL. This is meant for integration
74+
// with off-spec providers such as Azure.
75+
//
76+
// discoveryBaseURL := "https://login.microsoftonline.com/organizations/v2.0"
77+
// issuerURL := "https://login.microsoftonline.com/my-tenantid/v2.0"
78+
//
79+
// ctx := oidc.InsecureIssuerURLContext(parentContext, issuerURL)
80+
//
81+
// // Provider will be discovered with the discoveryBaseURL, but use issuerURL
82+
// // for future issuer validation.
83+
// provider, err := oidc.NewProvider(ctx, discoveryBaseURL)
84+
//
85+
// This is insecure because validating the correct issuer is critical for multi-tenant
86+
// proivders. Any overrides here MUST be carefully reviewed.
87+
func InsecureIssuerURLContext(ctx context.Context, issuerURL string) context.Context {
88+
return context.WithValue(ctx, issuerURLKey, issuerURL)
89+
}
90+
6891
func doRequest(ctx context.Context, req *http.Request) (*http.Response, error) {
6992
client := http.DefaultClient
7093
if c, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok {
@@ -142,7 +165,11 @@ func NewProvider(ctx context.Context, issuer string) (*Provider, error) {
142165
return nil, fmt.Errorf("oidc: failed to decode provider discovery object: %v", err)
143166
}
144167

145-
if p.Issuer != issuer {
168+
issuerURL, skipIssuerValidation := ctx.Value(issuerURLKey).(string)
169+
if !skipIssuerValidation {
170+
issuerURL = issuer
171+
}
172+
if p.Issuer != issuerURL && !skipIssuerValidation {
146173
return nil, fmt.Errorf("oidc: issuer did not match the issuer returned by provider, expected %q got %q", issuer, p.Issuer)
147174
}
148175
var algs []string
@@ -152,7 +179,7 @@ func NewProvider(ctx context.Context, issuer string) (*Provider, error) {
152179
}
153180
}
154181
return &Provider{
155-
issuer: p.Issuer,
182+
issuer: issuerURL,
156183
authURL: p.AuthURL,
157184
tokenURL: p.TokenURL,
158185
userInfoURL: p.UserInfoURL,

oidc/oidc_test.go

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,16 @@ func TestAccessTokenVerification(t *testing.T) {
104104

105105
func TestNewProvider(t *testing.T) {
106106
tests := []struct {
107-
name string
108-
data string
109-
trailingSlash bool
110-
wantAuthURL string
111-
wantTokenURL string
112-
wantUserInfoURL string
113-
wantAlgorithms []string
114-
wantErr bool
107+
name string
108+
data string
109+
issuerURLOverride string
110+
trailingSlash bool
111+
wantAuthURL string
112+
wantTokenURL string
113+
wantUserInfoURL string
114+
wantIssuerURL string
115+
wantAlgorithms []string
116+
wantErr bool
115117
}{
116118
{
117119
name: "basic_case",
@@ -165,6 +167,21 @@ func TestNewProvider(t *testing.T) {
165167
}`,
166168
wantErr: true,
167169
},
170+
{
171+
name: "mismatched_issuer_discovery_override",
172+
issuerURLOverride: "https://example.com",
173+
data: `{
174+
"issuer": "ISSUER",
175+
"authorization_endpoint": "https://example.com/auth",
176+
"token_endpoint": "https://example.com/token",
177+
"jwks_uri": "https://example.com/keys",
178+
"id_token_signing_alg_values_supported": ["RS256"]
179+
}`,
180+
wantIssuerURL: "https://example.com",
181+
wantAuthURL: "https://example.com/auth",
182+
wantTokenURL: "https://example.com/token",
183+
wantAlgorithms: []string{"RS256"},
184+
},
168185
{
169186
name: "issuer_with_trailing_slash",
170187
data: `{
@@ -269,6 +286,10 @@ func TestNewProvider(t *testing.T) {
269286
issuer += "/"
270287
}
271288

289+
if test.issuerURLOverride != "" {
290+
ctx = InsecureIssuerURLContext(ctx, test.issuerURLOverride)
291+
}
292+
272293
p, err := NewProvider(ctx, issuer)
273294
if err != nil {
274295
if !test.wantErr {
@@ -280,6 +301,11 @@ func TestNewProvider(t *testing.T) {
280301
t.Fatalf("NewProvider(): expected error")
281302
}
282303

304+
if test.wantIssuerURL != "" && p.issuer != test.wantIssuerURL {
305+
t.Errorf("NewProvider() unexpected issuer value, got=%s, want=%s",
306+
p.issuer, test.wantIssuerURL)
307+
}
308+
283309
if p.authURL != test.wantAuthURL {
284310
t.Errorf("NewProvider() unexpected authURL value, got=%s, want=%s",
285311
p.authURL, test.wantAuthURL)

0 commit comments

Comments
 (0)