Coverage for providers/github.py: 36%

1 import datetime

2

3 import requests

4

5 from plain.oauth.exceptions import OAuthError

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

7 from plain.utils import timezone

8

9

10 class 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 )