oauth

2011年02月17日

google app engine上でEvernote連携のwebサービスを作るには、OAuthでの認証が必要になる。
このエントリーでは、OAuthをGoogle AppEngine上でpythonを使って認証するサンプルプログラムcunsumer_oauth.pyを提供する。
OAuthの直接的な部分は、google codeで提供されているoauth.pyを使い、それの呼び出し方を記述しているのが、consumer_oauth.pyである。
このプログラムは、oauth.pyのライブラリの使い方の参考とすることを目的としているので、元のoauth.pyライブラリに則り、MITライセンスとする。

oauth.pyを使ったサンプルプログラムは、webで調べるとtwitter向きのものが多い。
しかし、Evernoteをproviderとしたとき、同じOAuthでも、twitterとevernoteではパラメータの扱いが若干違うため、そのままは使えない。 そのあたりを修正して、ここにあげておく。

1. OAuthの仕組み

まずは、OAuthの基本的な動きを理解する。

2.ライブラリの入手

google codeでは、pythonのOAuthライブラリが ここ で公開されている。

oauth.pyを入手

ここでは、exampleとしてoauth.pyを使った client.py というのもあるのだが、これがどうもわかりづらい。
いろいろ探っているうちに、わかりやすいexampleをみつけた。

GAE+OAuth

このexampleは、いろいろミスがあり、動かすためにはかなりの修正が必要となるが、oauth.pyの使い方はとても参考になった。おかげで、oauth.pyを使えたといっても過言ではない。

3.EvernoteからAPI keyの取得

実際に、以下のプログラムを動かすには、Evernoteからconsumer用API keyを入手しなくてはならない。

以下のフォームからEvernoteにAPI keyを申請する。

https://www.evernote.com/about/developer/api/

application typeは"web application"とすること。
1,2日でメールで届くはずである。

4.Evernote OAuthの特徴

Twitterなど、あちこちで公開されているOAuthの使用例と比べて、Evernoteでは、以下の2点を注意する必要がある。

  1. twitter OAuthなどでは、request tokenからaccess tokenに交換するために、承諾されたoauth_okenを送るが、evernoteの場合は、同時にverifierも送らないといけない。
    verifierは、エンドユーザが承諾ボタンを押した後のcallback urlで戻ってくるhttp getリクエストの中に、パラーメータとして格納されている。

  2. 承諾画面より後はevernoteではoauth_secretを使わないので、url encodeされたパラメータ部分で&oauth_secret=&、と値部分がnullになるリクエストが一度だけくる。

1.については、verifierパラメータを抜き出して、リクエストURLを生成時する関数にパラメータを渡すことで対処できる。oauth.pyのライブラリは、verifierをリクエストに加える機能を持っている。

2.については、secretパラメータがnullになるとoauth.pyの中でエラーとなる。ouath.pyの以下の行でエラーを補足し、secretにダミーの文字列をセットすることで対処する。

5.oauth.py変更箇所

137行目から

(変更前)
    def from_string(s):
        """ Returns a token from something like:
        oauth_token_secret=xxx&oauth_token=xxx
        """
        params = cgi.parse_qs(s, keep_blank_values=False)
        key = params['oauth_token'][0]
        secret = params['oauth_token_secret'][0]
        token = OAuthToken(key, secret)
        try:
            token.callback_confirmed = params['oauth_callback_confirmed'][0]
        except KeyError:
            pass # 1.0, no callback confirmed.
        return token

(変更後)
    def from_string(s):
        """ Returns a token from something like:
        oauth_token_secret=xxx&oauth_token=xxx
        """
        params = cgi.parse_qs(s, keep_blank_values=False)
        key = params['oauth_token'][0]
        try:
            secret = params['oauth_token_secret'][0]
        except KeyError:
            secret = "dummy"
        token = OAuthToken(key, secret)
        try:
            token.callback_confirmed = params['oauth_callback_confirmed'][0]
        except KeyError:
            pass # 1.0, no callback confirmed.
        return token

6.Access Tokenの保存

consumer webサービスを使うたびに、毎回evernoteで承諾をするのが面倒なので、承諾済みであることをどこかに記録しておきたい。

クッキーを使ってブラウザに保存する方法と、consumerサーバ上に保存する方法がある。
今回作成したconsumer_oauth.pyでは、google app engineのデータストアに保存することにする。 そのため、consumerサービスを使うときは、googleアカウントでのログインを必須とし、GAE上のデータストアにgookeアカウントにひもづくEvernote access tokenを保存することにした。
本当のwebサービスでは、プライバシー保護としてaccess tokenを保存しないオプションを用意する予定である。

