forked from omab/django-social-auth
/
github.py
113 lines (92 loc) · 3.66 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
107
108
109
110
111
112
113
"""
GitHub OAuth support.
This contribution adds support for GitHub OAuth service. The settings
GITHUB_APP_ID and GITHUB_API_SECRET must be defined with the values
given by GitHub application registration process.
Extended permissions are supported by defining GITHUB_EXTENDED_PERMISSIONS
setting, it must be a list of values to request.
By default account id and token expiration time are stored in extra_data
field, check OAuthBackend class for details on how to extend it.
"""
import cgi
from urllib import urlencode, urlopen
from django.utils import simplejson
from django.contrib.auth import authenticate
from social_auth.utils import setting
from social_auth.backends import BaseOAuth, OAuthBackend, USERNAME
from social_auth.backends.exceptions import AuthFailed
# GitHub configuration
GITHUB_AUTHORIZATION_URL = 'https://github.com/login/oauth/authorize?'
GITHUB_ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token?'
GITHUB_USER_DATA_URL = 'https://api.github.com/user?'
class GithubBackend(OAuthBackend):
"""Github OAuth authentication backend"""
name = 'github'
# Default extra data to store
EXTRA_DATA = [
('id', 'id'),
('expires', setting('SOCIAL_AUTH_EXPIRATION', 'expires'))
]
def get_user_details(self, response):
"""Return user details from Github account"""
return {USERNAME: response.get('login'),
'email': response.get('email') or '',
'first_name': response.get('name')}
class GithubAuth(BaseOAuth):
"""Github OAuth mechanism"""
AUTH_BACKEND = GithubBackend
def auth_url(self):
"""Returns redirect url"""
args = {
'client_id': setting('GITHUB_APP_ID'),
'redirect_uri': self.redirect_uri
}
if setting('GITHUB_EXTENDED_PERMISSIONS'):
args['scope'] = ','.join(setting('GITHUB_EXTENDED_PERMISSIONS'))
args.update(self.auth_extra_arguments())
return GITHUB_AUTHORIZATION_URL + urlencode(args)
def auth_complete(self, *args, **kwargs):
"""Returns user, might be logged in"""
if 'code' not in self.data:
error = self.data.get('error') or 'unknown error'
raise AuthFailed(self, error)
url = GITHUB_ACCESS_TOKEN_URL + urlencode({
'client_id': setting('GITHUB_APP_ID'),
'redirect_uri': self.redirect_uri,
'client_secret': setting('GITHUB_API_SECRET'),
'code': self.data['code']
})
response = cgi.parse_qs(urlopen(url).read())
if response.get('error'):
error = self.data.get('error') or 'unknown error'
raise AuthFailed(self, error)
access_token = response['access_token'][0]
data = self.user_data(access_token)
if data is not None:
if 'error' in data:
error = self.data.get('error') or 'unknown error'
raise AuthFailed(self, error)
data['access_token'] = access_token
kwargs.update({
'auth': self,
'response': data,
self.AUTH_BACKEND.name: True
})
return authenticate(*args, **kwargs)
def user_data(self, access_token):
"""Loads user data from service"""
url = GITHUB_USER_DATA_URL + urlencode({
'access_token': access_token
})
try:
return simplejson.load(urlopen(url))
except ValueError:
return None
@classmethod
def enabled(cls):
"""Return backend enabled status by checking basic settings"""
return setting('GITHUB_APP_ID') and setting('GITHUB_API_SECRET')
# Backend definition
BACKENDS = {
'github': GithubAuth,
}