We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
bug(auth): requests to the api with an invalid token receive a response status 500 instead of 401


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `timed/authentication.py`
Content:
```
1 import base64
2 import functools
3 import hashlib
4 
5 import requests
6 from django.conf import settings
7 from django.core.cache import cache
8 from django.core.exceptions import SuspiciousOperation
9 from django.utils.encoding import force_bytes
10 from mozilla_django_oidc.auth import LOGGER, OIDCAuthenticationBackend
11 
12 
13 class TimedOIDCAuthenticationBackend(OIDCAuthenticationBackend):
14     def get_introspection(self, access_token, id_token, payload):
15         """Return user details dictionary."""
16 
17         basic = base64.b64encode(
18             f"{settings.OIDC_RP_INTROSPECT_CLIENT_ID}:{settings.OIDC_RP_INTROSPECT_CLIENT_SECRET}".encode(
19                 "utf-8"
20             )
21         ).decode()
22         headers = {
23             "Authorization": f"Basic {basic}",
24             "Content-Type": "application/x-www-form-urlencoded",
25         }
26         response = requests.post(
27             settings.OIDC_OP_INTROSPECT_ENDPOINT,
28             verify=settings.OIDC_VERIFY_SSL,
29             headers=headers,
30             data={"token": access_token},
31         )
32         response.raise_for_status()
33         return response.json()
34 
35     def get_userinfo_or_introspection(self, access_token):
36         try:
37             claims = self.cached_request(
38                 self.get_userinfo, access_token, "auth.userinfo"
39             )
40         except requests.HTTPError as e:
41             if not (
42                 e.response.status_code in [401, 403] and settings.OIDC_CHECK_INTROSPECT
43             ):
44                 raise e
45 
46             # check introspection if userinfo fails (confidental client)
47             claims = self.cached_request(
48                 self.get_introspection, access_token, "auth.introspection"
49             )
50             if "client_id" not in claims:
51                 raise SuspiciousOperation("client_id not present in introspection")
52 
53         return claims
54 
55     def get_or_create_user(self, access_token, id_token, payload):
56         """Verify claims and return user, otherwise raise an Exception."""
57 
58         claims = self.get_userinfo_or_introspection(access_token)
59 
60         users = self.filter_users_by_claims(claims)
61 
62         if len(users) == 1:
63             user = users.get()
64             self.update_user_from_claims(user, claims)
65             return user
66         elif settings.OIDC_CREATE_USER:
67             return self.create_user(claims)
68         else:
69             LOGGER.debug(
70                 "Login failed: No user with username %s found, and "
71                 "OIDC_CREATE_USER is False",
72                 self.get_username(claims),
73             )
74             return None
75 
76     def update_user_from_claims(self, user, claims):
77         user.email = claims.get(settings.OIDC_EMAIL_CLAIM, "")
78         user.first_name = claims.get(settings.OIDC_FIRSTNAME_CLAIM, "")
79         user.last_name = claims.get(settings.OIDC_LASTNAME_CLAIM, "")
80         user.save()
81 
82     def filter_users_by_claims(self, claims):
83         username = self.get_username(claims)
84         return self.UserModel.objects.filter(username__iexact=username)
85 
86     def cached_request(self, method, token, cache_prefix):
87         token_hash = hashlib.sha256(force_bytes(token)).hexdigest()
88 
89         func = functools.partial(method, token, None, None)
90 
91         return cache.get_or_set(
92             f"{cache_prefix}.{token_hash}",
93             func,
94             timeout=settings.OIDC_BEARER_TOKEN_REVALIDATION_TIME,
95         )
96 
97     def create_user(self, claims):
98         """Return object for a newly created user account."""
99 
100         username = self.get_username(claims)
101         email = claims.get(settings.OIDC_EMAIL_CLAIM, "")
102         first_name = claims.get(settings.OIDC_FIRSTNAME_CLAIM, "")
103         last_name = claims.get(settings.OIDC_LASTNAME_CLAIM, "")
104 
105         return self.UserModel.objects.create(
106             username=username, email=email, first_name=first_name, last_name=last_name
107         )
108 
109     def get_username(self, claims):
110         try:
111             return claims[settings.OIDC_USERNAME_CLAIM]
112         except KeyError:
113             raise SuspiciousOperation("Couldn't find username claim")
114
```
--- 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/timed/authentication.py b/timed/authentication.py
--- a/timed/authentication.py
+++ b/timed/authentication.py
@@ -8,6 +8,7 @@
 from django.core.exceptions import SuspiciousOperation
 from django.utils.encoding import force_bytes
 from mozilla_django_oidc.auth import LOGGER, OIDCAuthenticationBackend
