Switch branches/tags
pre-1.0a hudson-python-oauth2-211 hudson-python-oauth2-210 hudson-python-oauth2-209 hudson-python-oauth2-208 hudson-python-oauth2-207 hudson-python-oauth2-206 hudson-python-oauth2-205 hudson-python-oauth2-204 hudson-python-oauth2-203 hudson-python-oauth2-202 hudson-python-oauth2-201 hudson-python-oauth2-200 hudson-python-oauth2-199 hudson-python-oauth2-170 hudson-python-oauth2-169 hudson-python-oauth2-169-SUCCESS hudson-python-oauth2-168 hudson-python-oauth2-168-SUCCESS hudson-python-oauth2-167 hudson-python-oauth2-167-SUCCESS hudson-python-oauth2-166 hudson-python-oauth2-166-SUCCESS hudson-python-oauth2-165 hudson-python-oauth2-165-SUCCESS hudson-python-oauth2-164 hudson-python-oauth2-164-SUCCESS hudson-python-oauth2-163 hudson-python-oauth2-163-SUCCESS hudson-python-oauth2-162 hudson-python-oauth2-162-SUCCESS hudson-python-oauth2-161 hudson-python-oauth2-161-SUCCESS hudson-python-oauth2-160 hudson-python-oauth2-160-SUCCESS hudson-python-oauth2-159 hudson-python-oauth2-159-SUCCESS hudson-python-oauth2-158 hudson-python-oauth2-158-SUCCESS hudson-python-oauth2-157 hudson-python-oauth2-156 hudson-python-oauth2-155 hudson-python-oauth2-155-SUCCESS hudson-python-oauth2-154 hudson-python-oauth2-154-SUCCESS hudson-python-oauth2-153 hudson-python-oauth2-153-SUCCESS hudson-python-oauth2-152 hudson-python-oauth2-152-SUCCESS hudson-python-oauth2-151 hudson-python-oauth2-151-SUCCESS hudson-python-oauth2-150 hudson-python-oauth2-150-SUCCESS hudson-python-oauth2-149 hudson-python-oauth2-149-SUCCESS hudson-python-oauth2-148 hudson-python-oauth2-148-SUCCESS hudson-python-oauth2-147 hudson-python-oauth2-147-SUCCESS hudson-python-oauth2-146 hudson-python-oauth2-146-SUCCESS hudson-python-oauth2-145 hudson-python-oauth2-145-SUCCESS hudson-python-oauth2-144 hudson-python-oauth2-144-SUCCESS hudson-python-oauth2-143 hudson-python-oauth2-143-SUCCESS hudson-python-oauth2-142 hudson-python-oauth2-141 hudson-python-oauth2-140 hudson-python-oauth2-139 hudson-python-oauth2-138 hudson-python-oauth2-137 hudson-python-oauth2-136 hudson-python-oauth2-135 hudson-python-oauth2-134 hudson-python-oauth2-134-SUCCESS hudson-python-oauth2-133 hudson-python-oauth2-133-SUCCESS hudson-python-oauth2-132 hudson-python-oauth2-132-SUCCESS hudson-python-oauth2-131 hudson-python-oauth2-131-SUCCESS hudson-python-oauth2-130 hudson-python-oauth2-130-SUCCESS hudson-python-oauth2-129 hudson-python-oauth2-129-SUCCESS hudson-python-oauth2-128 hudson-python-oauth2-128-SUCCESS hudson-python-oauth2-127 hudson-python-oauth2-127-SUCCESS debian/1.5.211 debian/1.5.210 debian/1.5.209 debian/1.5.208 debian/1.5.207 debian/1.5.206 debian/1.5.205 debian/1.5.204 debian/1.5.203
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
358 lines (268 sloc) 14.8 KB


This code was originally forked from Leah Culver and Andy Smith's code. Some of the tests come from a fork by Vic Fryzel, while a revamped Request class and more tests were merged in from Mark Paschal's fork. A number of notable differences exist between this code and its forefathers:

  • 100% unit test coverage.
  • The DataStore object has been completely ripped out. While creating unit tests for the library I found several substantial bugs with the implementation and confirmed with Andy Smith that it was never fully baked.
  • Classes are no longer prefixed with OAuth.
  • The Request class now extends from dict.
  • The library is likely no longer compatible with Python 2.3.
  • The Client class works and extends from httplib2. It's a thin wrapper that handles automatically signing any normal HTTP request you might wish to make.

Signing a Request

import oauth2 as oauth
import time

# Set the API endpoint 
url = ""

