Skip to content

Commit d9e2599

Browse files
authored
Add GitHub OAuth2 (#3)
1 parent 4419f9e commit d9e2599

File tree

3 files changed

+67
-6
lines changed

3 files changed

+67
-6
lines changed

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@
66

77
我们的目标是集成多个 CN 第三方客户端,敬请期待
88

9-
感谢 [httpx_oauth](https://github.com/frankie567/httpx-oauth) 鼎力相助
10-
119
#### TODO:
1210

1311
如果我们能够很容易获取测试客户端,对接将会很快发生
1412

13+
- [ ] library tests
1514
- [x] Google
1615
- [ ] 微信
1716
- [ ] QQ
@@ -21,7 +20,7 @@
2120
- [ ] 微博
2221
- [ ] 百度
2322
- [x] Gitee
24-
- [ ] Github
23+
- [x] Github
2524
- [ ] 开源中国
2625
- [ ] 阿里云
2726
- [ ] TestHome

src/fastapi_oauth20/clients/github.py

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+
import httpx
4+
5+
from fastapi_oauth20.oauth20 import OAuth20Base
6+
7+
AUTHORIZE_ENDPOINT = 'https://github.com/login/oauth/authorize'
8+
ACCESS_TOKEN_ENDPOINT = 'https://github.com/login/oauth/access_token'
9+
DEFAULT_SCOPES = ['user user:email']
10+
PROFILE_ENDPOINT = 'https://api.github.com/user'
11+
EMAILS_ENDPOINT = 'https://api.github.com/user/emails'
12+
13+
14+
class GitHubOAuth20(OAuth20Base):
15+
def __init__(self, client_id: str, client_secret: str):
16+
super().__init__(
17+
client_id=client_id,
18+
client_secret=client_secret,
19+
authorize_endpoint=AUTHORIZE_ENDPOINT,
20+
access_token_endpoint=ACCESS_TOKEN_ENDPOINT,
21+
refresh_token_endpoint=None,
22+
revoke_token_endpoint=None,
23+
oauth_callback_route_name='github',
24+
default_scopes=DEFAULT_SCOPES,
25+
)
26+
27+
async def get_userinfo(self, access_token: str) -> dict:
28+
"""Get user info from GitHub"""
29+
headers = {'Authorization': f'Bearer {access_token}'}
30+
async with httpx.AsyncClient(headers=headers) as client:
31+
response = await client.get(PROFILE_ENDPOINT)
32+
await self.raise_httpx_oauth20_errors(response)
33+
34+
res = response.json()
35+
36+
email = res.get('email')
37+
if email is None:
38+
response = await client.get(EMAILS_ENDPOINT)
39+
await self.raise_httpx_oauth20_errors(response)
40+
41+
emails = response.json()
42+
43+
email = next((email['email'] for email in emails if email.get('primary')), emails[0]['email'])
44+
res['email'] = email
45+
46+
return res

src/fastapi_oauth20/oauth20.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ def __init__(
3030
self.oauth_callback_route_name = oauth_callback_route_name
3131
self.default_scopes = default_scopes
3232

33+
self.request_headers = {
34+
'Accept': 'application/json',
35+
}
36+
3337
async def get_authorization_url(
3438
self,
3539
redirect_uri: str,
@@ -86,7 +90,11 @@ async def get_access_token(self, code: str, redirect_uri: str, code_verifier: st
8690
if code_verifier:
8791
data.update({'code_verifier': code_verifier})
8892
async with httpx.AsyncClient() as client:
89-
response = await client.post(self.access_token_endpoint, data=data)
93+
response = await client.post(
94+
self.access_token_endpoint,
95+
data=data,
96+
headers=self.request_headers,
97+
)
9098
await self.raise_httpx_oauth20_errors(response)
9199

92100
res = response.json()
@@ -104,7 +112,11 @@ async def refresh_token(self, refresh_token: str) -> dict:
104112
'grant_type': 'refresh_token',
105113
}
106114
async with httpx.AsyncClient() as client:
107-
response = await client.post(self.refresh_token_endpoint, data=data)
115+
response = await client.post(
116+
self.refresh_token_endpoint,
117+
data=data,
118+
headers=self.request_headers,
119+
)
108120
await self.raise_httpx_oauth20_errors(response)
109121

110122
res = response.json()
@@ -122,7 +134,11 @@ async def revoke_token(self, token: str, token_type_hint: str | None = None) ->
122134
if token_type_hint is not None:
123135
data.update({'token_type_hint': token_type_hint})
124136

125-
response = await client.post(self.revoke_token_endpoint, data=data)
137+
response = await client.post(
138+
self.revoke_token_endpoint,
139+
data=data,
140+
headers=self.request_headers,
141+
)
126142

127143
await self.raise_httpx_oauth20_errors(response)
128144

0 commit comments

Comments
 (0)