+from rest_framework.exceptions import AuthenticationFailed
 
 
 class TimedOIDCAuthenticationBackend(OIDCAuthenticationBackend):
@@ -37,20 +38,29 @@
             claims = self.cached_request(
                 self.get_userinfo, access_token, "auth.userinfo"
             )
+            return claims
         except requests.HTTPError as e:
-            if not (
-                e.response.status_code in [401, 403] and settings.OIDC_CHECK_INTROSPECT
-            ):
+            if e.response.status_code not in [401, 403]:
                 raise e
-
-            # check introspection if userinfo fails (confidental client)
-            claims = self.cached_request(
-                self.get_introspection, access_token, "auth.introspection"
-            )
-            if "client_id" not in claims:
-                raise SuspiciousOperation("client_id not present in introspection")
-
-        return claims
+            if settings.OIDC_CHECK_INTROSPECT:
+                try:
+                    # check introspection if userinfo fails (confidential client)
+                    claims = self.cached_request(
+                        self.get_introspection, access_token, "auth.introspection"
+                    )
+                    if "client_id" not in claims:
+                        raise SuspiciousOperation(
+                            "client_id not present in introspection"
+                        )
+                    return claims
+                except requests.HTTPError as e:
+                    # if the authorization fails it's not a valid client or
+                    # the token is expired and permission is denied.
+                    # Handing on the 401 Client Error would be transformed into
+                    # a 500 by Django's exception handling. But that's not what we want.
+                    if e.response.status_code not in [401, 403]:  # pragma: no cover
+                        raise e
+            raise AuthenticationFailed()
 
     def get_or_create_user(self, access_token, id_token, payload):
         """Verify claims and return user, otherwise raise an Exception."""

