Skip to content

Commit

Permalink
Add GitHub OAuth2 (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
wu-clan authored Feb 6, 2024
1 parent 4419f9e commit d9e2599
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 6 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@

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

感谢 [httpx_oauth](https://github.com/frankie567/httpx-oauth) 鼎力相助

#### TODO:

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

- [ ] library tests
- [x] Google
- [ ] 微信
- [ ] QQ
Expand All @@ -21,7 +20,7 @@
- [ ] 微博
- [ ] 百度
- [x] Gitee
- [ ] Github
- [x] Github
- [ ] 开源中国
- [ ] 阿里云
- [ ] TestHome
46 changes: 46 additions & 0 deletions src/fastapi_oauth20/clients/github.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import httpx

from fastapi_oauth20.oauth20 import OAuth20Base

AUTHORIZE_ENDPOINT = 'https://github.com/login/oauth/authorize'
ACCESS_TOKEN_ENDPOINT = 'https://github.com/login/oauth/access_token'
DEFAULT_SCOPES = ['user user:email']
PROFILE_ENDPOINT = 'https://api.github.com/user'
EMAILS_ENDPOINT = 'https://api.github.com/user/emails'


class GitHubOAuth20(OAuth20Base):
def __init__(self, client_id: str, client_secret: str):
super().__init__(
client_id=client_id,
client_secret=client_secret,
authorize_endpoint=AUTHORIZE_ENDPOINT,
access_token_endpoint=ACCESS_TOKEN_ENDPOINT,
refresh_token_endpoint=None,
revoke_token_endpoint=None,
oauth_callback_route_name='github',
default_scopes=DEFAULT_SCOPES,
)

async def get_userinfo(self, access_token: str) -> dict:
"""Get user info from GitHub"""
headers = {'Authorization': f'Bearer {access_token}'}
async with httpx.AsyncClient(headers=headers) as client:
response = await client.get(PROFILE_ENDPOINT)
await self.raise_httpx_oauth20_errors(response)

res = response.json()

email = res.get('email')
if email is None:
response = await client.get(EMAILS_ENDPOINT)
await self.raise_httpx_oauth20_errors(response)

emails = response.json()

email = next((email['email'] for email in emails if email.get('primary')), emails[0]['email'])
res['email'] = email

return res
22 changes: 19 additions & 3 deletions src/fastapi_oauth20/oauth20.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ def __init__(
self.oauth_callback_route_name = oauth_callback_route_name
self.default_scopes = default_scopes

self.request_headers = {
'Accept': 'application/json',
}

async def get_authorization_url(
self,
redirect_uri: str,
Expand Down Expand Up @@ -86,7 +90,11 @@ async def get_access_token(self, code: str, redirect_uri: str, code_verifier: st
if code_verifier:
data.update({'code_verifier': code_verifier})
async with httpx.AsyncClient() as client:
response = await client.post(self.access_token_endpoint, data=data)
response = await client.post(
self.access_token_endpoint,
data=data,
headers=self.request_headers,
)
await self.raise_httpx_oauth20_errors(response)

res = response.json()
Expand All @@ -104,7 +112,11 @@ async def refresh_token(self, refresh_token: str) -> dict:
'grant_type': 'refresh_token',
}
async with httpx.AsyncClient() as client:
response = await client.post(self.refresh_token_endpoint, data=data)
response = await client.post(
self.refresh_token_endpoint,
data=data,
headers=self.request_headers,
)
await self.raise_httpx_oauth20_errors(response)

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

response = await client.post(self.revoke_token_endpoint, data=data)
response = await client.post(
self.revoke_token_endpoint,
data=data,
headers=self.request_headers,
)

await self.raise_httpx_oauth20_errors(response)

Expand Down

0 comments on commit d9e2599

Please sign in to comment.