Skip to content

Commit 9aa74af

Browse files
authored
Add Linux Do OAuth2 login (#343)
* Add Linux Do OAuth2 login * Adapt to linux do oauth2 email field * Fix get user email logic * Fix lint
1 parent f21af0c commit 9aa74af

File tree

11 files changed

+105
-42
lines changed

11 files changed

+105
-42
lines changed

backend/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ OPERA_LOG_ENCRYPT_SECRET_KEY='d77b25790a804c2b4a339dd0207941e4cefa5751935a33735b
1818
# OAuth2
1919
OAUTH2_GITHUB_CLIENT_ID='test'
2020
OAUTH2_GITHUB_CLIENT_SECRET='test'
21+
OAUTH2_LINUX_DO_CLIENT_ID='test'
22+
OAUTH2_LINUX_DO_CLIENT_SECRET='test'
2123
# Task
2224
# Celery
2325
CELERY_BROKER_REDIS_DATABASE=1

backend/app/admin/api/v1/auth2/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
from fastapi import APIRouter
44

55
from backend.app.admin.api.v1.auth2.github import router as github_router
6+
from backend.app.admin.api.v1.auth2.linux_do import router as linux_do_router
67

78
router = APIRouter(prefix='/auth2')
89

9-
router.include_router(github_router, prefix='/github', tags=['OAuth2'])
10+
router.include_router(github_router, prefix='/github', tags=['GitHub OAuth2'])
11+
router.include_router(linux_do_router, prefix='/linuxdo', tags=['Linux Do OAuth2'])
Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,42 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
33
from fastapi import APIRouter, BackgroundTasks, Depends, Request
4+
from fastapi_limiter.depends import RateLimiter
45
from fastapi_oauth20 import FastAPIOAuth20, GitHubOAuth20
56

67
from backend.app.admin.conf import admin_settings
7-
from backend.app.admin.service.github_service import github_service
8+
from backend.app.admin.service.oauth2_service import oauth2_service
9+
from backend.common.enums import UserSocialType
810
from backend.common.response.response_schema import ResponseModel, response_base
911

1012
router = APIRouter()
1113

12-
github_client = GitHubOAuth20(admin_settings.OAUTH2_GITHUB_CLIENT_ID, admin_settings.OAUTH2_GITHUB_CLIENT_SECRET)
13-
github_oauth2 = FastAPIOAuth20(github_client, admin_settings.OAUTH2_GITHUB_REDIRECT_URI)
14+
_github_client = GitHubOAuth20(admin_settings.OAUTH2_GITHUB_CLIENT_ID, admin_settings.OAUTH2_GITHUB_CLIENT_SECRET)
15+
_github_oauth2 = FastAPIOAuth20(_github_client, admin_settings.OAUTH2_GITHUB_REDIRECT_URI)
1416

1517

1618
@router.get('', summary='获取 Github 授权链接')
1719
async def github_auth2() -> ResponseModel:
18-
auth_url = await github_client.get_authorization_url(redirect_uri=admin_settings.OAUTH2_GITHUB_REDIRECT_URI)
20+
auth_url = await _github_client.get_authorization_url(redirect_uri=admin_settings.OAUTH2_GITHUB_REDIRECT_URI)
1921
return await response_base.success(data=auth_url)
2022

2123

2224
@router.get(
2325
'/callback',
24-
summary='Github 授权重定向',
26+
summary='Github 授权自动重定向',
2527
description='Github 授权后,自动重定向到当前地址并获取用户信息,通过用户信息自动创建系统用户',
26-
response_model=None,
28+
dependencies=[Depends(RateLimiter(times=5, minutes=1))],
2729
)
2830
async def github_login(
29-
request: Request, background_tasks: BackgroundTasks, oauth: FastAPIOAuth20 = Depends(github_oauth2)
31+
request: Request, background_tasks: BackgroundTasks, oauth2: FastAPIOAuth20 = Depends(_github_oauth2)
3032
) -> ResponseModel:
31-
token, _state = oauth
33+
token, _state = oauth2
3234
access_token = token['access_token']
33-
user = await github_client.get_userinfo(access_token)
34-
data = await github_service.create_with_login(request, background_tasks, user)
35+
user = await _github_client.get_userinfo(access_token)
36+
data = await oauth2_service.create_with_login(
37+
request=request,
38+
background_tasks=background_tasks,
39+
user=user,
40+
social=UserSocialType.github,
41+
)
3542
return await response_base.success(data=data)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
from fastapi import APIRouter, BackgroundTasks, Depends, Request
5+
from fastapi_limiter.depends import RateLimiter
6+
from fastapi_oauth20 import FastAPIOAuth20, LinuxDoOAuth20
7+
8+
from backend.app.admin.conf import admin_settings
9+
from backend.app.admin.service.oauth2_service import oauth2_service
10+
from backend.common.enums import UserSocialType
11+
from backend.common.response.response_schema import ResponseModel, response_base
12+
13+
router = APIRouter()
14+
15+
_linux_do_client = LinuxDoOAuth20(
16+
admin_settings.OAUTH2_LINUX_DO_CLIENT_ID,
17+
admin_settings.OAUTH2_LINUX_DO_CLIENT_SECRET,
18+
)
19+
_linux_do_oauth2 = FastAPIOAuth20(_linux_do_client, admin_settings.OAUTH2_LINUX_DO_REDIRECT_URI)
20+
21+
22+
@router.get('', summary='获取 Linux Do 授权链接')
23+
async def linux_do_auth2() -> ResponseModel:
24+
auth_url = await _linux_do_client.get_authorization_url(redirect_uri=admin_settings.OAUTH2_GITHUB_REDIRECT_URI)
25+
return await response_base.success(data=auth_url)
26+
27+
28+
@router.get(
29+
'/callback',
30+
summary='Linux Do 授权自动重定向',
31+
description='Linux Do 授权后,自动重定向到当前地址并获取用户信息,通过用户信息自动创建系统用户',
32+
dependencies=[Depends(RateLimiter(times=5, minutes=1))],
33+
)
34+
async def linux_do_login(
35+
request: Request, background_tasks: BackgroundTasks, oauth2: FastAPIOAuth20 = Depends(_linux_do_oauth2)
36+
) -> ResponseModel:
37+
token, _state = oauth2
38+
access_token = token['access_token']
39+
user = await _linux_do_client.get_userinfo(access_token)
40+
data = await oauth2_service.create_with_login(
41+
request=request,
42+
background_tasks=background_tasks,
43+
user=user,
44+
social=UserSocialType.linuxdo,
45+
)
46+
return await response_base.success(data=data)

backend/app/admin/conf.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@ class AdminSettings(BaseSettings):
1313
model_config = SettingsConfigDict(env_file=f'{BasePath}/.env', env_file_encoding='utf-8', extra='ignore')
1414

1515
# OAuth2:https://github.com/fastapi-practices/fastapi_oauth20
16+
# GitHub
1617
OAUTH2_GITHUB_CLIENT_ID: str
1718
OAUTH2_GITHUB_CLIENT_SECRET: str
19+
OAUTH2_GITHUB_REDIRECT_URI: str = 'http://127.0.0.1:8000/api/v1/auth2/github/callback'
1820

19-
# OAuth2
20-
OAUTH2_GITHUB_REDIRECT_URI: str = 'http://127.0.0.1:8000/api/v1/auth/github/callback'
21+
# Linux Do
22+
OAUTH2_LINUX_DO_CLIENT_ID: str
23+
OAUTH2_LINUX_DO_CLIENT_SECRET: str
24+
OAUTH2_LINUX_DO_REDIRECT_URI: str = 'http://127.0.0.1:8000/api/v1/auth2/linuxdo/callback'
2125

2226
# Captcha
2327
CAPTCHA_LOGIN_REDIS_PREFIX: str = 'fba_login_captcha'

backend/app/admin/service/auth_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
class AuthService:
3131
@staticmethod
32-
async def swagger_login(obj: HTTPBasicCredentials) -> tuple[str, User]:
32+
async def swagger_login(*, obj: HTTPBasicCredentials) -> tuple[str, User]:
3333
async with async_db_session.begin() as db:
3434
current_user = await user_dao.get_by_username(db, obj.username)
3535
if not current_user:

backend/app/admin/service/github_service.py renamed to backend/app/admin/service/oauth2_service.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,40 @@
1818
from backend.utils.timezone import timezone
1919

2020

21-
class GithubService:
21+
class OAuth2Service:
2222
@staticmethod
2323
async def create_with_login(
24-
request: Request, background_tasks: BackgroundTasks, user: dict
24+
*, request: Request, background_tasks: BackgroundTasks, user: dict, social: UserSocialType
2525
) -> GetLoginToken | None:
2626
async with async_db_session.begin() as db:
27-
github_email = user['email']
28-
if not github_email:
29-
raise AuthorizationError(msg='授权失败,GitHub 账户未绑定邮箱')
30-
github_id = user['id']
31-
github_username = user['login']
32-
github_nickname = user['name']
33-
sys_user = await user_dao.check_email(db, github_email)
27+
# 获取 OAuth2 平台用户信息
28+
_id = user.get('id')
29+
_username = user.get('username')
30+
if social == UserSocialType.github:
31+
_username = user.get('login')
32+
_nickname = user.get('name')
33+
_email = user.get('email')
34+
if social == UserSocialType.linuxdo:
35+
_email = f'{_username}@linux.do'
36+
if not _email:
37+
raise AuthorizationError(msg=f'授权失败,{social.value} 账户未绑定邮箱')
38+
# 创建系统用户
39+
sys_user = await user_dao.check_email(db, _email)
3440
if not sys_user:
35-
# 创建系统用户
36-
sys_user = await user_dao.get_by_username(db, github_username)
41+
sys_user = await user_dao.get_by_username(db, _username)
3742
if sys_user:
38-
github_username = f'{github_username}{text_captcha(5)}'
39-
sys_user = await user_dao.get_by_nickname(db, github_nickname)
43+
_username = f'{_username}#{text_captcha(5)}'
44+
sys_user = await user_dao.get_by_nickname(db, _nickname)
4045
if sys_user:
41-
github_nickname = f'{github_nickname}{text_captcha(5)}'
42-
new_sys_user = RegisterUserParam(
43-
username=github_username, password=None, nickname=github_nickname, email=github_email
44-
)
46+
_nickname = f'{_nickname}#{text_captcha(5)}'
47+
new_sys_user = RegisterUserParam(username=_username, password=None, nickname=_nickname, email=_email)
4548
await user_dao.create(db, new_sys_user, social=True)
4649
await db.flush()
47-
sys_user = await user_dao.check_email(db, github_email)
50+
sys_user = await user_dao.check_email(db, _email)
4851
# 绑定社交用户
4952
user_social = await user_social_dao.get(db, sys_user.id, UserSocialType.github)
5053
if not user_social:
51-
new_user_social = CreateUserSocialParam(
52-
source=UserSocialType.github, uid=str(github_id), user_id=sys_user.id
53-
)
54+
new_user_social = CreateUserSocialParam(source=social.value, uid=str(_id), user_id=sys_user.id)
5455
await user_social_dao.create(db, new_user_social)
5556
# 创建 token
5657
access_token, access_token_expire_time = await jwt.create_access_token(
@@ -81,4 +82,4 @@ async def create_with_login(
8182
return data
8283

8384

84-
github_service = GithubService()
85+
oauth2_service = OAuth2Service()

backend/common/enums.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,4 @@ class UserSocialType(StrEnum):
8787
"""用户社交类型"""
8888

8989
github = 'GitHub'
90+
linuxdo = 'LinuxDo'

backend/pdm.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ dependencies = [
3838
"user-agents==2.2.0",
3939
"uvicorn[standard]==0.29.0",
4040
"XdbSearchIP==1.0.2",
41-
"fastapi_oauth20>=0.0.1a1",
41+
"fastapi_oauth20>=0.0.1a2",
4242
"flower==2.0.1",
4343
"sqlalchemy-crud-plus==0.0.2",
4444
]

0 commit comments

Comments
 (0)