For this last post in the series on oauth we’ll take a look at Microsoft (aka Hotmail, aka Live). But before we begin, read the overview / disclaimer. Now for Microsft …
First off, ensure that you have registered your application with Microsoft and have created the following in your settings file:
# https://manage.dev.live.com/Applications/Index | |
MICROSOFT_CLIENT_ID = 'YOUR-APP-ID' | |
MICROSOFT_CLIENT_SECRET = 'YOUR-APP-SECRET' |
1) Create the redirect URL
The first step in the oauth handshake is to redirect to Microsoft 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 Microsoft 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): | |
# encode the url | |
redirect_url = urllib.quote_plus(settings.SITE_URL + reverse('register_microsoft')) | |
# create a unique state value for CSRF validation | |
request.session['microsoft_state'] = unicode(csrf(request)['csrf_token']) | |
# redirect to microsoft for approval | |
url = 'https://login.live.com/oauth20_authorize.srf?' \ | |
+ 'client_id=' + settings.MICROSOFT_CLIENT_ID \ | |
+ '&redirect_uri=' + redirect_url \ | |
+ '&scope=wl.signin%20wl.basic%20wl.emails' \ | |
+ '&state=' + request.session['microsoft_state'] \ | |
+ '&response_type=code' | |
return url |
2) Redirect to provider’s site
This url can now be used to redirect to Microsoft (i.e. HttpResponseRedirect(url)
):
3) Handle the response (approved or denied)
Once the user makes their choice to approve or deny, Microsoft will redirect back to your redirect_url. You will need to verify the user approved the application:
def verify(request): | |
# ensure we have a session state and the state value is the same as what microsoft returned | |
if 'microsoft_state' not in request.session \ | |
or 'state' not in request.GET \ | |
or 'code' not in request.GET \ | |
or request.session['microsoft_state'] != request.GET['state']: | |
return False | |
else: | |
return True |
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, redirect_url): | |
data = {} | |
# if we don't have a token yet, get one now | |
if 'microsoft_access_token' not in request.session: | |
# set the token URL | |
url = 'https://login.live.com/oauth20_token.srf' | |
# set the post params | |
params = { | |
'code': request.GET['code'], | |
'client_id': settings.MICROSOFT_CLIENT_ID, | |
'client_secret': settings.MICROSOFT_CLIENT_SECRET, | |
'redirect_uri': redirect_url, | |
'grant_type': 'authorization_code' | |
} | |
# grab the token from microsoft | |
response = urllib2.urlopen(url, urllib.urlencode(params)).read() | |
# parse the response | |
tokens = json.loads(response) | |
# save the token | |
request.session['microsoft_access_token'] = tokens['access_token'] | |
# set the url using the user id | |
url = 'https://apis.live.net/v5.0/me?access_token=' + request.session['microsoft_access_token'] | |
# get the user's data from microsoft | |
response = urllib2.urlopen(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['name']).lower() if user['name'] is not None else '' | |
data['email'] = user['emails']['account'] if 'account' in user['emails'] else '' | |
data['full_name'] = user['name'] | |
data['first_name'] = user['first_name'] | |
data['last_name'] = user['last_name'] | |
data['timezone'] = None | |
# Microsoft doesn't appear to return the profile picture even if one is set, so use their default image instead | |
data['picture'] = 'https://secure.wlxrs.com/$live.controls.images/ic/bluemanmxxl.png' | |
# clean up the name | |
data['first_name'] = Formatting.clean_name(data['first_name']) | |
data['last_name'] = Formatting.clean_name(data['last_name']) | |
return data |
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 Microsoft 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.