Skip to content

Commit 5fd531b

Browse files
committed
Fix OIDC token instrospection redis_instance args
1 parent aa06e0e commit 5fd531b

File tree

2 files changed

+50
-2
lines changed

2 files changed

+50
-2
lines changed

backend/src/appointment/dependencies/auth.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import sentry_sdk
77
from fastapi import Depends, Body, Request, HTTPException
8+
from fastapi.params import Depends as DependsClass
89
from fastapi.security import OAuth2PasswordBearer
910
import jwt
1011

@@ -127,6 +128,12 @@ def get_subscriber(
127128
if token is None:
128129
raise InvalidTokenException()
129130

131+
# When this method is called directly (not through FastAPI DI), redis_instance will be
132+
# the Depends object itself rather than the resolved value, so assigning it to None will
133+
# make it call the OIDC introspect endpoint in that case (no caching)
134+
if isinstance(redis_instance, DependsClass):
135+
redis_instance = None
136+
130137
if AuthScheme.is_oidc():
131138
user = get_user_from_oidc_token_introspection(db, token, redis_instance)
132139
else:
@@ -149,20 +156,22 @@ def get_subscriber(
149156
async def get_subscriber_from_onetime_token(
150157
request: Request,
151158
db: Session = Depends(get_db),
159+
redis_instance=Depends(get_redis),
152160
):
153161
"""Retrieve the subscriber via a one-time token only!"""
154162
token: str = await oauth2_scheme(request)
155-
return get_subscriber(request, token, db, require_jti=True)
163+
return get_subscriber(request, token, db, redis_instance, require_jti=True)
156164

157165

158166
async def get_subscriber_or_none(
159167
request: Request,
160168
db: Session = Depends(get_db),
169+
redis_instance=Depends(get_redis),
161170
):
162171
"""Retrieve the subscriber or return None. This does not automatically error out like the other deps"""
163172
try:
164173
token: str = await oauth2_scheme(request)
165-
subscriber = get_subscriber(request, token, db)
174+
subscriber = get_subscriber(request, token, db, redis_instance)
166175
except InvalidTokenException:
167176
return None
168177
except HTTPException:

backend/test/unit/test_auth_dependency.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import pytest
77
from freezegun import freeze_time
88
from unittest import mock
9+
from fastapi.params import Depends as DependsClass
910

1011

1112
from appointment.controller.auth import signed_url_by_subscriber
@@ -167,6 +168,44 @@ def test_get_subscriber_with_invalid_token(self, with_db, with_l10n, make_pro_su
167168
# Use a nonsense value, like the subscriber id!
168169
get_subscriber(request, subscriber.id, db)
169170

171+
def test_get_subscriber_handles_depends_object_for_redis(
172+
self, with_db, with_l10n, make_pro_subscriber, make_external_connections, monkeypatch
173+
):
174+
"""Test that get_subscriber gracefully handles when redis_instance is a Depends object."""
175+
176+
saved_scheme = os.environ.get('AUTH_SCHEME', 'password')
177+
os.environ['AUTH_SCHEME'] = 'oidc'
178+
179+
try:
180+
subscriber = make_pro_subscriber()
181+
oidc_id = uuid.uuid4().hex
182+
access_token = uuid.uuid4().hex
183+
184+
make_external_connections(
185+
subscriber_id=subscriber.id, type_id=oidc_id, type=ExternalConnectionType.oidc
186+
)
187+
188+
request = mock.MagicMock()
189+
190+
with patch('appointment.controller.apis.oidc_client.OIDCClient.introspect_token') as introspect_mock:
191+
introspect_mock.return_value = {
192+
'sub': oidc_id,
193+
'username': subscriber.username,
194+
'email': subscriber.email,
195+
}
196+
197+
with with_db() as db:
198+
# Pass a Depends object as redis_instance (simulating direct call without DI resolution)
199+
depends_obj = DependsClass(lambda: None)
200+
retrieved_subscriber = get_subscriber(request, access_token, db, depends_obj)
201+
202+
# Should succeed by treating Depends as None and falling back to OIDC introspection
203+
assert retrieved_subscriber is not None
204+
assert retrieved_subscriber.id == subscriber.id
205+
introspect_mock.assert_called_once_with(access_token)
206+
finally:
207+
os.environ['AUTH_SCHEME'] = saved_scheme
208+
170209
def test_get_admin_subscriber(self, with_db, with_l10n, make_pro_subscriber):
171210
subscriber = make_pro_subscriber()
172211
access_token = create_access_token(data={'sub': f'uid-{subscriber.id}'})

0 commit comments

Comments
 (0)