Skip to content

Commit f4652d6

Browse files
fix: openid ignores missing redirect uri
Fixes an issue where Authorize Requests which were intended for an OpenID Connect 1.0 client would incorrectly be allowed when missing the redirect URI when it's required by the specification.
1 parent 1721ca3 commit f4652d6

File tree

3 files changed

+56
-9
lines changed

3 files changed

+56
-9
lines changed

authorize_request_handler.go

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,17 @@ func (f *Fosite) validateAuthorizeRedirectURI(_ *http.Request, request *Authoriz
161161
// Fetch redirect URI from request
162162
rawRedirURI := request.Form.Get("redirect_uri")
163163

164+
// This ensures that the 'redirect_uri' parameter is present for OpenID Connect 1.0 authorization requests as per:
165+
//
166+
// Authorization Code Flow - https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
167+
// Implicit Flow - https://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthRequest
168+
// Hybrid Flow - https://openid.net/specs/openid-connect-core-1_0.html#HybridAuthRequest
169+
//
170+
// Note: as per the Hybrid Flow documentation the Hybrid Flow has the same requirements as the Authorization Code Flow.
171+
if len(rawRedirURI) == 0 && request.GetRequestedScopes().Has("openid") {
172+
return errorsx.WithStack(ErrInvalidRequest.WithHint("The 'redirect_uri' parameter is required when using OpenID Connect 1.0."))
173+
}
174+
164175
// Validate redirect uri
165176
redirectURI, err := MatchRedirectURIWithClientRedirectURIs(rawRedirURI, request.Client)
166177
if err != nil {
@@ -172,14 +183,18 @@ func (f *Fosite) validateAuthorizeRedirectURI(_ *http.Request, request *Authoriz
172183
return nil
173184
}
174185

186+
func (f *Fosite) parseAuthorizeScope(_ *http.Request, request *AuthorizeRequest) error {
187+
request.SetRequestedScopes(RemoveEmpty(strings.Split(request.Form.Get("scope"), " ")))
188+
189+
return nil
190+
}
191+
175192
func (f *Fosite) validateAuthorizeScope(ctx context.Context, _ *http.Request, request *AuthorizeRequest) error {
176-
scope := RemoveEmpty(strings.Split(request.Form.Get("scope"), " "))
177-
for _, permission := range scope {
193+
for _, permission := range request.GetRequestedScopes() {
178194
if !f.Config.GetScopeStrategy(ctx)(request.Client.GetScopes(), permission) {
179195
return errorsx.WithStack(ErrInvalidScope.WithHintf("The OAuth 2.0 Client is not allowed to request scope '%s'.", permission))
180196
}
181197
}
182-
request.SetRequestedScopes(scope)
183198

184199
return nil
185200
}
@@ -356,27 +371,31 @@ func (f *Fosite) newAuthorizeRequest(ctx context.Context, r *http.Request, isPAR
356371
return request, err
357372
}
358373

359-
if err := f.validateAuthorizeRedirectURI(r, request); err != nil {
374+
if err = f.parseAuthorizeScope(r, request); err != nil {
375+
return request, err
376+
}
377+
378+
if err = f.validateAuthorizeRedirectURI(r, request); err != nil {
360379
return request, err
361380
}
362381

363-
if err := f.validateAuthorizeScope(ctx, r, request); err != nil {
382+
if err = f.validateAuthorizeScope(ctx, r, request); err != nil {
364383
return request, err
365384
}
366385

367-
if err := f.validateAuthorizeAudience(ctx, r, request); err != nil {
386+
if err = f.validateAuthorizeAudience(ctx, r, request); err != nil {
368387
return request, err
369388
}
370389

371390
if len(request.Form.Get("registration")) > 0 {
372391
return request, errorsx.WithStack(ErrRegistrationNotSupported)
373392
}
374393

375-
if err := f.validateResponseTypes(r, request); err != nil {
394+
if err = f.validateResponseTypes(r, request); err != nil {
376395
return request, err
377396
}
378397

379-
if err := f.validateResponseMode(r, request); err != nil {
398+
if err = f.validateResponseMode(r, request); err != nil {
380399
return request, err
381400
}
382401

integration/oidc_explicit_test.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ func TestOpenIDConnectExplicitFlow(t *testing.T) {
5555
},
5656
authStatusCode: http.StatusOK,
5757
},
58+
{
59+
session: newIDSession(&jwt.IDTokenClaims{Subject: "peter"}),
60+
description: "should fail registered single redirect uri but no redirect uri in request",
61+
setup: func(oauthClient *oauth2.Config) string {
62+
oauthClient.Scopes = []string{"openid"}
63+
oauthClient.RedirectURL = ""
64+
65+
return oauthClient.AuthCodeURL("12345678901234567890") + "&nonce=11234123"
66+
},
67+
authStatusCode: http.StatusBadRequest,
68+
expectAuthErr: `{"error":"invalid_request","error_description":"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The 'redirect_uri' parameter is required when using OpenID Connect 1.0."}`,
69+
},
5870
{
5971
session: newIDSession(&jwt.IDTokenClaims{Subject: "peter"}),
6072
description: "should fail because nonce is not long enough",
@@ -88,6 +100,21 @@ func TestOpenIDConnectExplicitFlow(t *testing.T) {
88100
},
89101
authStatusCode: http.StatusOK,
90102
},
103+
{
104+
session: newIDSession(&jwt.IDTokenClaims{
105+
Subject: "peter",
106+
RequestedAt: time.Now().UTC(),
107+
AuthTime: time.Now().Add(time.Second).UTC(),
108+
}),
109+
description: "should not pass missing redirect uri",
110+
setup: func(oauthClient *oauth2.Config) string {
111+
oauthClient.RedirectURL = ""
112+
oauthClient.Scopes = []string{"openid"}
113+
return oauthClient.AuthCodeURL("12345678901234567890") + "&nonce=1234567890&prompt=login"
114+
},
115+
expectAuthErr: `{"error":"invalid_request","error_description":"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The 'redirect_uri' parameter is required when using OpenID Connect 1.0."}`,
116+
authStatusCode: http.StatusBadRequest,
117+
},
91118
{
92119
session: newIDSession(&jwt.IDTokenClaims{
93120
Subject: "peter",
@@ -121,7 +148,7 @@ func TestOpenIDConnectExplicitFlow(t *testing.T) {
121148
defer ts.Close()
122149

123150
oauthClient := newOAuth2Client(ts)
124-
store.Clients["my-client"].(*goauth2.DefaultClient).RedirectURIs[0] = ts.URL + "/callback"
151+
store.Clients["my-client"].(*goauth2.DefaultClient).RedirectURIs = []string{ts.URL + "/callback"}
125152

126153
resp, err := http.Get(c.setup(oauthClient))
127154
require.NoError(t, err)

integration/oidc_implicit_hybrid_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ func TestOIDCImplicitFlow(t *testing.T) {
107107
var callbackURL *url.URL
108108

109109
authURL := strings.Replace(oauthClient.AuthCodeURL(state), "response_type=code", "response_type="+c.responseType, -1) + "&nonce=" + c.nonce
110+
110111
client := &http.Client{
111112
CheckRedirect: func(req *http.Request, via []*http.Request) error {
112113
callbackURL = req.URL

0 commit comments

Comments
 (0)