1
0
Fork 1
mirror of https://github.com/coddingtonbear/django-mailbox.git synced 2026-07-09 22:38:19 +02:00

Adding gmail oauth2 authentication

python-social-auth is an optional dependency
This commit is contained in:
Alex Lovell-Troy 2014-05-28 20:10:45 -06:00
parent 6336e910bd
commit cc4e72305d
4 changed files with 184 additions and 1 deletions

View file

@ -0,0 +1,126 @@
from social.apps.django_app.default.models import UserSocialAuth
import requests
from django.conf import settings
class AccessTokenNotFound(Exception):
pass
class RefreshTokenNotFound(Exception):
pass
def get_google_consumer_key():
return settings.SOCIAL_AUTH_GOOGLE_OAUTH2_KEY
def get_google_consumer_secret():
return settings.SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET
def get_google_access_token(email):
# TODO: This should be cacheable
try:
me = UserSocialAuth.objects.get(uid=email, provider="google-oauth2")
return me.extra_data['access_token']
except (UserSocialAuth.DoesNotExist, KeyError):
raise AccessTokenNotFound
def update_google_extra_data(email, extra_data):
try:
me = UserSocialAuth.objects.get(uid=email, provider="google-oauth2")
me.extra_data = extra_data
me.save()
except (UserSocialAuth.DoesNotExist, KeyError):
raise AccessTokenNotFound
def get_google_refresh_token(email):
try:
me = UserSocialAuth.objects.get(uid=email, provider="google-oauth2")
return me.extra_data['refresh_token']
except (UserSocialAuth.DoesNotExist, KeyError):
raise RefreshTokenNotFound
def google_api_get(email, url):
headers = dict(
Authorization="Bearer %s" % get_google_access_token(email),
)
r = requests.get(url, headers=headers)
print "I got a %s" % r.status_code
if r.status_code == 401:
# Go use the refresh token
refresh_authorization(email)
r = requests.get(url, headers=headers)
print "I got a %s" % r.status_code
if r.status_code == 200:
try:
return r.json()
except ValueError:
print "returning text"
return r.text
def google_api_post(email, url, post_data, authorized=True):
# TODO: Make this a lot less ugly. especially the 401 handling
headers = dict()
if authorized is True:
headers.update(dict(
Authorization="Bearer %s" % get_google_access_token(email),
))
r = requests.post(url, headers=headers, data=post_data)
if r.status_code == 401:
refresh_authorization(email)
r = requests.post(url, headers=headers, data=post_data)
if r.status_code == 200:
try:
return r.json()
except ValueError:
print "returning text"
return r.text
def refresh_authorization(email):
refresh_token = get_google_refresh_token(email)
post_data = dict(
refresh_token=refresh_token,
client_id=get_google_consumer_key(),
client_secret=get_google_consumer_secret(),
grant_type='refresh_token',
)
results = google_api_post(
email,
"https://accounts.google.com/o/oauth2/token?access_type=offline",
post_data,
authorized=False)
results.update({'refresh_token': refresh_token})
update_google_extra_data(email, results)
def fetch_user_info(email):
result = google_api_get(
email,
"https://www.googleapis.com/oauth2/v1/userinfo?alt=json"
)
return result
def fetch_google_contacts(email, limit=10000):
result = google_api_get(
email,
"https://www.google.com/m8/feeds/contacts/default/full?v=3.0&alt=json&max-results=%s" % limit
)
entries = result['feed']['entry']
valid_entries = [x for x in entries if u'gd$email' in x.keys() and u'gd$name' in x.keys()]
contacts = []
for each in valid_entries:
try:
name = each[u'gd$name'][u'gd$fullName'][u'$t']
except KeyError:
name = None
for each_email in each[u'gd$email']:
contacts.append(dict(name=name, email=each_email[u'address']))
return contacts

View file

@ -5,3 +5,4 @@ from django_mailbox.transports.mbox import MboxTransport
from django_mailbox.transports.babyl import BabylTransport
from django_mailbox.transports.mh import MHTransport
from django_mailbox.transports.mmdf import MMDFTransport
from django_mailbox.transports.gmail import GmailImapTransport

View file

@ -0,0 +1,49 @@
from django_mailbox.transports.imap import ImapTransport
class GmailImapTransport(ImapTransport):
def connect(self, username, password):
# Try to use oauth2 first. It's much safer
try:
self._connect_oauth(username)
except (TypeError, ValueError), e:
print " Couldn't do oauth2", e
self.server = self.transport(self.hostname, self.port)
typ, msg = self.server.login(username, password)
self.server.select()
def _connect_oauth(self, username):
# username should be an email address that has already been authorized
# for gmail access
try:
from django_mailbox.google_utils import (
get_google_access_token,
fetch_user_info,
AccessTokenNotFound,
)
except ImportError:
raise ValueError(
"Install python-social-auth to use oauth2 auth for gmail"
)
access_token = None
while access_token is None:
try:
access_token = get_google_access_token(username)
google_email_address = fetch_user_info(username)['email']
except TypeError:
# This means that the google process took too long
# Trying again is the right thing to do
pass
except AccessTokenNotFound:
raise ValueError(
"No Token available in python-social-auth for %s" % username
)
auth_string = 'user=%s\1auth=Bearer %s\1\1' % (
google_email_address,
access_token
)
self.server = self.transport(self.hostname, self.port)
self.server.authenticate('XOAUTH2', lambda x: auth_string)
self.server.select()