Skip to content

Commit

Permalink
Merge pull request #432 from singingwolfboy/error-response
Browse files Browse the repository at this point in the history
When returning a Response from the oauth_error signal, use it
  • Loading branch information
singingwolfboy authored Mar 4, 2024
2 parents facc344 + 23a45eb commit 941fb2a
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Changelog
-------------
* Set ``auto_refresh_url`` automatically in ``make_azure_blueprint`` when the ``offline_access``
scope is included, thereby enabling automatic token refresh
* Allow returning a custom response from a ``oauth_error`` signal handler.

`7.0.1`_ (2024-01-05)
---------------------
Expand Down
10 changes: 10 additions & 0 deletions docs/signals.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,15 @@ The following signals exist in Flask-Dance:
anyway, so it is your responsibility to hook into this signal and inform
the user that there was an error.

You can also return a :class:`~flask.Response` instance from an event
subscriber. If you do, that response will be returned to the user instead
of the normal redirect. For example::

from flask import redirect, url_for

@oauth_error.connect
def handle_error(blueprint, error, error_description=None, error_uri=None):
return redirect(url_for("custom_error_page"))

.. _flash a message: http://flask.pocoo.org/docs/latest/patterns/flashing/
.. _blinker: http://pythonhosted.org/blinker/
6 changes: 5 additions & 1 deletion flask_dance/consumer/oauth2.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,13 @@ def authorized(self):
error_desc,
error_uri,
)
oauth_error.send(
results = oauth_error.send(
self, error=error, error_description=error_desc, error_uri=error_uri
)
if results:
for _, ret in results:
if isinstance(ret, (Response, current_app.response_class)):
return ret
return redirect(next_url)

state_key = f"{self.name}_oauth_state"
Expand Down
8 changes: 5 additions & 3 deletions flask_dance/contrib/slack.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ def make_slack_blueprint(
client_secret=client_secret,
scope=scope,
base_url="https://slack.com/api/",
authorization_url="https://slack.com/oauth/authorize"
if subdomain is None
else f"https://{subdomain}.slack.com/oauth/authorize",
authorization_url=(
"https://slack.com/oauth/authorize"
if subdomain is None
else f"https://{subdomain}.slack.com/oauth/authorize"
),
token_url="https://slack.com/api/oauth.access",
redirect_url=redirect_url,
redirect_to=redirect_to,
Expand Down
36 changes: 36 additions & 0 deletions tests/consumer/test_oauth2.py
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,42 @@ def callback(*args, **kwargs):
assert resp.status_code == 302


@requires_blinker
def test_signal_oauth_error_response(request):
app, bp = make_app()

calls = []

def callback(*args, **kwargs):
calls.append((args, kwargs))
return flask.redirect("/url")

oauth_error.connect(callback)
request.addfinalizer(lambda: oauth_error.disconnect(callback))

with app.test_client() as client:
resp = client.get(
"/login/test-service/authorized?"
"error=unauthorized_client&"
"error_description=Invalid+redirect+URI&"
"error_uri=https%3a%2f%2fexample.com%2fdocs%2fhelp",
base_url="https://a.b.c",
)

assert resp.status_code == 302
assert resp.headers["Location"] in ("/url", "http://localhost/url")

assert len(calls) == 1
assert calls[0] == (
(bp,),
{
"error": "unauthorized_client",
"error_description": "Invalid redirect URI",
"error_uri": "https://example.com/docs/help",
},
)


class CustomOAuth2Session(OAuth2Session):
my_attr = "foobar"

Expand Down

0 comments on commit 941fb2a

Please sign in to comment.