Coverage for providers/github.py: 36%

39 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-23 11:16 -0600

1import datetime 

2 

3import requests 

4 

5from plain.oauth.exceptions import OAuthError 

6from plain.oauth.providers import OAuthProvider, OAuthToken, OAuthUser 

7from plain.utils import timezone 

8 

9 

10class GitHubOAuthProvider(OAuthProvider): 

11 authorization_url = "https://github.com/login/oauth/authorize" 

12 

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" 

16 

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() 

27 

28 oauth_token = OAuthToken( 

29 access_token=data["access_token"], 

30 ) 

31 

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 ) 

37 

38 if "refresh_token" in data: 

39 oauth_token.refresh_token = data["refresh_token"] 

40 

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 ) 

45 

46 return oauth_token 

47 

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 ) 

56 

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 ) 

66 

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"] 

79 

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() 

89 

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") 

96 

97 return OAuthUser( 

98 id=user_id, 

99 email=verified_primary_email, 

100 username=username, 

101 )