Skip to content

Commit 6a311f4

Browse files
authored
feat: add ability to get original userinfo response instead of OpenID (#148)
* feat: add ability to get original userinfo response instead of OpenID * chore: make typehinting compatible with python 3.8 * test: fix typing in tests * test(cov): allow 1 % drop in coverage
1 parent 0b2a0b4 commit 6a311f4

File tree

3 files changed

+66
-10
lines changed

3 files changed

+66
-10
lines changed

codecov.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ coverage:
88
patch:
99
default:
1010
target: auto
11-
threshold: null
11+
threshold: 1%
1212
if_not_found: success
1313
only_pulls: true
1414
project:
1515
default:
1616
target: auto
17-
threshold: null
17+
threshold: 1%
1818
if_not_found: success
1919
only_pulls: true

fastapi_sso/sso/base.py

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import sys
99
import warnings
1010
from types import TracebackType
11-
from typing import Any, Dict, List, Optional, Type, Union
11+
from typing import Any, Dict, List, Literal, Optional, Type, Union, overload
1212

1313
import httpx
1414
import pydantic
@@ -296,14 +296,37 @@ async def get_login_redirect(
296296
response.set_cookie("pkce_code_verifier", str(self._pkce_code_verifier))
297297
return response
298298

299+
@overload
299300
async def verify_and_process(
300301
self,
301302
request: Request,
302303
*,
303304
params: Optional[Dict[str, Any]] = None,
304305
headers: Optional[Dict[str, Any]] = None,
305306
redirect_uri: Optional[str] = None,
306-
) -> Optional[OpenID]:
307+
convert_response: Literal[True] = True,
308+
) -> Optional[OpenID]: ...
309+
310+
@overload
311+
async def verify_and_process(
312+
self,
313+
request: Request,
314+
*,
315+
params: Optional[Dict[str, Any]] = None,
316+
headers: Optional[Dict[str, Any]] = None,
317+
redirect_uri: Optional[str] = None,
318+
convert_response: Literal[False],
319+
) -> Optional[Dict[str, Any]]: ...
320+
321+
async def verify_and_process(
322+
self,
323+
request: Request,
324+
*,
325+
params: Optional[Dict[str, Any]] = None,
326+
headers: Optional[Dict[str, Any]] = None,
327+
redirect_uri: Optional[str] = None,
328+
convert_response: Union[Literal[True], Literal[False]] = True,
329+
) -> Union[Optional[OpenID], Optional[Dict[str, Any]]]:
307330
"""
308331
Processes the login given a FastAPI (Starlette) Request object. This should be used for the /callback path.
309332
@@ -312,12 +335,14 @@ async def verify_and_process(
312335
params (Optional[Dict[str, Any]]): Additional query parameters to pass to the provider.
313336
headers (Optional[Dict[str, Any]]): Additional headers to pass to the provider.
314337
redirect_uri (Optional[str]): Overrides the `redirect_uri` specified on this instance.
338+
convert_response (bool): If True, userinfo response is converted to OpenID object.
315339
316340
Raises:
317341
SSOLoginError: If the 'code' parameter is not found in the callback request.
318342
319343
Returns:
320-
Optional[OpenID]: User information in OpenID format if the login was successful.
344+
Optional[OpenID]: User information as OpenID instance (if convert_response == True)
345+
Optional[Dict[str, Any]]: The original JSON response from the API.
321346
"""
322347
headers = headers or {}
323348
code = request.query_params.get("code")
@@ -338,6 +363,7 @@ async def verify_and_process(
338363
additional_headers=headers,
339364
redirect_uri=redirect_uri,
340365
pkce_code_verifier=pkce_code_verifier,
366+
convert_response=convert_response,
341367
)
342368

343369
def __enter__(self) -> "SSOBase":
@@ -363,6 +389,7 @@ def __exit__(
363389
def _extra_query_params(self) -> Dict:
364390
return {}
365391

392+
@overload
366393
async def process_login(
367394
self,
368395
code: str,
@@ -372,7 +399,33 @@ async def process_login(
372399
additional_headers: Optional[Dict[str, Any]] = None,
373400
redirect_uri: Optional[str] = None,
374401
pkce_code_verifier: Optional[str] = None,
375-
) -> Optional[OpenID]:
402+
convert_response: Literal[True] = True,
403+
) -> Optional[OpenID]: ...
404+
405+
@overload
406+
async def process_login(
407+
self,
408+
code: str,
409+
request: Request,
410+
*,
411+
params: Optional[Dict[str, Any]] = None,
412+
additional_headers: Optional[Dict[str, Any]] = None,
413+
redirect_uri: Optional[str] = None,
414+
pkce_code_verifier: Optional[str] = None,
415+
convert_response: Literal[False],
416+
) -> Optional[Dict[str, Any]]: ...
417+
418+
async def process_login(
419+
self,
420+
code: str,
421+
request: Request,
422+
*,
423+
params: Optional[Dict[str, Any]] = None,
424+
additional_headers: Optional[Dict[str, Any]] = None,
425+
redirect_uri: Optional[str] = None,
426+
pkce_code_verifier: Optional[str] = None,
427+
convert_response: Union[Literal[True], Literal[False]] = True,
428+
) -> Union[Optional[OpenID], Optional[Dict[str, Any]]]:
376429
"""
377430
Processes login from the callback endpoint to verify the user and request user info endpoint.
378431
It's a lower-level method, typically, you should use `verify_and_process` instead.
@@ -384,12 +437,14 @@ async def process_login(
384437
additional_headers (Optional[Dict[str, Any]]): Additional headers to be added to all requests.
385438
redirect_uri (Optional[str]): Overrides the `redirect_uri` specified on this instance.
386439
pkce_code_verifier (Optional[str]): A PKCE code verifier sent to the server to verify the login request.
440+
convert_response (bool): If True, userinfo response is converted to OpenID object.
387441
388442
Raises:
389443
ReusedOauthClientWarning: If the SSO object is reused, which is not safe and caused security issues.
390444
391445
Returns:
392-
Optional[OpenID]: User information in OpenID format if the login was successful.
446+
Optional[OpenID]: User information in OpenID format if the login was successful (convert_response == True).
447+
Optional[Dict[str, Any]]: Original userinfo API endpoint response.
393448
"""
394449
# pylint: disable=too-many-locals
395450
if self._oauth_client is not None: # pragma: no cover
@@ -447,5 +502,6 @@ async def process_login(
447502
session.headers.update(headers)
448503
response = await session.get(uri)
449504
content = response.json()
450-
451-
return await self.openid_from_response(content, session)
505+
if convert_response:
506+
return await self.openid_from_response(content, session)
507+
return content

tests/test_openid_responses.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from fastapi_sso.sso.facebook import FacebookSSO
1616
from fastapi_sso.sso.yandex import YandexSSO
1717

18-
sso_test_cases: Tuple[Type[SSOBase], Tuple[Dict[str, Any], OpenID]] = (
18+
sso_test_cases: Tuple[Tuple[Type[SSOBase], Dict[str, Any], OpenID], ...] = (
1919
(
2020
TwitterSSO,
2121
{"data": {"id": "test", "username": "TestUser1234", "name": "Test User"}},

0 commit comments

Comments
 (0)