7.consumer_oauth.pyの実行

変更済みのoauth.pyとapp.yamlもセットにして、consumer_oauth.pyをgithubで公開したので、以下からダウンロードする。

consumer_oauth.pyのソース

GAE開発キットが動作する環境であれば、githubからダウンロードした3つのファイルを同一ディレクトリにおいて、開発サーバを起動すれば、ブラウザからアクセスできる。

注意点は、以下の2点である。

  1. evernote開発環境のアカウント
  2. Evernoteでの承諾は、evernoteログインが必要である。(当然だが) このプログラムでは、URLをEvernoteの開発サーバ(sandbox.evernote.com)に指定しているので、本番サーバ(www.evernote.com)のアカウントとは別に、開発サーバ用にユーザ登録をしないとEvernoteにログインはできない。

  3. Callback URL
  4. callback URLは、GAE開発環境(localhost:8080)をデフォルトとしている。GAE上にアップロードする前には、.appspot.com名のホスト名に修正してからアップロードする必要がある。

 

正常に動作すれば、以下のような画面がブラウザでみれるはず。

1.googleにもEvernoteで承認されていない状態

1-2

2.Googleにログインし、Evernoteで承認されていない状態

2-2

3.Evernoteで承認する画面

4-2

4.Evernoteに承認された状態

3-2

これ以降、このoauth_keyをEdamに格納し、Edam_userIdのディレクトリに向かってAPI を呼び出す部分を作ることで、consumerからノートの操作が可能となる。

Endpoint URL: https://sandbox.evernote.com/edam/note/<shardId>



sylphide_ffr31mr at 23:09コメント(0)トラックバック(0) 

2010年08月18日

GAE上でsinatraとoauthとtwitter

GAE上のjrubyプログラムでクライアントからの値を取れるようになったので、次はいよいよ他のサービスとの通信だ。 しかし、他のサービスとの通信をするには、認証という大きな壁がある。
一番簡単そうなtwitterで遊んでみることにする。
twitterは、今月(2010-08)にbasic認証を終了するとアナウンスしているので、oauthを使う。 oauthのお勉強はこちらなど。

まずは、環境の確認の意味で、こちらのプログラムを動かしてみる。

しかし、なかなか素直に動かない。
試行錯誤すること3日。
あちこちを参考にするが、つまづきまくる。

結果としては、原因が2つあったようだ。

一つは、appengine gemでjrubyへライブラリを組み込むとき、appcfg.rb bundleをするが、gemの依存関係の処理がうまくいっていないようだ。 依存gemをGemfilesに書いてあげることで、gemのloadはクリアできた。

二つ目の原因は、twitter gemとoauth gemの2つをrequireしていたのだが、twitter gemにoauthのクラスが組み込まれており、oauth gemと競合していた。require twitterのみにして解決した。

あと、GAEの特徴として、Net::HTTPが素のままでは使えないという問題もあるが、これもgoogle-appengineが解決してくれているので、今や意識しなくてよいようだ。

動作環境は以下。

$ ruby -v
ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux]
$ gem list

*** LOCAL GEMS ***

appengine-apis (0.0.18)
appengine-rack (0.0.11)
appengine-sdk (1.3.5)
appengine-tools (0.0.15)
bundler08 (0.8.5)
crack (0.1.6)
google-appengine (0.0.15)
hashie (0.2.2)
httparty (0.5.2)
jruby-jars (1.5.1)
jruby-rack (1.0.2, 1.0.1)
oauth (0.4.1)
rack (1.2.1)
ruby-hmac (0.4.0)
rubyzip (0.9.4)
twitter (0.9.8)
yajl-ruby (0.7.7)

Gemfilesとrequireリストは、以下になる。

$ more Gemfile 
# Critical default settings:
disable_system_gems
disable_rubygems
bundle_path ".gems/bundler_gems"

# List gems to bundle here:
gem 'appengine-rack', '0.0.11.pre'
gem 'appengine-apis'
gem 'sinatra'
gem 'httparty'
gem 'yajl-ruby'
gem 'jruby-openssl'
gem 'twitter'

$ cat sample.tw.rb
require 'rubygems'
require 'openssl'
require 'twitter'
(以下略)
まちゅダイアリー Sinatra と OAuth を使って Twitter のタイムラインを取得してみたのソースが続く


sylphide_ffr31mr at 20:10コメント(0)トラックバック(0) 
記事検索
最新コメント
livedoor プロフィール
月別アーカイブ
  • ライブドアブログ