Skip to content

Commit 9dfde46

Browse files
authored
Merge pull request #385 from PCMan/pcman@preserve_state_on_error_according_to_rfc6749
Bug fix: Preserve "state" parameter in the oauth2 handler when non-critical errors happen as required in RFC 6749.
2 parents 3735210 + 7729d6e commit 9dfde46

File tree

2 files changed

+24
-3
lines changed

2 files changed

+24
-3
lines changed

flask_oauthlib/provider/oauth2.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,10 @@ def decorated(*args, **kwargs):
398398
return redirect(e.in_uri(self.error_uri))
399399
except oauth2.OAuth2Error as e:
400400
log.debug('OAuth2Error: %r', e, exc_info=True)
401+
# on auth error, we should preserve state if it's present according to RFC 6749
402+
state = request.values.get('state')
403+
if state and not e.state:
404+
e.state = state # set e.state so e.in_uri() can add the state query parameter to redirect uri
401405
return redirect(e.in_uri(redirect_uri))
402406
except Exception as e:
403407
log.exception(e)
@@ -417,6 +421,10 @@ def decorated(*args, **kwargs):
417421
return redirect(e.in_uri(self.error_uri))
418422
except oauth2.OAuth2Error as e:
419423
log.debug('OAuth2Error: %r', e, exc_info=True)
424+
# on auth error, we should preserve state if it's present according to RFC 6749
425+
state = request.values.get('state')
426+
if state and not e.state:
427+
e.state = state # set e.state so e.in_uri() can add the state query parameter to redirect uri
420428
return redirect(e.in_uri(redirect_uri))
421429

422430
if not isinstance(rv, bool):
@@ -425,7 +433,7 @@ def decorated(*args, **kwargs):
425433

426434
if not rv:
427435
# denied by user
428-
e = oauth2.AccessDeniedError()
436+
e = oauth2.AccessDeniedError(state=request.values.get('state'))
429437
return redirect(e.in_uri(redirect_uri))
430438
return self.confirm_authorization_request()
431439
return decorated
@@ -456,6 +464,10 @@ def confirm_authorization_request(self):
456464
return redirect(e.in_uri(self.error_uri))
457465
except oauth2.OAuth2Error as e:
458466
log.debug('OAuth2Error: %r', e, exc_info=True)
467+
# on auth error, we should preserve state if it's present according to RFC 6749
468+
state = request.values.get('state')
469+
if state and not e.state:
470+
e.state = state # set e.state so e.in_uri() can add the state query parameter to redirect uri
459471
return redirect(e.in_uri(redirect_uri or self.error_uri))
460472
except Exception as e:
461473
log.exception(e)

tests/oauth2/test_oauth2.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,21 @@ def test_oauth_authorize_valid_url(self):
8181
assert 'code=' in rv.location
8282
assert 'state' not in rv.location
8383

84-
# test state
84+
# test state on access denied
85+
# According to RFC 6749, state should be preserved on error response if it's present in the client request.
86+
# Reference: https://tools.ietf.org/html/rfc6749#section-4.1.2
87+
rv = self.client.post(authorize_url + '&state=foo', data=dict(
88+
confirm='no'
89+
))
90+
assert 'error=access_denied' in rv.location
91+
assert 'state=foo' in rv.location
92+
93+
# test state on success
8594
rv = self.client.post(authorize_url + '&state=foo', data=dict(
8695
confirm='yes'
8796
))
8897
assert 'code=' in rv.location
89-
assert 'state' in rv.location
98+
assert 'state=foo' in rv.location
9099

91100
def test_http_head_oauth_authorize_valid_url(self):
92101
rv = self.client.head(authorize_url)

0 commit comments

Comments
 (0)