Skip to content

Commit cca1de1

Browse files
authored
Optimize the fastapi oauth2 integration (#12)
1 parent 5f27f38 commit cca1de1

File tree

10 files changed

+61
-26
lines changed

10 files changed

+61
-26
lines changed

fastapi_oauth20/clients/feishu.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ def __init__(self, client_id: str, client_secret: str):
1414
authorize_endpoint='https://passport.feishu.cn/suite/passport/oauth/authorize',
1515
access_token_endpoint='https://passport.feishu.cn/suite/passport/oauth/token',
1616
refresh_token_endpoint='https://passport.feishu.cn/suite/passport/oauth/authorize',
17-
oauth_callback_route_name='feishu',
1817
default_scopes=[
1918
'contact:user.employee_id:readonly',
2019
'contact:user.base:readonly',

fastapi_oauth20/clients/gitee.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ def __init__(self, client_id: str, client_secret: str):
1414
authorize_endpoint='https://gitee.com/oauth/authorize',
1515
access_token_endpoint='https://gitee.com/oauth/token',
1616
refresh_token_endpoint='https://gitee.com/oauth/token',
17-
oauth_callback_route_name='gitee',
1817
default_scopes=['user_info'],
1918
)
2019

fastapi_oauth20/clients/github.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ def __init__(self, client_id: str, client_secret: str):
1313
client_secret=client_secret,
1414
authorize_endpoint='https://github.com/login/oauth/authorize',
1515
access_token_endpoint='https://github.com/login/oauth/access_token',
16-
oauth_callback_route_name='github',
1716
default_scopes=['user', 'user:email'],
1817
)
1918

fastapi_oauth20/clients/google.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ def __init__(self, client_id: str, client_secret: str):
1515
access_token_endpoint='https://oauth2.googleapis.com/token',
1616
refresh_token_endpoint='https://oauth2.googleapis.com/token',
1717
revoke_token_endpoint='https://accounts.google.com/o/oauth2/revoke',
18-
oauth_callback_route_name='google',
1918
default_scopes=['email', 'openid', 'profile'],
2019
)
2120

fastapi_oauth20/clients/linuxdo.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ def __init__(self, client_id: str, client_secret: str):
1414
authorize_endpoint='https://connect.linux.do/oauth2/authorize',
1515
access_token_endpoint='https://connect.linux.do/oauth2/token',
1616
refresh_token_endpoint='https://connect.linux.do/oauth2/token',
17-
oauth_callback_route_name='linuxdo',
1817
token_endpoint_basic_auth=True,
1918
)
2019

fastapi_oauth20/clients/oschina.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ def __init__(self, client_id: str, client_secret: str):
1414
authorize_endpoint='https://www.oschina.net/action/oauth2/authorize',
1515
access_token_endpoint='https://www.oschina.net/action/openapi/token',
1616
refresh_token_endpoint='https://www.oschina.net/action/openapi/token',
17-
oauth_callback_route_name='oschina',
1817
)
1918

2019
async def get_userinfo(self, access_token: str) -> dict:

fastapi_oauth20/errors.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import httpx
44

55

6-
class FastAPIOAuth20BaseError(Exception):
7-
"""The fastapi-oauth20 base error."""
6+
class OAuth20BaseError(Exception):
7+
"""The oauth2 base error."""
88

99
msg: str
1010

@@ -13,7 +13,7 @@ def __init__(self, msg: str) -> None:
1313
super().__init__(msg)
1414

1515

16-
class OAuth20RequestError(FastAPIOAuth20BaseError):
16+
class OAuth20RequestError(OAuth20BaseError):
1717
"""OAuth2 httpx request error"""
1818

1919
def __init__(self, msg: str, response: httpx.Response | None = None) -> None:
Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,80 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
3-
from fastapi import Request
3+
from typing import Any
44

5-
from fastapi_oauth20.errors import RedirectURIError
5+
import httpx
6+
7+
from fastapi import HTTPException, Request
8+
9+
from fastapi_oauth20.errors import AccessTokenError, HTTPXOAuth20Error, OAuth20BaseError
610
from fastapi_oauth20.oauth20 import OAuth20Base
711

812

