Google’s Chrome 39 Sunsetting SHA-1

Google has announced that they are sunsetting SHA-1 (as used in certificate signatures for HTTPS) with Chrome 39 in November 2014. SHA-1 root certificates are not affected by this plan.

Most providers are offering free upgrades to SHA-2 certificates so be sure to contact yours to see if you qualify.

More info can be found on Google’s security blog.

Django Register with Oauth – Google

Before we begin, read the overview / disclaimer. Now for Google…

First off, ensure that you have registered your application with Google and have created the following in your settings file:

# https://code.google.com/apis/console/
GOOGLE_OAUTH2_CLIENT_ID = 'YOUR-APP-ID'
GOOGLE_OAUTH2_CLIENT_SECRET = 'YOUR-APP-SECRET'

view raw
settings.py
hosted with ❤ by GitHub

1) Create the redirect URL

The first step in the oauth handshake is to redirect to Google with some application specific data in the URL including your client ID, scope (what data you want access to) and redirect URL (where you want Google to send the user after they’ve approved / denied the application)

import urllib
from django.conf import settings
from django.core.urlresolvers import reverse
from django.core.context_processors import csrf
def get_authorization_url(request):
# URL to where we will redirect to
redirect_url = urllib.quote_plus(settings.SITE_URL + reverse('register_google'))
# create a unique state value for CSRF validation
request.session['google_state'] = unicode(csrf(request)['csrf_token'])
scope = urllib.quote_plus('https://www.googleapis.com/auth/userinfo.email') + '+' + \
urllib.quote_plus('https://www.googleapis.com/auth/userinfo.profile')
# redirect to google for approval
url = 'https://accounts.google.com/o/oauth2/auth?' \
+ 'scope=' + scope \
+ '&state=' + request.session['google_state'] \
+ '&redirect_uri=' + redirect_url \
+ '&response_type=code' \
+ '&client_id=' + settings.GOOGLE_OAUTH2_CLIENT_ID \
+ '&access_type=offline' \
+ '&approval_prompt=auto'
return url

view raw
google.py
hosted with ❤ by GitHub

2) Redirect to provider’s site

This url can now be used to redirect to Google (i.e. HttpResponseRedirect(url)):

3) Handle the response (approved or denied)

Once the user makes their choice to approve or deny, Google will redirect back to your redirect_url. You will need to verify the user approved the application:

def verify(request):
# Google will direct with state and code in the URL
# ?state=zNHRjuYO…&code=4/zK5F93g2we…
# ensure we have a session state and the state value is the same as what google returned
if 'google_state' not in request.session \
or 'state' not in request.GET \
or 'code' not in request.GET \
or request.session['google_state'] != request.GET['state']:
return False
else:
return True

view raw
google.py
hosted with ❤ by GitHub

If any of the above tests fail, we can safely assume the user either arrived at this page directly (i.e. by typing in the URL) or they denied the application. Either way we don’t want to proceed and should redirect them to the start of the registration flow (i.e. HttpResponseRedirect(reverse('register'))).

4 & 5) Get an access token and the user’s profile

At this point the user has authorized your application but you don’t have actual access to their data yet. To get that you’ll need to request an access token. Notice that we’re saving the access token to the user’s session as we don’t want to request it more than once during the registration flow.

Once you have the access token, you can then make the request for the user’s profile data:

import urllib2
import json
import re
from django.conf import settings
from django.core.urlresolvers import reverse
def get_user_data(request):
data = {}
# if we don't have a token yet, get one now
if 'google_access_token' not in request.session:
# URL to where we will redirect to
redirect_url = settings.SITE_URL + reverse('register_google')
# set the token URL
token_url = 'https://accounts.google.com/o/oauth2/token'
# set the post params
params = {
'code': request.GET['code'],
'client_id': settings.GOOGLE_OAUTH2_CLIENT_ID,
'client_secret': settings.GOOGLE_OAUTH2_CLIENT_SECRET,
'redirect_uri': redirect_url,
'grant_type': 'authorization_code'
}
# grab the token from google
response = urllib2.urlopen(token_url, urllib.urlencode(params)).read()
# parse the response
tokens = json.loads(response)
# save the token
request.session['google_access_token'] = tokens['access_token']
request.session['google_access_token_expires'] = token['expires_in']
# set the api URL using the token we just fetched
api_url = 'https://www.googleapis.com/oauth2/v1/userinfo?' \
+ 'access_token=' + request.session['google_access_token']
# get the user's data from google
response = urllib2.urlopen(api_url).read()
user = json.loads(response)
# get the user's info
data['user_id'] = user['id']
data['username'] = re.sub('[^0-9a-zA-Z]+', '', user['email'].split('@')[0]).lower()
data['email'] = user['email']
data['full_name'] = user['name']
data['first_name'] = user['given_name']
data['last_name'] = user['family_name']
data['timezone'] = user['timezone'] if 'timezone' in user else None
data['picture'] = user['picture'] if 'picture' in user else \
'https://lh4.googleusercontent.com/-yMO06Y5jMmA/AAAAAAAAAAI/AAAAAAAAAAA/mV7mqn9o52w/s250-c-k/photo.jpg'
return data

view raw
google.py
hosted with ❤ by GitHub

That’s it! You can now use the user’s profile information to pre-fill a registration form, perhaps skipping over fields where you already have a required value such as an email address or first and last name. Just be sure to save their Google ID along with their profile so you can use it validate them in the future. You should also save the access_token and expires values so that you can make future requests to the API for this user or refresh the access token when it has expired.

In a future post I’ll be looking at how to detect an already registered user as well as provide a login with this provider button.