Skip to content

Commit 0a5ada6

Browse files
committed
refactor: create base class OAuthBackend
1 parent ad7f7f8 commit 0a5ada6

File tree

7 files changed

+123
-136
lines changed

7 files changed

+123
-136
lines changed

fastapi/app/auth/base/libs.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from datetime import datetime
2+
from typing import Any, Optional
3+
4+
from fastapi import Response
5+
from fastapi_users import models
6+
from fastapi_users.authentication import AuthenticationBackend
7+
from fastapi_users.authentication.strategy import Strategy
8+
from fastapi_users.authentication.transport import Transport
9+
from fastapi_users.types import DependencyCallable
10+
11+
from ..models import User
12+
13+
14+
class OAuthBackend(AuthenticationBackend):
15+
def __init__(
16+
self,
17+
name: str,
18+
transport: Transport,
19+
get_strategy: DependencyCallable[Strategy[models.UP, models.ID]],
20+
oauth_name: str,
21+
has_profile_callback: bool = False,
22+
):
23+
super().__init__(name, transport, get_strategy)
24+
self.oauth_name = oauth_name
25+
self.has_profile_callback = has_profile_callback
26+
27+
async def login(self, strategy: Strategy, user: User, response: Response) -> Any:
28+
strategy_response = await super().login(strategy, user, response)
29+
user.last_login_at = datetime.now()
30+
if self.has_profile_callback:
31+
token = self.get_access_token(user)
32+
profile = self.get_profile(token)
33+
user = self.update_profile(user, profile)
34+
await user.save()
35+
return strategy_response
36+
37+
def get_access_token(self, user: User) -> Optional[str]:
38+
for account in user.oauth_accounts:
39+
if account.oauth_name == self.oauth_name:
40+
return account.access_token
41+
return None
42+
43+
def get_profile(self, access_token: str) -> dict:
44+
return {}
45+
46+
def update_profile(self, user: User, profile: dict) -> User:
47+
return user

fastapi/app/auth/github/libs.py

Lines changed: 28 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,40 @@
1-
from datetime import datetime
2-
from typing import Any, Optional
3-
41
import requests
5-
from fastapi import Response
6-
from fastapi_users.authentication import AuthenticationBackend
7-
from fastapi_users.authentication.strategy import Strategy
82

3+
from ..base.libs import OAuthBackend
94
from ..exceptions import BadCredentialException
105
from ..libs import bearer_transport, get_jwt_strategy
116
from ..models import User
127
from .constants import GITHUB_USERINFO_URL
138

149