# Set the base oauth_* parameters along with any other parameters required
# for the API call.
params = {
    'oauth_version': "1.0",
    'oauth_nonce': oauth.generate_nonce(),
    'oauth_timestamp': int(time.time())
    'user': 'joestump',
    'photoid': 555555555555

# Set up instances of our Token and Consumer. The Consumer.key and 
# Consumer.secret are given to you by the API provider. The Token.key and
# Token.secret is given to you after a three-legged authentication.
token = oauth.Token(key="tok-test-key", secret="tok-test-secret")
consumer = oauth.Consumer(key="con-test-key", secret="con-test-secret")

# Set our token/key parameters
params['oauth_token'] = token.key
params['oauth_consumer_key'] = consumer.key

# Create our request. Change method, etc. accordingly.
req = oauth.Request(method="GET", url=url, parameters=params)

# Sign the request.
signature_method = oauth.SignatureMethod_HMAC_SHA1()
req.sign_request(signature_method, consumer, token)

Using the Client

The oauth2.Client is based on httplib2 and works just as you'd expect it to. The only difference is the first two arguments to the constructor are an instance of oauth2.Consumer and oauth2.Token (oauth2.Token is only needed for three-legged requests).

import oauth2 as oauth

# Create your consumer with the proper key/secret.
consumer = oauth.Consumer(key="your-twitter-consumer-key", 

# Request token URL for Twitter.
request_token_url = ""

# Create our client.
client = oauth.Client(consumer)

# The OAuth Client request works just like httplib2 for the most part.
resp, content = client.request(request_token_url, "GET")
print resp
print content

Twitter Three-legged OAuth Example

Below is an example of how one would go through a three-legged OAuth flow to gain access to protected resources on Twitter. This is a simple CLI script, but can be easily translated to a web application.

import urlparse
import oauth2 as oauth

consumer_key = 'my_key_from_twitter'
consumer_secret = 'my_secret_from_twitter'

request_token_url = ''
access_token_url = ''
authorize_url = ''

consumer = oauth.Consumer(consumer_key, consumer_secret)
client = oauth.Client(consumer)

# Step 1: Get a request token. This is a temporary token that is used for 
# having the user authorize an access token and to sign the request to obtain 
# said access token.

resp, content = client.request(request_token_url, "GET")
if resp['status'] != '200':
    raise Exception("Invalid response %s." % resp['status'])

request_token = dict(urlparse.parse_qsl(content))

print "Request Token:"
print "    - oauth_token        = %s" % request_token['oauth_token']
print "    - oauth_token_secret = %s" % request_token['oauth_token_secret']

# Step 2: Redirect to the provider. Since this is a CLI script we do not 
# redirect. In a web application you would redirect the user to the URL
# below.

print "Go to the following link in your browser:"
print "%s?oauth_token=%s" % (authorize_url, request_token['oauth_token'])

# After the user has granted access to you, the consumer, the provider will
# redirect you to whatever URL you have told them to redirect to. You can 
# usually define this in the oauth_callback argument as well.
accepted = 'n'
while accepted.lower() == 'n':
    accepted = raw_input('Have you authorized me? (y/n) ')
oauth_verifier = raw_input('What is the PIN? ')

# Step 3: Once the consumer has redirected the user back to the oauth_callback
# URL you can request the access token the user has approved. You use the 
# request token to sign this request. After this is done you throw away the
# request token and use the access token returned. You should store this 
# access token somewhere safe, like a database, for future use.
token = oauth.Token(request_token['oauth_token'],
client = oauth.Client(consumer, token)

resp, content = client.request(access_token_url, "POST")
access_token = dict(urlparse.parse_qsl(content))

print "Access Token:"
print "    - oauth_token        = %s" % access_token['oauth_token']
print "    - oauth_token_secret = %s" % access_token['oauth_token_secret']
print "You may now access protected resources using the access tokens above." 

Logging into Django w/ Twitter

Twitter also has the ability to authenticate a user via an OAuth flow. This flow is exactly like the three-legged OAuth flow, except you send them to a slightly different URL to authorize them.

In this example we'll look at how you can implement this login flow using Django and python-oauth2.

Set up a Profile model

You'll need a place to store all of your Twitter OAuth credentials after the user has logged in. In your app's file you should add something that resembles the following model.

class Profile(models.Model):
    user = models.ForeignKey(User)
    oauth_token = models.CharField(max_length=200)
    oauth_secret = models.CharField(max_length=200)

Set up your Django views

Your should look something like the following. Basically, you need to have a login URL, a callback URL that Twitter will redirect your users back to, and a logout URL.

In this example ^login/ and twitter_login will send the user to Twitter to be logged in, ^login/authenticated/ and twitter_authenticated will confirm the login, create the account if necessary, and log the user into the application, and ^logout/ logs the user out in the twitter_logout view.

from django.conf.urls.defaults import *
from django.contrib import admin
from mytwitterapp.views import twitter_login, twitter_logout, \


urlpatterns = patterns('',
    url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
    url(r'^admin/', include(,
    url(r'^login/?$', twitter_login),
    url(r'^logout/?$', twitter_logout),
    url(r'^login/authenticated/?$', twitter_authenticated),

NOTE: The following code was coded for Python 2.4 so some of the libraries and code here might need to be updated if you are using Python 2.6+.

# Python
import oauth2 as oauth
import cgi

# Django
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.conf import settings
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required

# Project
from mytwitterapp.models import Profile

# It's probably a good idea to put your consumer's OAuth token and
# OAuth secret into your project's settings. 
consumer = oauth.Consumer(settings.TWITTER_TOKEN, settings.TWITTER_SECRET)
client = oauth.Client(consumer)

request_token_url = ''
access_token_url = ''

# This is the slightly different URL used to authenticate/authorize.
authenticate_url = ''

def twitter_login(request):
    # Step 1. Get a request token from Twitter.
    resp, content = client.request(request_token_url, "GET")
    if resp['status'] != '200':
        raise Exception("Invalid response from Twitter.")

    # Step 2. Store the request token in a session for later use.
    request.session['request_token'] = dict(cgi.parse_qsl(content))

    # Step 3. Redirect the user to the authentication URL.
    url = "%s?oauth_token=%s" % (authenticate_url,

    return HttpResponseRedirect(url)

def twitter_logout(request):
    # Log a user out using Django's logout function and redirect them
    # back to the homepage.
    return HttpResponseRedirect('/')

def twitter_authenticated(request):
    # Step 1. Use the request token in the session to build a new client.
    token = oauth.Token(request.session['request_token']['oauth_token'],
    client = oauth.Client(consumer, token)

    # Step 2. Request the authorized access token from Twitter.
    resp, content = client.request(access_token_url, "GET")
    if resp['status'] != '200':
        print content
        raise Exception("Invalid response from Twitter.")

    This is what you'll get back from Twitter. Note that it includes the
    user's user_id and screen_name.
        'oauth_token_secret': 'IcJXPiJh8be3BjDWW50uCY31chyhsMHEhqJVsphC3M',
        'user_id': '120889797', 
        'oauth_token': '120889797-H5zNnM3qE0iFoTTpNEHIz3noL9FKzXiOxwtnyVOD',
        'screen_name': 'heyismysiteup'
    access_token = dict(cgi.parse_qsl(content))

    # Step 3. Lookup the user or create them if they don't exist.
        user = User.objects.get(username=access_token['screen_name'])
    except User.DoesNotExist:
        # When creating the user I just use their
        # for their email and the oauth_token_secret for their password.
        # These two things will likely never be used. Alternatively, you 
        # can prompt them for their email here. Either way, the password 
        # should never be used.
        user = User.objects.create_user(access_token['screen_name'],
            '' % access_token['screen_name'],

        # Save our permanent token and secret for later.
        profile = Profile()
        profile.user = user
        profile.oauth_token = access_token['oauth_token']
        profile.oauth_secret = access_token['oauth_token_secret']

    # Authenticate the user and log them in using Django's pre-built 
    # functions for these things.
    user = authenticate(username=access_token['screen_name'],
    login(request, user)

    return HttpResponseRedirect('/')

  • You'll likely want to set LOGIN_URL to /login/ so that users are properly redirected to your Twitter login handler when you use @login_required in other parts of your Django app.
  • You can also set AUTH_PROFILE_MODULE = 'mytwitterapp.Profile' so that you can easily access the Twitter OAuth token/secret for that user using the User.get_profile() method in Django.


Gmail supports OAuth over IMAP and SMTP via a standard they call XOAUTH. This allows you to authenticate against Gmail's IMAP and SMTP servers using an OAuth token and secret. It also has the added benefit of allowing you to use vanilla SMTP and IMAP libraries. The python-oauth2 package provides both IMAP and SMTP libraries that implement XOAUTH and wrap imaplib.IMAP4_SSL and smtplib.SMTP. This allows you to connect to Gmail with OAuth credentials using standard Python libraries.


import oauth2 as oauth
import oauth2.clients.imap as imaplib

# Set up your Consumer and Token as per usual. Just like any other
# three-legged OAuth request.
consumer = oauth.Consumer('your_consumer_key', 'your_consumer_secret')
token = oauth.Token('your_users_3_legged_token', 

# Setup the URL according to Google's XOAUTH implementation. Be sure
# to replace the email here with the appropriate email address that
# you wish to access.
url = ""

conn = imaplib.IMAP4_SSL('')
conn.debug = 4 

# This is the only thing in the API for impaplib.IMAP4_SSL that has 
# changed. You now authenticate with the URL, consumer, and token.
conn.authenticate(url, consumer, token)

# Once authenticated everything from the impalib.IMAP4_SSL class will 
# work as per usual without any modification to your code.'INBOX')
print conn.list()


import oauth2 as oauth
import oauth2.clients.smtp as smtplib

# Set up your Consumer and Token as per usual. Just like any other
# three-legged OAuth request.
consumer = oauth.Consumer('your_consumer_key', 'your_consumer_secret')
token = oauth.Token('your_users_3_legged_token', 

# Setup the URL according to Google's XOAUTH implementation. Be sure
# to replace the email here with the appropriate email address that
# you wish to access.
url = ""

conn = smtplib.SMTP('', 587)

# Again the only thing modified from smtplib.SMTP is the authenticate
# method, which works identically to the imaplib.IMAP4_SSL method.
conn.authenticate(url, consumer, token)