13+
class OAuth20AuthorizeCallbackError(HTTPException, OAuth20BaseError):
14+
"""The OAuth2 authorization callback error."""
15+
16+
def __init__(
17+
self,
18+
status_code: int,
19+
detail: Any = None,
20+
headers: dict[str, str] | None = None,
21+
response: httpx.Response | None = None,
22+
) -> None:
23+
self.response = response
24+
super().__init__(status_code=status_code, detail=detail, headers=headers)
25+
26+
927
class FastAPIOAuth20:
1028
def __init__(
1129
self,
1230
client: OAuth20Base,
1331
redirect_uri: str | None = None,
14-
oauth_callback_route_name: str | None = None,
32+
oauth2_callback_route_name: str | None = None,
1533
):
34+
"""
35+
OAuth2 authorization callback dependency injection
36+
37+
:param client: A client base on OAuth20Base.
38+
:param redirect_uri: OAuth2 callback full URL.
39+
:param oauth2_callback_route_name: OAuth2 callback route name, as defined by the route decorator 'name' parameter.
40+
"""
41+
assert (redirect_uri is None and oauth2_callback_route_name is not None) or (
42+
redirect_uri is not None and oauth2_callback_route_name is None
43+
), 'FastAPIOAuth20 redirect_uri and oauth2_callback_route_name cannot be defined at the same time.'
1644
self.client = client
17-
self.oauth_callback_route_name = oauth_callback_route_name
1845
self.redirect_uri = redirect_uri
46+
self.oauth2_callback_route_name = oauth2_callback_route_name
1947

2048
async def __call__(
2149
self,
2250
request: Request,
23-
code: str,
51+
code: str | None = None,
2452
state: str | None = None,
2553
code_verifier: str | None = None,
54+
error: str | None = None,
2655
) -> tuple[dict, str]:
27-
if self.redirect_uri is None:
28-
if self.oauth_callback_route_name is None:
29-
raise RedirectURIError('redirect_uri is required')
30-
self.redirect_uri = str(request.url_for(self.oauth_callback_route_name))
31-
32-
access_token = await self.client.get_access_token(
33-
code=code, redirect_uri=self.redirect_uri, code_verifier=code_verifier
34-
)
56+
if code is None or error is not None:
57+
raise OAuth20AuthorizeCallbackError(
58+
status_code=400,
59+
detail=error if error is not None else None,
60+
)
61+
62+
if self.oauth2_callback_route_name:
63+
redirect_url = str(request.url_for(self.oauth2_callback_route_name))
64+
else:
65+
redirect_url = self.redirect_uri
66+
67+
try:
68+
access_token = await self.client.get_access_token(
69+
code=code,
70+
redirect_uri=redirect_url,
71+
code_verifier=code_verifier,
72+
)
73+
except (HTTPXOAuth20Error, AccessTokenError) as e:
74+
raise OAuth20AuthorizeCallbackError(
75+
status_code=500,
76+
detail=e.msg,
77+
response=e.response,
78+
) from e
3579

3680
return access_token, state

fastapi_oauth20/oauth20.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ def __init__(
2727
access_token_endpoint: str,
2828
refresh_token_endpoint: str | None = None,
2929
revoke_token_endpoint: str | None = None,
30-
oauth_callback_route_name: str = 'oauth20',
3130
default_scopes: list[str] | None = None,
3231
token_endpoint_basic_auth: bool = False,
3332
revoke_token_endpoint_basic_auth: bool = False,
@@ -41,7 +40,6 @@ def __init__(
4140
:param access_token_endpoint: The access token endpoint URL.
4241
:param refresh_token_endpoint: The refresh token endpoint URL.
4342
:param revoke_token_endpoint: The revoke token endpoint URL.
44-
:param oauth_callback_route_name:
4543
:param default_scopes:
4644
:param token_endpoint_basic_auth:
4745
:param revoke_token_endpoint_basic_auth:
@@ -52,7 +50,6 @@ def __init__(
5250
self.access_token_endpoint = access_token_endpoint
5351
self.refresh_token_endpoint = refresh_token_endpoint
5452
self.revoke_token_endpoint = revoke_token_endpoint
55-
self.oauth_callback_route_name = oauth_callback_route_name
5653
self.default_scopes = default_scopes
5754
self.token_endpoint_basic_auth = token_endpoint_basic_auth
5855
self.revoke_token_endpoint_basic_auth = revoke_token_endpoint_basic_auth

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[project]
2-
name = "fastapi_oauth20"
2+
name = "fastapi-oauth20"
33
description = "在 FastAPI 中异步授权 OAuth 2.0 客户端"
44
dynamic = [
55
"version",

0 commit comments

Comments
 (0)