15-
class GithubAuthBackend(AuthenticationBackend):
16-
async def login(self, strategy: Strategy, user: User, response: Response) -> Any:
17-
strategy_response = await super().login(strategy, user, response)
18-
token = self.get_google_access_token(user)
19-
profile = get_profile(token)
20-
await update_profile(user, profile).save()
21-
await user.save()
22-
return strategy_response
23-
24-
def get_google_access_token(self, user: User) -> Optional[str]:
25-
for account in user.oauth_accounts:
26-
if account.oauth_name == 'github':
27-
return account.access_token
28-
return None
29-
30-
31-
def get_profile(access_token: str) -> dict:
32-
headers = dict(Authorization=f'Bearer {access_token}')
33-
response = requests.get(GITHUB_USERINFO_URL, headers=headers)
34-
if not response.ok:
35-
raise BadCredentialException(
36-
'Failed to get user information from Github.')
37-
profile = dict(response.json())
38-
name = profile.get('name').split()
39-
try:
40-
profile.update({
41-
"first_name": name[0],
42-
"last_name": name[1],
43-
})
44-
except:
45-
pass
46-
return profile
47-
48-
49-
def update_profile(user: User, profile: dict) -> User:
50-
if user.first_name == None:
51-
user.first_name = profile.get('first_name')
52-
if user.last_name == None:
53-
user.last_name = profile.get('last_name')
54-
if user.picture == None:
55-
user.picture = profile.get('avatar_url')
56-
user.last_login_at = datetime.now()
57-
return user
58-
59-
60-
auth_backend_github = GithubAuthBackend(
10+
class GithubAuthBackend(OAuthBackend):
11+
def get_profile(self, access_token: str) -> dict:
12+
headers = dict(Authorization=f'Bearer {access_token}')
13+
response = requests.get(GITHUB_USERINFO_URL, headers=headers)
14+
if not response.ok:
15+
raise BadCredentialException(
16+
'Failed to get user information from Github.')
17+
return response.json()
18+
19+
def update_profile(self, user: User, profile: dict) -> User:
20+
user = super().update_profile(user, profile)
21+
try:
22+
name = profile.get('name').split()
23+
if user.first_name == None:
24+
user.first_name = name[0]
25+
if user.last_name == None:
26+
user.last_name = name[1]
27+
except:
28+
pass
29+
if user.picture == None:
30+
user.picture = profile.get('avatar_url')
31+
return user
32+
33+
34+
auth_backend = GithubAuthBackend(
6135
name="jwt-github",
6236
transport=bearer_transport,
6337
get_strategy=get_jwt_strategy,
38+
oauth_name='github',
39+
has_profile_callback=True,
6440
)

fastapi/app/auth/github/routes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from httpx_oauth.clients.github import GitHubOAuth2
44

55
from ..libs import fastapi_users
6-
from .libs import auth_backend_github
6+
from .libs import auth_backend
77

88
github_oauth_client = GitHubOAuth2(
99
client_id=Configs.GITHUB_CLIENT_ID,
@@ -12,7 +12,7 @@
1212

1313
github_oauth_router = fastapi_users.get_oauth_router(
1414
oauth_client=github_oauth_client,
15-
backend=auth_backend_github,
15+
backend=auth_backend,
1616
state_secret=Configs.SECRET_KEY,
1717
redirect_url=Configs.GITHUB_CALLBACK_URL,
1818
associate_by_email=True,

fastapi/app/auth/google/libs.py

Lines changed: 21 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,36 @@
1-
from datetime import datetime
2-
from typing import Any, Optional
3-
41
import requests
5-
from fastapi import Response
6-
from fastapi_users.authentication import AuthenticationBackend
7-
from fastapi_users.authentication.strategy import Strategy
82

3+
from ..base.libs import OAuthBackend
94
from ..exceptions import BadCredentialException
105
from ..libs import bearer_transport, get_jwt_strategy
116
from ..models import User
127
from .constants import GOOGLE_USERINFO_URL
138

149

15-
class GoogleAuthBackend(AuthenticationBackend):
16-
async def login(self, strategy: Strategy, user: User, response: Response) -> Any:
17-
strategy_response = await super().login(strategy, user, response)
18-
token = self.get_google_access_token(user)
19-
profile = get_profile(token)
20-
await update_profile(user, profile).save()
21-
return strategy_response
22-
23-
def get_google_access_token(self, user: User) -> Optional[str]:
24-
for account in user.oauth_accounts:
25-
if account.oauth_name == 'google':
26-
return account.access_token
27-
return None
28-
29-
30-
def get_profile(access_token: str) -> dict:
31-
response = requests.get(url=GOOGLE_USERINFO_URL,
32-
params={'access_token': access_token})
33-
if not response.ok:
34-
raise BadCredentialException(
35-
'Failed to get user information from Google.')
36-
return response.json()
37-
10+
class GoogleAuthBackend(OAuthBackend):
11+
def get_profile(self, access_token: str) -> dict:
12+
response = requests.get(url=GOOGLE_USERINFO_URL,
13+
params={'access_token': access_token})
14+
if not response.ok:
15+
raise BadCredentialException(
16+
'Failed to get user information from Google.')
17+
return response.json()
3818

39-
def update_profile(user: User, profile: dict) -> User:
40-
if user.first_name == None:
41-
user.first_name = profile.get('given_name')
42-
if user.last_name == None:
43-
user.last_name = profile.get('family_name')
44-
if user.picture == None:
45-
user.picture = profile.get('picture')
46-
user.last_login_at = datetime.now()
47-
return user
19+
def update_profile(self, user: User, profile: dict) -> User:
20+
user = super().update_profile(user, profile)
21+
if user.first_name == None:
22+
user.first_name = profile.get('given_name')
23+
if user.last_name == None:
24+
user.last_name = profile.get('family_name')
25+
if user.picture == None:
26+
user.picture = profile.get('picture')
27+
return user
4828

4929

50-
auth_backend_google = GoogleAuthBackend(
30+
auth_backend = GoogleAuthBackend(
5131
name="jwt-google",
5232
transport=bearer_transport,
5333
get_strategy=get_jwt_strategy,
34+
oauth_name='google',
35+
has_profile_callback=True,
5436
)

fastapi/app/auth/google/routes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from ..libs import fastapi_users
66
from .constants import GOOGLE_SCOPE_EMAIL, GOOGLE_SCOPE_PROFILE
7-
from .libs import auth_backend_google
7+
from .libs import auth_backend
88

99
google_oauth_client = GoogleOAuth2(
1010
client_id=Configs.GOOGLE_CLIENT_ID,
@@ -16,7 +16,7 @@
1616

1717
google_oauth_router = fastapi_users.get_oauth_router(
1818
oauth_client=google_oauth_client,
19-
backend=auth_backend_google,
19+
backend=auth_backend,
2020
state_secret=Configs.SECRET_KEY,
2121
redirect_url=Configs.GOOGLE_CALLBACK_URL,
2222
associate_by_email=True,

fastapi/app/auth/kakao/libs.py

Lines changed: 21 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,39 @@
1-
from datetime import datetime
21
import json
3-
from typing import Any, Optional
42

53
import requests
6-
from fastapi import Response
7-
from fastapi_users.authentication import AuthenticationBackend
8-
from fastapi_users.authentication.strategy import Strategy
94

5+
from ..base.libs import OAuthBackend
106
from ..exceptions import BadCredentialException
117
from ..libs import bearer_transport, get_jwt_strategy
128
from ..models import User
139
from .constants import KAKAO_USERINFO_URL
1410

1511

16-
class KakaoAuthBackend(AuthenticationBackend):
17-
async def login(self, strategy: Strategy, user: User, response: Response) -> Any:
18-
strategy_response = await super().login(strategy, user, response)
19-
token = self.get_access_token(user)
20-
profile = get_profile(token)
21-
await update_profile(user, profile).save()
22-
return strategy_response
12+
class KakaoAuthBackend(OAuthBackend):
13+
def get_profile(self, access_token: str) -> dict:
14+
headers = dict(Authorization=f'Bearer {access_token}')
15+
response = requests.post(KAKAO_USERINFO_URL,
16+
headers=headers,
17+
params={"property_keys": json.dumps(["kakao_account.profile"])})
18+
if not response.ok:
19+
raise BadCredentialException(
20+
'Failed to get user information from Kakao.')
2321

24-
def get_access_token(self, user: User) -> Optional[str]:
25-
for account in user.oauth_accounts:
26-
if account.oauth_name == 'kakao':
27-
return account.access_token
28-
return None
22+
return response.json()
2923

24+
def update_profile(self, user: User, profile: dict) -> User:
25+
user = super().update_profile(user, profile)
26+
kakao_account = profile.get('kakao_account')
27+
profile = kakao_account.get('profile')
28+
if user.picture == None:
29+
user.picture = profile.get('profile_image_url')
30+
return user
3031

31-
def get_profile(access_token: str) -> dict:
32-
headers = dict(Authorization=f'Bearer {access_token}')
33-
response = requests.post(KAKAO_USERINFO_URL,
34-
headers=headers,
35-
params={"property_keys": json.dumps(["kakao_account.profile"])})
36-
if not response.ok:
37-
raise BadCredentialException(
38-
'Failed to get user information from Kakao.')
3932

40-
profile = dict(response.json())
41-
kakao_account = profile.get('kakao_account')
42-
return kakao_account.get('profile')
43-
44-
45-
def update_profile(user: User, profile: dict) -> User:
46-
if user.picture == None:
47-
user.picture = profile.get('profile_image_url')
48-
user.last_login_at = datetime.now()
49-
return user
50-
51-
52-
auth_backend_kakao = KakaoAuthBackend(
33+
auth_backend = KakaoAuthBackend(
5334
name="jwt-kakao",
5435
transport=bearer_transport,
5536
get_strategy=get_jwt_strategy,
37+
oauth_name='kakao',
38+
has_profile_callback=True,
5639
)

fastapi/app/auth/kakao/routes.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
21
from app.configs import Configs
32

43
from ..libs import fastapi_users
54
from .client import KakaoOAuth2
6-
from .libs import auth_backend_kakao
5+
from .libs import auth_backend
76

87
kakao_oauth_client = KakaoOAuth2(
98
client_id=Configs.KAKAO_CLIENT_ID,
@@ -15,7 +14,7 @@
1514

1615
kakao_oauth_router = fastapi_users.get_oauth_router(
1716
oauth_client=kakao_oauth_client,
18-
backend=auth_backend_kakao,
17+
backend=auth_backend,
1918
state_secret=Configs.SECRET_KEY,
2019
redirect_url=Configs.KAKAO_CALLBACK_URL,
2120
associate_by_email=True,

0 commit comments

Comments
 (0)