-
Notifications
You must be signed in to change notification settings - Fork 52
/
github.py
106 lines (96 loc) · 3.62 KB
/
github.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
"""GitHub OAuth2 client."""
from __future__ import annotations
import requests
from flask import current_app, redirect, request
from furl import furl
from sentry_sdk import capture_exception
from baseframe import _
from ..registry import LoginCallbackError, LoginProvider, LoginProviderData
from ..typing import ReturnView
__all__ = ['GitHubProvider']
class GitHubProvider(LoginProvider):
at_username = True
auth_url = 'https://github.com/login/oauth/authorize'
token_url = 'https://github.com/login/oauth/access_token' # noqa: S105
user_info_url = 'https://api.github.com/user'
user_emails_url = 'https://api.github.com/user/emails'
def do(self, callback_url: str) -> ReturnView:
return redirect(
furl(self.auth_url)
.add(
{
'client_id': self.key,
'redirect_uri': callback_url,
'scope': 'user:email',
}
)
.url
)
def callback(self) -> LoginProviderData:
if request.args.get('error'):
if request.args['error'] == 'user_denied':
raise LoginCallbackError(_("You denied the GitHub login request"))
if request.args['error'] == 'redirect_uri_mismatch':
# TODO: Log this as an exception for the server admin to look at
raise LoginCallbackError(
_("This server’s callback URL is misconfigured")
)
raise LoginCallbackError(_("Unknown failure"))
code = request.args.get('code')
try:
response = requests.post(
self.token_url,
timeout=30,
headers={'Accept': 'application/json'},
params={
'client_id': self.key,
'client_secret': self.secret,
'code': code,
},
).json()
if 'error' in response:
raise LoginCallbackError(response['error'])
ghinfo = requests.get(
self.user_info_url,
timeout=30,
headers={'Authorization': f'token {response["access_token"]}'},
).json()
ghemails = requests.get(
self.user_emails_url,
timeout=30,
headers={
'Accept': 'application/vnd.github.v3+json',
'Authorization': f'token {response["access_token"]}',
},
).json()
except (
requests.exceptions.RequestException,
requests.exceptions.JSONDecodeError,
) as exc:
current_app.logger.error("GitHub OAuth2 error: %s", repr(exc))
capture_exception(exc)
raise LoginCallbackError(
_("GitHub had an intermittent problem. Try again?")
) from exc
email = None
if ghemails and isinstance(ghemails, list | tuple):
emails = [
item['email']
for item in ghemails
if item.get('verified')
and not item['email'].endswith('@users.noreply.github.com')
]
else:
emails = []
if emails:
email = emails[0]
return LoginProviderData(
email=email,
emails=emails,
userid=ghinfo['login'],
username=ghinfo['login'],
fullname=(ghinfo.get('name') or '').strip(),
avatar_url=ghinfo.get('avatar_url'),
oauth_token=response['access_token'],
oauth_token_type=response['token_type'],
)