Skip to content

Commit b8b6e91

Browse files
authored
Add LinuxDo OAuth2 (#9)
* Add Linux Do OAuth2 * Fix Linux Do get token
1 parent b0b42bb commit b8b6e91

File tree

11 files changed

+106
-28
lines changed

11 files changed

+106
-28
lines changed

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v4.5.0
3+
rev: v4.6.0
44
hooks:
55
- id: end-of-file-fixer
66
- id: check-yaml
77
- id: check-toml
88

99
- repo: https://github.com/charliermarsh/ruff-pre-commit
10-
rev: v0.2.0
10+
rev: v0.5.0
1111
hooks:
1212
- id: ruff
1313
args:
1414
- '--fix'
1515
- id: ruff-format
1616

1717
- repo: https://github.com/pdm-project/pdm
18-
rev: 2.12.3
18+
rev: 2.16.1
1919
hooks:
2020
- id: pdm-lock-check

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@
66
![GitHub release (with filter)](https://img.shields.io/github/v/release/fastapi-practices/fastapi_oauth20)
77
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
88

9-
109
在 FastAPI 中异步授权 OAuth2 客户端
1110

12-
我们的目标是集成多个 CN 第三方客户端,敬请期待
11+
我们的目标是集成多个 CN 第三方客户端,敬请期待(🐦)...
1312

1413
#### TODO:
1514

16-
如果我们能够很容易获取测试客户端,对接将会很快发生
15+
如果我们能够很容易获取测试客户端,或许这会很快
1716

18-
- [ ] library tests
19-
- [x] Google
17+
- [ ] tests
18+
- [x] [Google](https://developers.google.cn/identity/protocols/oauth2/javascript-implicit-flow?hl=en)
2019
- [ ] 微信
2120
- [ ] QQ
2221
- [ ] 抖音
23-
- [x] 飞书
22+
- [x] [飞书](https://open.feishu.cn/document/common-capabilities/sso/web-application-sso/web-app-overview)
2423
- [ ] 钉钉
2524
- [ ] 微博
2625
- [ ] 百度
27-
- [x] Gitee
28-
- [x] Github
29-
- [X] 开源中国
26+
- [x] [Gitee](https://gitee.com/api/v5/oauth_doc#/)
27+
- [x] [Github](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app)
28+
- [X] [开源中国](https://www.oschina.net/openapi)
3029
- [ ] 阿里云
30+
- [ ] [Linux Do](https://connect.linux.do/)

example/main.py

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

1212
GOOGLE_CLIENT_ID = '1053650337583-ljnla4m1e5cg16erq3tld5vjflqh4bij.apps.googleusercontent.com'
1313
GOOGLE_CLIENT_SECRET = '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'
14-
GOOGLE_REDIRECT_URI = 'http://localhost:8000/auth/google'
14+
GOOGLE_REDIRECT_URI = 'http://localhost:8000/auth2/google'
1515

1616
google_client = GoogleOAuth20(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET)
1717
oauth20 = FastAPIOAuth20(google_client, GOOGLE_REDIRECT_URI)
@@ -22,9 +22,9 @@ async def login_google():
2222
return await google_client.get_authorization_url(redirect_uri=GOOGLE_REDIRECT_URI)
2323

2424

25-
@app.get('/auth/google', response_model=None)
26-
async def auth_google(oauth: oauth20 = Depends()):
27-
token, state = oauth
25+
@app.get('/auth2/google', response_model=None)
26+
async def auth2_google(oauth2: oauth20 = Depends()):
27+
token, state = oauth2
2828
access_token = token['access_token']
2929
print(access_token)
3030
# do something

fastapi_oauth20/__init__.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
"""在 FastAPI 中异步授权 OAuth2 客户端"""
22

3-
__version__ = '0.0.1a1'
3+
__version__ = '0.0.1a2'
44

5-
__all__ = ['OSChinaOAuth20', 'GoogleOAuth20', 'FeiShuOAuth20', 'GiteeOAuth20', 'GitHubOAuth20', 'FastAPIOAuth20']
5+
__all__ = [
6+
'OSChinaOAuth20',
7+
'GoogleOAuth20',
8+
'FeiShuOAuth20',
9+
'GiteeOAuth20',
10+
'GitHubOAuth20',
11+
'FastAPIOAuth20',
12+
'LinuxDoOAuth20',
13+
]
614

715
from .clients.feishu import FeiShuOAuth20
816
from .clients.gitee import GiteeOAuth20
917
from .clients.github import GitHubOAuth20
1018
from .clients.google import GoogleOAuth20
19+
from .clients.linuxdo import LinuxDoOAuth20
1120
from .clients.oschina import OSChinaOAuth20
1221
from .integrations.fastapi import OAuth20 as FastAPIOAuth20

fastapi_oauth20/clients/feishu.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
AUTHORIZE_ENDPOINT = 'https://passport.feishu.cn/suite/passport/oauth/authorize'
88
ACCESS_TOKEN_ENDPOINT = 'https://passport.feishu.cn/suite/passport/oauth/token'
99
REFRESH_TOKEN_ENDPOINT = AUTHORIZE_ENDPOINT
10+
REVOKE_TOKEN_ENDPOINT = None
1011
DEFAULT_SCOPES = ['contact:user.employee_id:readonly', 'contact:user.base:readonly', 'contact:user.email:readonly']
1112
PROFILE_ENDPOINT = 'https://passport.feishu.cn/suite/passport/oauth/userinfo'
1213

@@ -19,7 +20,7 @@ def __init__(self, client_id: str, client_secret: str):
1920
authorize_endpoint=AUTHORIZE_ENDPOINT,
2021
access_token_endpoint=ACCESS_TOKEN_ENDPOINT,
2122
refresh_token_endpoint=REFRESH_TOKEN_ENDPOINT,
22-
revoke_token_endpoint=None,
23+
revoke_token_endpoint=REVOKE_TOKEN_ENDPOINT,
2324
oauth_callback_route_name='feishu',
2425
default_scopes=DEFAULT_SCOPES,
2526
)

fastapi_oauth20/clients/gitee.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
AUTHORIZE_ENDPOINT = 'https://gitee.com/oauth/authorize'
88
ACCESS_TOKEN_ENDPOINT = 'https://gitee.com/oauth/token'
99
REFRESH_TOKEN_ENDPOINT = ACCESS_TOKEN_ENDPOINT
10+
REVOKE_TOKEN_ENDPOINT = None
1011
DEFAULT_SCOPES = ['user_info']
1112
PROFILE_ENDPOINT = 'https://gitee.com/api/v5/user'
1213

@@ -19,7 +20,7 @@ def __init__(self, client_id: str, client_secret: str):
1920
authorize_endpoint=AUTHORIZE_ENDPOINT,
2021
access_token_endpoint=ACCESS_TOKEN_ENDPOINT,
2122
refresh_token_endpoint=REFRESH_TOKEN_ENDPOINT,
22-
revoke_token_endpoint=None,
23+
revoke_token_endpoint=REVOKE_TOKEN_ENDPOINT,
2324
oauth_callback_route_name='gitee',
2425
default_scopes=DEFAULT_SCOPES,
2526
)

fastapi_oauth20/clients/github.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66

77
AUTHORIZE_ENDPOINT = 'https://github.com/login/oauth/authorize'
88
ACCESS_TOKEN_ENDPOINT = 'https://github.com/login/oauth/access_token'
9+
REFRESH_TOKEN_ENDPOINT = None
10+
REVOKE_TOKEN_ENDPOINT = None
911
DEFAULT_SCOPES = ['user', 'user:email']
1012
PROFILE_ENDPOINT = 'https://api.github.com/user'
11-
EMAILS_ENDPOINT = 'https://api.github.com/user/emails'
1213

1314

1415
class GitHubOAuth20(OAuth20Base):
@@ -18,8 +19,8 @@ def __init__(self, client_id: str, client_secret: str):
1819
client_secret=client_secret,
1920
authorize_endpoint=AUTHORIZE_ENDPOINT,
2021
access_token_endpoint=ACCESS_TOKEN_ENDPOINT,
21-
refresh_token_endpoint=None,
22-
revoke_token_endpoint=None,
22+
refresh_token_endpoint=REFRESH_TOKEN_ENDPOINT,
23+
revoke_token_endpoint=REVOKE_TOKEN_ENDPOINT,
2324
oauth_callback_route_name='github',
2425
default_scopes=DEFAULT_SCOPES,
2526
)
@@ -35,7 +36,7 @@ async def get_userinfo(self, access_token: str) -> dict:
3536

3637
email = res.get('email')
3738
if email is None:
38-
response = await client.get(EMAILS_ENDPOINT)
39+
response = await client.get(f'{PROFILE_ENDPOINT}/emails')
3940
await self.raise_httpx_oauth20_errors(response)
4041

4142
emails = response.json()

fastapi_oauth20/clients/google.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def __init__(self, client_id: str, client_secret: str):
2626
)
2727

2828
async def get_userinfo(self, access_token: str) -> dict:
29-
"""Gets user info from Google"""
29+
"""Get user info from Google"""
3030
headers = {'Authorization': f'Bearer {access_token}'}
3131
async with httpx.AsyncClient() as client:
3232
response = await client.get(PROFILE_ENDPOINT, headers=headers)

fastapi_oauth20/clients/linuxdo.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
import httpx
4+
5+
from fastapi_oauth20.oauth20 import OAuth20Base
6+
7+
AUTHORIZE_ENDPOINT = 'https://connect.linux.do/oauth2/authorize'
8+
ACCESS_TOKEN_ENDPOINT = 'https://connect.linux.do/oauth2/token'
9+
REFRESH_TOKEN_ENDPOINT = ACCESS_TOKEN_ENDPOINT
10+
REVOKE_TOKEN_ENDPOINT = None
11+
DEFAULT_SCOPES = None
12+
PROFILE_ENDPOINT = 'https://connect.linux.do/api/user'
13+
14+
15+
class LinuxDoOAuth20(OAuth20Base):
16+
def __init__(self, client_id: str, client_secret: str):
17+
super().__init__(
18+
client_id=client_id,
19+
client_secret=client_secret,
20+
authorize_endpoint=AUTHORIZE_ENDPOINT,
21+
access_token_endpoint=ACCESS_TOKEN_ENDPOINT,
22+
refresh_token_endpoint=REFRESH_TOKEN_ENDPOINT,
23+
revoke_token_endpoint=REVOKE_TOKEN_ENDPOINT,
24+
oauth_callback_route_name='linuxdo',
25+
default_scopes=DEFAULT_SCOPES,
26+
)
27+
28+
async def get_access_token(self, code: str, redirect_uri: str, code_verifier: str | None = None) -> dict:
29+
"""Obtain the token based on the Linux do authorization method"""
30+
data = {
31+
'code': code,
32+
'redirect_uri': redirect_uri,
33+
'grant_type': 'authorization_code',
34+
}
35+
36+
auth = httpx.BasicAuth(self.client_id, self.client_secret)
37+
38+
if code_verifier:
39+
data.update({'code_verifier': code_verifier})
40+
async with httpx.AsyncClient(auth=auth) as client:
41+
response = await client.post(
42+
self.access_token_endpoint,
43+
data=data,
44+
headers=self.request_headers,
45+
)
46+
await self.raise_httpx_oauth20_errors(response)
47+
48+
res = response.json()
49+
50+
return res
51+
52+
async def get_userinfo(self, access_token: str) -> dict:
53+
"""Get user info from Linux Do"""
54+
headers = {'Authorization': f'Bearer {access_token}'}
55+
async with httpx.AsyncClient() as client:
56+
response = await client.get(PROFILE_ENDPOINT, headers=headers)
57+
await self.raise_httpx_oauth20_errors(response)
58+
59+
res = response.json()
60+
61+
return res

fastapi_oauth20/clients/oschina.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
AUTHORIZE_ENDPOINT = 'https://www.oschina.net/action/oauth2/authorize'
88
ACCESS_TOKEN_ENDPOINT = 'https://www.oschina.net/action/openapi/token'
99
REFRESH_TOKEN_ENDPOINT = ACCESS_TOKEN_ENDPOINT
10+
REVOKE_TOKEN_ENDPOINT = None
11+
DEFAULT_SCOPES = None
1012
PROFILE_ENDPOINT = 'https://www.oschina.net/action/openapi/user'
1113

1214

@@ -18,9 +20,9 @@ def __init__(self, client_id: str, client_secret: str):
1820
authorize_endpoint=AUTHORIZE_ENDPOINT,
1921
access_token_endpoint=ACCESS_TOKEN_ENDPOINT,
2022
refresh_token_endpoint=REFRESH_TOKEN_ENDPOINT,
21-
revoke_token_endpoint=None,
23+
revoke_token_endpoint=REVOKE_TOKEN_ENDPOINT,
2224
oauth_callback_route_name='oschina',
23-
default_scopes=None,
25+
default_scopes=DEFAULT_SCOPES,
2426
)
2527

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

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,12 @@ order-by-type = true
5353
quote-style = "single"
5454

5555
[tool.pdm]
56-
package-type = "library"
56+
distribution = true
5757
version = { source = "file", path = "fastapi_oauth20/__init__.py" }
5858

59+
[tool.pdm.scripts]
60+
lint = "pre-commit run --all-files"
61+
5962
[build-system]
6063
requires = ["pdm-backend"]
6164
build-backend = "pdm.backend"

0 commit comments

Comments
 (0)