pythonでoauth認証

pythonTwitteroauth認証部分を作成したのだが,少し手こずったのでメモっておく.

ちなみに,botを動かす場合のように,自分だけが認証をパスすればよい場合は簡単.

参考:TwitterBotの作成① - Hello World!!

今回は,Twitterクライアントなどのアプリケーションのように,それを使うユーザがそれぞれに認証を必要とする場合を想定している.

まず,認証の流れを示す.

認証画面

認証

コールバック

Twitterのアプリを使用する時にこんなの1度は見たことあるはず.

http://www.dl.kuis.kyoto-u.ac.jp/~takemura/Twitter/oauth.cgi で実際に試せるよ.

具体的なoauth認証の流れを示す.

1. リクエストトークンの取得

まず,アプリを使いたいユーザが,そのアプリのConsumer keyConsumer secretを用いて,リクエストを送る.

すると,Request token URL から,そのアプリをリクエストするための,Request tokenRequest token secretが発行される.

2. アプリの認証

次に,このRequest tokenを用いて,Authorize URL でアプリの認証を行う.

ちなみに,このURLは,アプリを使う度に認証を必要とする.

実際のアプリでは,アプリを初めて使う時のみ認証して,2回目以降は認証を必要としない場合も多い.

このような実装を望む場合は,Authenticate URL で認証を行えばよい.

このURLでは,1回目のみ Authorize URL と同じ挙動をし,2回目以降は認証されているものとして自動でパスする.

3. コールバック

認証されると,認証後のトップページへコールバックされる.

ちなみに認証形式には2種類あり,PINという8文字の数字をユーザに手動で入力してもらわないといけないものと,指定されたページヘ自動で遷移(コールバック)するものとがある.

今回は後者を実装している.Webアプリでは後者が一般的だと思う.

前者でよいなら OAuth を使用して Twitter へ投稿する。 | Lonely Mobiler を参考にするとよい.

コールバック先のページは,アプリのページ(Twitter Developers)で指定できる.

何も指定しなければPIN入力形式となる.

コールバック時に,oauth_verifierという,認証をパスしたことを示す証明書が発行される.

4. アクセストークンの取得

oauth_verifierと,Request token,Request token secretを用いて,アクセストークンをリクエストする.

すると,Access token URL から,Access tokenAccess token secretが発行される.


これでやっと終了.かなりめんどくさい…

このアクセストークンを用いて,ユーザのTwitterアカウントにアクセス可能になる.

以前までは,もっと単純に,ユーザのアカウントとパスワードを取得して認証するというBasic認証というものがあったのだが,セキュリティ上の問題のために,現在は廃止されている.

ちなみに,oauth認証で個人的にめんどくさかった点は,認証前に取得したRequest tokenとRequest token secretが,認証後にも必要となる点.

すなわち,Webページの遷移のため,セッションの管理が必要になる.

以下,プログラムを載せておく.

もし使いたい人がいれば,pika-shi/oauth · GitHub からダウンロードすればいいよ.

認証前

#!/usr/local/bin/python
# -*- coding: utf-8 -*-

import urllib
import oauth2 as oauth
import sqlite3

request_token_url = 'http://twitter.com/oauth/request_token'
access_token_url = 'http://twitter.com/oauth/access_token'
authenticate_url = 'http://twitter.com/oauth/authenticate'

consumer_key = '**********'
consumer_secret = '**********'

def getOAuth():
    consumer = oauth.Consumer(key=consumer_key, secret=consumer_secret)
    client = oauth.Client(consumer)
    # reqest_token を取得
    resp, content = client.request(request_token_url, 'GET')
    request_token = dict(parse_qsl(content))

    # 認証ページに遷移
    url = '%s?oauth_token=%s' % (authenticate_url, request_token['oauth_token'])
    print '<meta http-equiv="refresh"content="1; url=%s">' % url

    # request_token と request_token_secret を保存
    con = sqlite3.connect('oauth.db')
    con.execute(u'insert into oauth values (?, ?)', (request_token['oauth_token'], request_token['oauth_token_secret']))
    con.commit()
    con.close()

def parse_qsl(url):
    param = {}
    for i in url.split('&'):
        _p = i.split('=')
        param.update({_p[0]: _p[1]})
    return param

if __name__ == '__main__':
    print 'Content-type: text/html; charset: utf-8'
    print
    getOAuth()

認証後

#!/usr/local/bin/python
# -*- coding: utf-8 -*-

import os
import cgi
import cgitb
import oauth2 as oauth
import sqlite3
import twitter
cgitb.enable()

request_token_url = 'http://twitter.com/oauth/request_token'
access_token_url = 'http://twitter.com/oauth/access_token'

consumer_key = '**********'
consumer_secret = '**********'

def callback():
    # oauth_token と oauth_verifier を取得
    if 'QUERY_STRING' in os.environ:
        query = cgi.parse_qs(os.environ['QUERY_STRING'])
    else:
        query = {}

    # oauth_token_secret を取得
    con = sqlite3.connect('oauth.db')
    oauth_token_secret = con.execute(
        u'select oauth_token_secret from oauth where oauth_token = ?'
        , [query['oauth_token'][0]]).fetchone()[0]
    con.close()

    # Access_token と access_token_secret を取得
    consumer = oauth.Consumer(key=consumer_key, secret=consumer_secret)
    token = oauth.Token(query['oauth_token'][0], query['oauth_verifier'][0])
    client = oauth.Client(consumer, token)
    resp, content = client.request(access_token_url, "POST", body="oauth_verifier=%s" % query['oauth_verifier'][0])
    access_token = dict(parse_qsl(content))

    return access_token['oauth_token'], access_token['oauth_token_secret']

def client(access_token, access_token_secret):
    api = twitter.Api(consumer_key=consumer_key,
                      consumer_secret=consumer_secret,
                      access_token_key=access_token,
                      access_token_secret=access_token_secret,
                      cache=None)
    TL = api.GetFriendsTimeline()
    for tweet in TL:
        print tweet.text.encode('utf-8')
        print '<br>'

def parse_qsl(url):
    param = {}
    for i in url.split('&'):
        _p = i.split('=')
        param.update({_p[0]: _p[1]})
    return param

if __name__ == '__main__':
    print 'Content-type: text/html; charset: utf-8'
    print
    access_token, access_token_secret = callback()
    client(access_token, access_token_secret)

参考ページ
tweepy と flask でwebアプリケーション認証 - 銀の人のメモ帳
OAuth を使用して Twitter へ投稿する。 | Lonely Mobiler