From 6550a1048e48105290a29483841a343771f14d3d Mon Sep 17 00:00:00 2001 From: Kegan Maher Date: Wed, 17 Jul 2024 15:57:57 +0000 Subject: [PATCH] feat(oauth): handle load_server_metadata errors --- benefits/oauth/redirects.py | 11 ++++++++++- benefits/oauth/views.py | 11 +++++------ tests/pytest/oauth/test_redirects.py | 23 ++++++++++++++++++++++- tests/pytest/oauth/test_views.py | 2 +- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/benefits/oauth/redirects.py b/benefits/oauth/redirects.py index 52753a07b..aa46edc9f 100644 --- a/benefits/oauth/redirects.py +++ b/benefits/oauth/redirects.py @@ -1,6 +1,10 @@ from django.shortcuts import redirect from django.utils.http import urlencode +import sentry_sdk + +ROUTE_SYSTEM_ERROR = "oauth:system-error" + def deauthorize_redirect(oauth_client, token, redirect_uri): """Helper implements OIDC signout via the `end_session_endpoint`.""" @@ -9,7 +13,12 @@ def deauthorize_redirect(oauth_client, token, redirect_uri): # See https://github.com/lepture/authlib/issues/331#issuecomment-827295954 for more # # The implementation here was adapted from the same ticket: https://github.com/lepture/authlib/issues/331#issue-838728145 - metadata = oauth_client.load_server_metadata() + try: + metadata = oauth_client.load_server_metadata() + except Exception as ex: + sentry_sdk.capture_exception(ex) + return redirect(ROUTE_SYSTEM_ERROR) + end_session_endpoint = metadata.get("end_session_endpoint") params = dict(id_token_hint=token, post_logout_redirect_uri=redirect_uri) diff --git a/benefits/oauth/views.py b/benefits/oauth/views.py index cadea4816..036337330 100644 --- a/benefits/oauth/views.py +++ b/benefits/oauth/views.py @@ -20,7 +20,6 @@ ROUTE_CONFIRM = "eligibility:confirm" ROUTE_UNVERIFIED = "eligibility:unverified" ROUTE_POST_LOGOUT = "oauth:post_logout" -ROUTE_SYSTEM_ERROR = "oauth:system-error" TEMPLATE_SYSTEM_ERROR = "oauth/system_error.html" @@ -44,7 +43,7 @@ def _oauth_client_or_error_redirect(auth_provider): if exception: sentry_sdk.capture_exception(exception) - return redirect(ROUTE_SYSTEM_ERROR) + return redirect(redirects.ROUTE_SYSTEM_ERROR) return oauth_client @@ -74,11 +73,11 @@ def login(request): result = oauth_client.authorize_redirect(request, redirect_uri) except Exception as ex: sentry_sdk.capture_exception(ex) - result = redirect(ROUTE_SYSTEM_ERROR) + result = redirect(redirects.ROUTE_SYSTEM_ERROR) if result.status_code >= 400: sentry_sdk.capture_message(f"authorize_redirect error response [{result.status_code}]: {result.content.decode()}") - result = redirect(ROUTE_SYSTEM_ERROR) + result = redirect(redirects.ROUTE_SYSTEM_ERROR) return result @@ -104,12 +103,12 @@ def authorize(request): token = oauth_client.authorize_access_token(request) except Exception as ex: sentry_sdk.capture_exception(ex) - return redirect(ROUTE_SYSTEM_ERROR) + return redirect(redirects.ROUTE_SYSTEM_ERROR) if token is None: logger.warning("Could not authorize OAuth access token") sentry_sdk.capture_message("oauth_client.authorize_access_token returned None") - return redirect(ROUTE_SYSTEM_ERROR) + return redirect(redirects.ROUTE_SYSTEM_ERROR) logger.debug("OAuth access token authorized") diff --git a/tests/pytest/oauth/test_redirects.py b/tests/pytest/oauth/test_redirects.py index 227e13a0c..09658ba92 100644 --- a/tests/pytest/oauth/test_redirects.py +++ b/tests/pytest/oauth/test_redirects.py @@ -1,4 +1,15 @@ -from benefits.oauth.redirects import deauthorize_redirect, generate_redirect_uri +from django.urls import reverse + + +from benefits.oauth.redirects import ROUTE_SYSTEM_ERROR, deauthorize_redirect, generate_redirect_uri +import benefits.oauth.redirects + +import pytest + + +@pytest.fixture +def mocked_sentry_sdk_module(mocker): + return mocker.patch.object(benefits.oauth.redirects, "sentry_sdk") def test_deauthorize_redirect(mocked_oauth_client): @@ -14,6 +25,16 @@ def test_deauthorize_redirect(mocked_oauth_client): ) +def test_deauthorize_redirect_load_server_metadata_error(mocked_oauth_client, mocked_sentry_sdk_module): + mocked_oauth_client.load_server_metadata.side_effect = Exception("Side effect") + + result = deauthorize_redirect(mocked_oauth_client, "token", "https://localhost/redirect_uri") + + assert result.status_code == 302 + assert result.url == reverse(ROUTE_SYSTEM_ERROR) + mocked_sentry_sdk_module.capture_exception.assert_called_once() + + def test_generate_redirect_uri_default(rf): request = rf.get("/oauth/login") path = "/test" diff --git a/tests/pytest/oauth/test_views.py b/tests/pytest/oauth/test_views.py index a8b37331b..4a6c77c98 100644 --- a/tests/pytest/oauth/test_views.py +++ b/tests/pytest/oauth/test_views.py @@ -8,9 +8,9 @@ from benefits.eligibility.views import ROUTE_START +from benefits.oauth.redirects import ROUTE_SYSTEM_ERROR from benefits.oauth.views import ( ROUTE_CONFIRM, - ROUTE_SYSTEM_ERROR, ROUTE_UNVERIFIED, TEMPLATE_SYSTEM_ERROR, _oauth_client_or_error_redirect,