Coverage for providers/github.py: 36%
39 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-23 11:16 -0600
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-23 11:16 -0600
1import datetime
3import requests
5from plain.oauth.exceptions import OAuthError
6from plain.oauth.providers import OAuthProvider, OAuthToken, OAuthUser
7from plain.utils import timezone
10class GitHubOAuthProvider(OAuthProvider):
11 authorization_url = "https://github.com/login/oauth/authorize"
13 github_token_url = "https://github.com/login/oauth/access_token"
14 github_user_url = "https://api.github.com/user"
15 github_emails_url = "https://api.github.com/user/emails"
17 def _get_token(self, request_data):
18 response = requests.post(
19 self.github_token_url,
20 headers={
21 "Accept": "application/json",
22 },
23 data=request_data,
24 )
25 response.raise_for_status()
26 data = response.json()
28 oauth_token = OAuthToken(
29 access_token=data["access_token"],
30 )
32 # Expiration and refresh tokens are optional in GitHub depending on the app
33 if "expires_in" in data:
34 oauth_token.access_token_expires_at = timezone.now() + datetime.timedelta(
35 seconds=data["expires_in"]
36 )
38 if "refresh_token" in data:
39 oauth_token.refresh_token = data["refresh_token"]
41 if "refresh_token_expires_in" in data:
42 oauth_token.refresh_token_expires_at = timezone.now() + datetime.timedelta(
43 seconds=data["refresh_token_expires_in"]
44 )
46 return oauth_token
48 def get_oauth_token(self, *, code, request):
49 return self._get_token(
50 {
51 "client_id": self.get_client_id(),
52 "client_secret": self.get_client_secret(),
53 "code": code,
54 }
55 )
57 def refresh_oauth_token(self, *, oauth_token):
58 return self._get_token(
59 {
60 "client_id": self.get_client_id(),
61 "client_secret": self.get_client_secret(),
62 "refresh_token": oauth_token.refresh_token,
63 "grant_type": "refresh_token",
64 }
65 )
67 def get_oauth_user(self, *, oauth_token):
68 response = requests.get(
69 self.github_user_url,
70 headers={
71 "Accept": "application/json",
72 "Authorization": f"token {oauth_token.access_token}",
73 },
74 )
75 response.raise_for_status()
76 data = response.json()
77 user_id = data["id"]
78 username = data["login"]
80 # Use the verified, primary email address (not the public profile email, which is optional anyway)
81 response = requests.get(
82 self.github_emails_url,
83 headers={
84 "Accept": "application/json",
85 "Authorization": f"token {oauth_token.access_token}",
86 },
87 )
88 response.raise_for_status()
90 try:
91 verified_primary_email = [
92 x["email"] for x in response.json() if x["primary"] and x["verified"]
93 ][0]
94 except IndexError:
95 raise OAuthError("A verified primary email address is required on GitHub")
97 return OAuthUser(
98 id=user_id,
99 email=verified_primary_email,
100 username=username,
101 )