We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Improve checks in `oauth` `authorize` view
We ran into a case while deploying #862 where our `auth_provider` model is configured with a `scope`, but the response from the Auth server doesn't actually contain the scope (probably still being set up).

The `oauth` `authorize` function doesn't handle this case and raises an `AttributeError`
```
  File "/home/calitp/app/benefits/oauth/views.py", line 70, in authorize
    claim_flag = (userinfo.get(verifier_claim) if userinfo else "false").lower() == "true"
AttributeError: 'NoneType' object has no attribute 'lower'
```

Let's add a check and tests for this so that the logs for this are more helpful.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `benefits/oauth/views.py`
Content:
```
1 import logging
2 
3 from django.shortcuts import redirect
4 from django.urls import reverse
5 from django.utils.decorators import decorator_from_middleware
6 
7 from benefits.core import session
8 from benefits.core.middleware import VerifierSessionRequired
9 from . import analytics, redirects
10 from .client import oauth
11 
12 
13 logger = logging.getLogger(__name__)
14 
15 
16 ROUTE_AUTH = "oauth:authorize"
17 ROUTE_START = "eligibility:start"
18 ROUTE_CONFIRM = "eligibility:confirm"
19 ROUTE_UNVERIFIED = "eligibility:unverified"
20 ROUTE_POST_LOGOUT = "oauth:post_logout"
21 
22 
23 @decorator_from_middleware(VerifierSessionRequired)
24 def login(request):
25     """View implementing OIDC authorize_redirect."""
26     verifier = session.verifier(request)
27     oauth_client = oauth.create_client(verifier.auth_provider.client_name)
28 
29     if not oauth_client:
30         raise Exception(f"oauth_client not registered: {verifier.auth_provider.client_name}")
31 
32     route = reverse(ROUTE_AUTH)
33     redirect_uri = redirects.generate_redirect_uri(request, route)
34 
35     logger.debug(f"OAuth authorize_redirect with redirect_uri: {redirect_uri}")
36 
37     analytics.started_sign_in(request)
38 
39     return oauth_client.authorize_redirect(request, redirect_uri)
40 
41 
42 @decorator_from_middleware(VerifierSessionRequired)
43 def authorize(request):
44     """View implementing OIDC token authorization."""
45     verifier = session.verifier(request)
46     oauth_client = oauth.create_client(verifier.auth_provider.client_name)
47 
48     if not oauth_client:
49         raise Exception(f"oauth_client not registered: {verifier.auth_provider.client_name}")
50 
51     logger.debug("Attempting to authorize OAuth access token")
52     token = oauth_client.authorize_access_token(request)
53 
54     if token is None:
55         logger.warning("Could not authorize OAuth access token")
56         return redirect(ROUTE_START)
57 
58     logger.debug("OAuth access token authorized")
59 
60     # We store the id_token in the user's session. This is the minimal amount of information needed later to log the user out.
61     id_token = token["id_token"]
62 
63     # We store the returned claim in case it can be used later in eligibility verification.
64     verifier_claim = verifier.auth_provider.claim
65     stored_claim = None
66 
67     if verifier_claim:
68         userinfo = token.get("userinfo")
69         # the claim comes back in userinfo like { "claim": "True" | "False" }
70         claim_flag = (userinfo.get(verifier_claim) if userinfo else "false").lower() == "true"
71         # if userinfo contains our claim and the flag is true, store the *claim*
72         stored_claim = verifier_claim if claim_flag else None
73 
74     session.update(request, oauth_token=id_token, oauth_claim=stored_claim)
75 
76     analytics.finished_sign_in(request)
77 
78     return redirect(ROUTE_CONFIRM)
79 
80 
81 def cancel(request):
82     """View implementing cancellation of OIDC authorization."""
83 
84     analytics.canceled_sign_in(request)
85 
86     return redirect(ROUTE_UNVERIFIED)
87 
88 
89 @decorator_from_middleware(VerifierSessionRequired)
90 def logout(request):
91     """View implementing OIDC and application sign out."""
92     verifier = session.verifier(request)
93     oauth_client = oauth.create_client(verifier.auth_provider.client_name)
94 
95     if not oauth_client:
96         raise Exception(f"oauth_client not registered: {verifier.auth_provider.client_name}")
97 
98     analytics.started_sign_out(request)
99 
100     # overwrite the oauth session token, the user is signed out of the app
101     token = session.oauth_token(request)
102     session.logout(request)
103 
104     route = reverse(ROUTE_POST_LOGOUT)
105     redirect_uri = redirects.generate_redirect_uri(request, route)
106 
107     logger.debug(f"OAuth end_session_endpoint with redirect_uri: {redirect_uri}")
108 
109     # send the user through the end_session_endpoint, redirecting back to
110     # the post_logout route
111     return redirects.deauthorize_redirect(oauth_client, token, redirect_uri)
112 
113 
114 def post_logout(request):
115     """View routes the user to their origin after sign out."""
116 
117     analytics.finished_sign_out(request)
118 
119     origin = session.origin(request)
120     return redirect(origin)
121
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/benefits/oauth/views.py b/benefits/oauth/views.py
--- a/benefits/oauth/views.py
+++ b/benefits/oauth/views.py
@@ -66,10 +66,15 @@
 
     if verifier_claim:
         userinfo = token.get("userinfo")
-        # the claim comes back in userinfo like { "claim": "True" | "False" }
-        claim_flag = (userinfo.get(verifier_claim) if userinfo else "false").lower() == "true"
-        # if userinfo contains our claim and the flag is true, store the *claim*
-        stored_claim = verifier_claim if claim_flag else None
+
+        if userinfo:
+            claim_value = userinfo.get(verifier_claim)
+            # the claim comes back in userinfo like { "claim": "True" | "False" }
+            if claim_value is None:
+                logger.warning(f"userinfo did not contain: {verifier_claim}")
+            elif claim_value.lower() == "true":
+                # if userinfo contains our claim and the flag is true, store the *claim*
+                stored_claim = verifier_claim
 
     session.update(request, oauth_token=id_token, oauth_claim=stored_claim)

{"golden_diff": "diff --git a/benefits/oauth/views.py b/benefits/oauth/views.py\n--- a/benefits/oauth/views.py\n+++ b/benefits/oauth/views.py\n@@ -66,10 +66,15 @@\n \n     if verifier_claim:\n         userinfo = token.get(\"userinfo\")\n-        # the claim comes back in userinfo like { \"claim\": \"True\" | \"False\" }\n-        claim_flag = (userinfo.get(verifier_claim) if userinfo else \"false\").lower() == \"true\"\n-        # if userinfo contains our claim and the flag is true, store the *claim*\n-        stored_claim = verifier_claim if claim_flag else None\n+\n+        if userinfo:\n+            claim_value = userinfo.get(verifier_claim)\n+            # the claim comes back in userinfo like { \"claim\": \"True\" | \"False\" }\n+            if claim_value is None:\n+                logger.warning(f\"userinfo did not contain: {verifier_claim}\")\n+            elif claim_value.lower() == \"true\":\n+                # if userinfo contains our claim and the flag is true, store the *claim*\n+                stored_claim = verifier_claim\n \n     session.update(request, oauth_token=id_token, oauth_claim=stored_claim)\n", "issue": "Improve checks in `oauth` `authorize` view\nWe ran into a case while deploying #862 where our `auth_provider` model is configured with a `scope`, but the response from the Auth server doesn't actually contain the scope (probably still being set up).\r\n\r\nThe `oauth` `authorize` function doesn't handle this case and raises an `AttributeError`\r\n```\r\n  File \"/home/calitp/app/benefits/oauth/views.py\", line 70, in authorize\r\n    claim_flag = (userinfo.get(verifier_claim) if userinfo else \"false\").lower() == \"true\"\r\nAttributeError: 'NoneType' object has no attribute 'lower'\r\n```\r\n\r\nLet's add a check and tests for this so that the logs for this are more helpful.\n", "before_files": [{"content": "import logging\n\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.decorators import decorator_from_middleware\n\nfrom benefits.core import session\nfrom benefits.core.middleware import VerifierSessionRequired\nfrom . import analytics, redirects\nfrom .client import oauth\n\n\nlogger = logging.getLogger(__name__)\n\n\nROUTE_AUTH = \"oauth:authorize\"\nROUTE_START = \"eligibility:start\"\nROUTE_CONFIRM = \"eligibility:confirm\"\nROUTE_UNVERIFIED = \"eligibility:unverified\"\nROUTE_POST_LOGOUT = \"oauth:post_logout\"\n\n\n@decorator_from_middleware(VerifierSessionRequired)\ndef login(request):\n    \"\"\"View implementing OIDC authorize_redirect.\"\"\"\n    verifier = session.verifier(request)\n    oauth_client = oauth.create_client(verifier.auth_provider.client_name)\n\n    if not oauth_client:\n        raise Exception(f\"oauth_client not registered: {verifier.auth_provider.client_name}\")\n\n    route = reverse(ROUTE_AUTH)\n    redirect_uri = redirects.generate_redirect_uri(request, route)\n\n    logger.debug(f\"OAuth authorize_redirect with redirect_uri: {redirect_uri}\")\n\n    analytics.started_sign_in(request)\n\n    return oauth_client.authorize_redirect(request, redirect_uri)\n\n\n@decorator_from_middleware(VerifierSessionRequired)\ndef authorize(request):\n    \"\"\"View implementing OIDC token authorization.\"\"\"\n    verifier = session.verifier(request)\n    oauth_client = oauth.create_client(verifier.auth_provider.client_name)\n\n    if not oauth_client:\n        raise Exception(f\"oauth_client not registered: {verifier.auth_provider.client_name}\")\n\n    logger.debug(\"Attempting to authorize OAuth access token\")\n    token = oauth_client.authorize_access_token(request)\n\n    if token is None:\n        logger.warning(\"Could not authorize OAuth access token\")\n        return redirect(ROUTE_START)\n\n    logger.debug(\"OAuth access token authorized\")\n\n    # We store the id_token in the user's session. This is the minimal amount of information needed later to log the user out.\n    id_token = token[\"id_token\"]\n\n    # We store the returned claim in case it can be used later in eligibility verification.\n    verifier_claim = verifier.auth_provider.claim\n    stored_claim = None\n\n    if verifier_claim:\n        userinfo = token.get(\"userinfo\")\n        # the claim comes back in userinfo like { \"claim\": \"True\" | \"False\" }\n        claim_flag = (userinfo.get(verifier_claim) if userinfo else \"false\").lower() == \"true\"\n        # if userinfo contains our claim and the flag is true, store the *claim*\n        stored_claim = verifier_claim if claim_flag else None\n\n    session.update(request, oauth_token=id_token, oauth_claim=stored_claim)\n\n    analytics.finished_sign_in(request)\n\n    return redirect(ROUTE_CONFIRM)\n\n\ndef cancel(request):\n    \"\"\"View implementing cancellation of OIDC authorization.\"\"\"\n\n    analytics.canceled_sign_in(request)\n\n    return redirect(ROUTE_UNVERIFIED)\n\n\n@decorator_from_middleware(VerifierSessionRequired)\ndef logout(request):\n    \"\"\"View implementing OIDC and application sign out.\"\"\"\n    verifier = session.verifier(request)\n    oauth_client = oauth.create_client(verifier.auth_provider.client_name)\n\n    if not oauth_client:\n        raise Exception(f\"oauth_client not registered: {verifier.auth_provider.client_name}\")\n\n    analytics.started_sign_out(request)\n\n    # overwrite the oauth session token, the user is signed out of the app\n    token = session.oauth_token(request)\n    session.logout(request)\n\n    route = reverse(ROUTE_POST_LOGOUT)\n    redirect_uri = redirects.generate_redirect_uri(request, route)\n\n    logger.debug(f\"OAuth end_session_endpoint with redirect_uri: {redirect_uri}\")\n\n    # send the user through the end_session_endpoint, redirecting back to\n    # the post_logout route\n    return redirects.deauthorize_redirect(oauth_client, token, redirect_uri)\n\n\ndef post_logout(request):\n    \"\"\"View routes the user to their origin after sign out.\"\"\"\n\n    analytics.finished_sign_out(request)\n\n    origin = session.origin(request)\n    return redirect(origin)\n", "path": "benefits/oauth/views.py"}], "after_files": [{"content": "import logging\n\nfrom django.shortcuts import redirect\nfrom django.urls import reverse\nfrom django.utils.decorators import decorator_from_middleware\n\nfrom benefits.core import session\nfrom benefits.core.middleware import VerifierSessionRequired\nfrom . import analytics, redirects\nfrom .client import oauth\n\n\nlogger = logging.getLogger(__name__)\n\n\nROUTE_AUTH = \"oauth:authorize\"\nROUTE_START = \"eligibility:start\"\nROUTE_CONFIRM = \"eligibility:confirm\"\nROUTE_UNVERIFIED = \"eligibility:unverified\"\nROUTE_POST_LOGOUT = \"oauth:post_logout\"\n\n\n@decorator_from_middleware(VerifierSessionRequired)\ndef login(request):\n    \"\"\"View implementing OIDC authorize_redirect.\"\"\"\n    verifier = session.verifier(request)\n    oauth_client = oauth.create_client(verifier.auth_provider.client_name)\n\n    if not oauth_client:\n        raise Exception(f\"oauth_client not registered: {verifier.auth_provider.client_name}\")\n\n    route = reverse(ROUTE_AUTH)\n    redirect_uri = redirects.generate_redirect_uri(request, route)\n\n    logger.debug(f\"OAuth authorize_redirect with redirect_uri: {redirect_uri}\")\n\n    analytics.started_sign_in(request)\n\n    return oauth_client.authorize_redirect(request, redirect_uri)\n\n\n@decorator_from_middleware(VerifierSessionRequired)\ndef authorize(request):\n    \"\"\"View implementing OIDC token authorization.\"\"\"\n    verifier = session.verifier(request)\n    oauth_client = oauth.create_client(verifier.auth_provider.client_name)\n\n    if not oauth_client:\n        raise Exception(f\"oauth_client not registered: {verifier.auth_provider.client_name}\")\n\n    logger.debug(\"Attempting to authorize OAuth access token\")\n    token = oauth_client.authorize_access_token(request)\n\n    if token is None:\n        logger.warning(\"Could not authorize OAuth access token\")\n        return redirect(ROUTE_START)\n\n    logger.debug(\"OAuth access token authorized\")\n\n    # We store the id_token in the user's session. This is the minimal amount of information needed later to log the user out.\n    id_token = token[\"id_token\"]\n\n    # We store the returned claim in case it can be used later in eligibility verification.\n    verifier_claim = verifier.auth_provider.claim\n    stored_claim = None\n\n    if verifier_claim:\n        userinfo = token.get(\"userinfo\")\n\n        if userinfo:\n            claim_value = userinfo.get(verifier_claim)\n            # the claim comes back in userinfo like { \"claim\": \"True\" | \"False\" }\n            if claim_value is None:\n                logger.warning(f\"userinfo did not contain: {verifier_claim}\")\n            elif claim_value.lower() == \"true\":\n                # if userinfo contains our claim and the flag is true, store the *claim*\n                stored_claim = verifier_claim\n\n    session.update(request, oauth_token=id_token, oauth_claim=stored_claim)\n\n    analytics.finished_sign_in(request)\n\n    return redirect(ROUTE_CONFIRM)\n\n\ndef cancel(request):\n    \"\"\"View implementing cancellation of OIDC authorization.\"\"\"\n\n    analytics.canceled_sign_in(request)\n\n    return redirect(ROUTE_UNVERIFIED)\n\n\n@decorator_from_middleware(VerifierSessionRequired)\ndef logout(request):\n    \"\"\"View implementing OIDC and application sign out.\"\"\"\n    verifier = session.verifier(request)\n    oauth_client = oauth.create_client(verifier.auth_provider.client_name)\n\n    if not oauth_client:\n        raise Exception(f\"oauth_client not registered: {verifier.auth_provider.client_name}\")\n\n    analytics.started_sign_out(request)\n\n    # overwrite the oauth session token, the user is signed out of the app\n    token = session.oauth_token(request)\n    session.logout(request)\n\n    route = reverse(ROUTE_POST_LOGOUT)\n    redirect_uri = redirects.generate_redirect_uri(request, route)\n\n    logger.debug(f\"OAuth end_session_endpoint with redirect_uri: {redirect_uri}\")\n\n    # send the user through the end_session_endpoint, redirecting back to\n    # the post_logout route\n    return redirects.deauthorize_redirect(oauth_client, token, redirect_uri)\n\n\ndef post_logout(request):\n    \"\"\"View routes the user to their origin after sign out.\"\"\"\n\n    analytics.finished_sign_out(request)\n\n    origin = session.origin(request)\n    return redirect(origin)\n", "path": "benefits/oauth/views.py"}]}