{"golden_diff": "diff --git a/timed/authentication.py b/timed/authentication.py\n--- a/timed/authentication.py\n+++ b/timed/authentication.py\n@@ -8,6 +8,7 @@\n from django.core.exceptions import SuspiciousOperation\n from django.utils.encoding import force_bytes\n from mozilla_django_oidc.auth import LOGGER, OIDCAuthenticationBackend\n+from rest_framework.exceptions import AuthenticationFailed\n \n \n class TimedOIDCAuthenticationBackend(OIDCAuthenticationBackend):\n@@ -37,20 +38,29 @@\n             claims = self.cached_request(\n                 self.get_userinfo, access_token, \"auth.userinfo\"\n             )\n+            return claims\n         except requests.HTTPError as e:\n-            if not (\n-                e.response.status_code in [401, 403] and settings.OIDC_CHECK_INTROSPECT\n-            ):\n+            if e.response.status_code not in [401, 403]:\n                 raise e\n-\n-            # check introspection if userinfo fails (confidental client)\n-            claims = self.cached_request(\n-                self.get_introspection, access_token, \"auth.introspection\"\n-            )\n-            if \"client_id\" not in claims:\n-                raise SuspiciousOperation(\"client_id not present in introspection\")\n-\n-        return claims\n+            if settings.OIDC_CHECK_INTROSPECT:\n+                try:\n+                    # check introspection if userinfo fails (confidential client)\n+                    claims = self.cached_request(\n+                        self.get_introspection, access_token, \"auth.introspection\"\n+                    )\n+                    if \"client_id\" not in claims:\n+                        raise SuspiciousOperation(\n+                            \"client_id not present in introspection\"\n+                        )\n+                    return claims\n+                except requests.HTTPError as e:\n+                    # if the authorization fails it's not a valid client or\n+                    # the token is expired and permission is denied.\n+                    # Handing on the 401 Client Error would be transformed into\n+                    # a 500 by Django's exception handling. But that's not what we want.\n+                    if e.response.status_code not in [401, 403]:  # pragma: no cover\n+                        raise e\n+            raise AuthenticationFailed()\n \n     def get_or_create_user(self, access_token, id_token, payload):\n         \"\"\"Verify claims and return user, otherwise raise an Exception.\"\"\"\n", "issue": "bug(auth): requests to the api with an invalid token receive a response status 500 instead of 401\n\n", "before_files": [{"content": "import base64\nimport functools\nimport hashlib\n\nimport requests\nfrom django.conf import settings\nfrom django.core.cache import cache\nfrom django.core.exceptions import SuspiciousOperation\nfrom django.utils.encoding import force_bytes\nfrom mozilla_django_oidc.auth import LOGGER, OIDCAuthenticationBackend\n\n\nclass TimedOIDCAuthenticationBackend(OIDCAuthenticationBackend):\n    def get_introspection(self, access_token, id_token, payload):\n        \"\"\"Return user details dictionary.\"\"\"\n\n        basic = base64.b64encode(\n            f\"{settings.OIDC_RP_INTROSPECT_CLIENT_ID}:{settings.OIDC_RP_INTROSPECT_CLIENT_SECRET}\".encode(\n                \"utf-8\"\n            )\n        ).decode()\n        headers = {\n            \"Authorization\": f\"Basic {basic}\",\n            \"Content-Type\": \"application/x-www-form-urlencoded\",\n        }\n        response = requests.post(\n            settings.OIDC_OP_INTROSPECT_ENDPOINT,\n            verify=settings.OIDC_VERIFY_SSL,\n            headers=headers,\n            data={\"token\": access_token},\n        )\n        response.raise_for_status()\n        return response.json()\n\n    def get_userinfo_or_introspection(self, access_token):\n        try:\n            claims = self.cached_request(\n                self.get_userinfo, access_token, \"auth.userinfo\"\n            )\n        except requests.HTTPError as e:\n            if not (\n                e.response.status_code in [401, 403] and settings.OIDC_CHECK_INTROSPECT\n            ):\n                raise e\n\n            # check introspection if userinfo fails (confidental client)\n            claims = self.cached_request(\n                self.get_introspection, access_token, \"auth.introspection\"\n            )\n            if \"client_id\" not in claims:\n                raise SuspiciousOperation(\"client_id not present in introspection\")\n\n        return claims\n\n    def get_or_create_user(self, access_token, id_token, payload):\n        \"\"\"Verify claims and return user, otherwise raise an Exception.\"\"\"\n\n        claims = self.get_userinfo_or_introspection(access_token)\n\n        users = self.filter_users_by_claims(claims)\n\n        if len(users) == 1:\n            user = users.get()\n            self.update_user_from_claims(user, claims)\n            return user\n        elif settings.OIDC_CREATE_USER:\n            return self.create_user(claims)\n        else:\n            LOGGER.debug(\n                \"Login failed: No user with username %s found, and \"\n                \"OIDC_CREATE_USER is False\",\n                self.get_username(claims),\n            )\n            return None\n\n    def update_user_from_claims(self, user, claims):\n        user.email = claims.get(settings.OIDC_EMAIL_CLAIM, \"\")\n        user.first_name = claims.get(settings.OIDC_FIRSTNAME_CLAIM, \"\")\n        user.last_name = claims.get(settings.OIDC_LASTNAME_CLAIM, \"\")\n        user.save()\n\n    def filter_users_by_claims(self, claims):\n        username = self.get_username(claims)\n        return self.UserModel.objects.filter(username__iexact=username)\n\n    def cached_request(self, method, token, cache_prefix):\n        token_hash = hashlib.sha256(force_bytes(token)).hexdigest()\n\n        func = functools.partial(method, token, None, None)\n\n        return cache.get_or_set(\n            f\"{cache_prefix}.{token_hash}\",\n            func,\n            timeout=settings.OIDC_BEARER_TOKEN_REVALIDATION_TIME,\n        )\n\n    def create_user(self, claims):\n        \"\"\"Return object for a newly created user account.\"\"\"\n\n        username = self.get_username(claims)\n        email = claims.get(settings.OIDC_EMAIL_CLAIM, \"\")\n        first_name = claims.get(settings.OIDC_FIRSTNAME_CLAIM, \"\")\n        last_name = claims.get(settings.OIDC_LASTNAME_CLAIM, \"\")\n\n        return self.UserModel.objects.create(\n            username=username, email=email, first_name=first_name, last_name=last_name\n        )\n\n    def get_username(self, claims):\n        try:\n            return claims[settings.OIDC_USERNAME_CLAIM]\n        except KeyError:\n            raise SuspiciousOperation(\"Couldn't find username claim\")\n", "path": "timed/authentication.py"}], "after_files": [{"content": "import base64\nimport functools\nimport hashlib\n\nimport requests\nfrom django.conf import settings\nfrom django.core.cache import cache\nfrom django.core.exceptions import SuspiciousOperation\nfrom django.utils.encoding import force_bytes\nfrom mozilla_django_oidc.auth import LOGGER, OIDCAuthenticationBackend\nfrom rest_framework.exceptions import AuthenticationFailed\n\n\nclass TimedOIDCAuthenticationBackend(OIDCAuthenticationBackend):\n    def get_introspection(self, access_token, id_token, payload):\n        \"\"\"Return user details dictionary.\"\"\"\n\n        basic = base64.b64encode(\n            f\"{settings.OIDC_RP_INTROSPECT_CLIENT_ID}:{settings.OIDC_RP_INTROSPECT_CLIENT_SECRET}\".encode(\n                \"utf-8\"\n            )\n        ).decode()\n        headers = {\n            \"Authorization\": f\"Basic {basic}\",\n            \"Content-Type\": \"application/x-www-form-urlencoded\",\n        }\n        response = requests.post(\n            settings.OIDC_OP_INTROSPECT_ENDPOINT,\n            verify=settings.OIDC_VERIFY_SSL,\n            headers=headers,\n            data={\"token\": access_token},\n        )\n        response.raise_for_status()\n        return response.json()\n\n    def get_userinfo_or_introspection(self, access_token):\n        try:\n            claims = self.cached_request(\n                self.get_userinfo, access_token, \"auth.userinfo\"\n            )\n            return claims\n        except requests.HTTPError as e:\n            if e.response.status_code not in [401, 403]:\n                raise e\n            if settings.OIDC_CHECK_INTROSPECT:\n                try:\n                    # check introspection if userinfo fails (confidential client)\n                    claims = self.cached_request(\n                        self.get_introspection, access_token, \"auth.introspection\"\n                    )\n                    if \"client_id\" not in claims:\n                        raise SuspiciousOperation(\n                            \"client_id not present in introspection\"\n                        )\n                    return claims\n                except requests.HTTPError as e:\n                    # if the authorization fails it's not a valid client or\n                    # the token is expired and permission is denied.\n                    # Handing on the 401 Client Error would be transformed into\n                    # a 500 by Django's exception handling. But that's not what we want.\n                    if e.response.status_code not in [401, 403]:  # pragma: no cover\n                        raise e\n            raise AuthenticationFailed()\n\n    def get_or_create_user(self, access_token, id_token, payload):\n        \"\"\"Verify claims and return user, otherwise raise an Exception.\"\"\"\n\n        claims = self.get_userinfo_or_introspection(access_token)\n\n        users = self.filter_users_by_claims(claims)\n\n        if len(users) == 1:\n            user = users.get()\n            self.update_user_from_claims(user, claims)\n            return user\n        elif settings.OIDC_CREATE_USER:\n            return self.create_user(claims)\n        else:\n            LOGGER.debug(\n                \"Login failed: No user with username %s found, and \"\n                \"OIDC_CREATE_USER is False\",\n                self.get_username(claims),\n            )\n            return None\n\n    def update_user_from_claims(self, user, claims):\n        user.email = claims.get(settings.OIDC_EMAIL_CLAIM, \"\")\n        user.first_name = claims.get(settings.OIDC_FIRSTNAME_CLAIM, \"\")\n        user.last_name = claims.get(settings.OIDC_LASTNAME_CLAIM, \"\")\n        user.save()\n\n    def filter_users_by_claims(self, claims):\n        username = self.get_username(claims)\n        return self.UserModel.objects.filter(username__iexact=username)\n\n    def cached_request(self, method, token, cache_prefix):\n        token_hash = hashlib.sha256(force_bytes(token)).hexdigest()\n\n        func = functools.partial(method, token, None, None)\n\n        return cache.get_or_set(\n            f\"{cache_prefix}.{token_hash}\",\n            func,\n            timeout=settings.OIDC_BEARER_TOKEN_REVALIDATION_TIME,\n        )\n\n    def create_user(self, claims):\n        \"\"\"Return object for a newly created user account.\"\"\"\n\n        username = self.get_username(claims)\n        email = claims.get(settings.OIDC_EMAIL_CLAIM, \"\")\n        first_name = claims.get(settings.OIDC_FIRSTNAME_CLAIM, \"\")\n        last_name = claims.get(settings.OIDC_LASTNAME_CLAIM, \"\")\n\n        return self.UserModel.objects.create(\n            username=username, email=email, first_name=first_name, last_name=last_name\n        )\n\n    def get_username(self, claims):\n        try:\n            return claims[settings.OIDC_USERNAME_CLAIM]\n        except KeyError:\n            raise SuspiciousOperation(\"Couldn't find username claim\")\n", "path": "timed/authentication.py"}]}