[公式]Evernote API徹底活用レシピ

第12回Google App Engine上でOAuth認証を行う

今回はGoogle App Engine上でOAuth認証をして、ノートブックの一覧を表示してみます。まずedamtest.zipをダウンロードして解凍した後、どこか適当なところに置いてください。

GoogleAppEngineLauncherのメニューからFile=>"Add Existing Application..."を選択して、解凍後のディレクトリを指定します。また、ポート番号を8081としてください。その後、main.pyの36-37行目 OAUTH_CONSUMER_KEYOAUTH_CONSUMER_SECRETを、取得したWebアプリケーション用キーで置き換えてください。これでGoogleAppEngineLauncherを通してデモを実行できます。ブラウザでhttp://localhost:8081を開くと、以下のような単純なトップページが表示されます。

EDAM OAuthデモ
EDAM OAuthデモ

ポート番号を8081以外にする場合は、main.pyの47行目APP_BASE_URIの番号を変更すれば良いです。また、本番環境で動かす場合は、この部分をGoogle App Engineのホスト名に変更する必要があります。

Google App Engineでは、予め必要なライブラリを全てアプリケーションディレクトリ内に置いておく必要があります。今回使用している外部ライブラリは以下の通りです。

evernoteとthriftはeverenote-api-1.19/python/lib/にあるものです。python-oauth2はOAuth認証用のライブラリ、httplib2はpython-oauth2が依存しているライブラリ、gaeutiltiesはセッション用です。

今回は説明を簡単にするためにセッションによってOAuthの情報を管理するようにしています。Googleアカウントなどに紐付けて管理というのでも良いでしょう。

では、OAuthの処理を見ていきましょう。デモ画面で"トークン取得"をクリックすると、以下のOAuthTokenHandlerクラスに処理が渡されます。

class OAuthTokenHandler(webapp.RequestHandler):
    """OAuthの認証手続き"""
    def get(self):
        ## Webアプリケーション用キーの設定
        consumer = oauth.Consumer(key=OAUTH_CONSUMER_KEY, secret=OAUTH_CONSUMER_SECRET)
        ## トークン取得後にOAuthサーバから転送してもらうURIの設定
        params = { "oauth_callback": TOKEN_CALLBACK_URI }
        ## 最初の要求作成: この段階ではトークンはなし
        request = oauth.Request.from_consumer_and_token(
            consumer, token=None, http_method="GET", http_url=TOKEN_URI, parameters=params)
        ## 署名作成
        request.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None)
        ## 最初の要求
        uri = request.to_url()
        result = urlfetch.fetch(uri)
        if result.status_code != 200:
            self.redirect("/error")
            return

        ## レスポンスから仮証明(リクエストトークン)を抽出
        token = oauth.Token.from_string(result.content)
        ## OAuthサーバのユーザの認証・同意画面に飛ばす
        request = oauth.Request.from_token_and_callback(
            token=token, callback=TOKEN_CALLBACK_URI, http_url=AUTHORIZATION_URI)
        uri = request.to_url()
        self.redirect(uri)

まず、Webアプリケーション用のキーとシークレットをOAuthサーバの仮証明要求URI(以前のリクエストトークンURI)に送ります。この際にユーザ認可後にOAuthサーバからリダイレクトしたもらうURIをoauth_callbackで指定しておきます。また、OAuthのリクエストでは署名を作成して付加する必要があります。

成功するとOAuthサーバから仮証明(以前のリクエストトークンに相当)の情報が返ってきます。次に、この仮証明を使用してユーザの認証と承認をOAuthサーバに依頼するために、OAuthサーバの認証用URIにリダイレクトします。成功すると、以下のようにEvernoteの認証画面が表示されます。

OAuthログイン画面
OAuthログイン画面

ここでユーザ名・パスワードを入力すると、権限の承認画面に遷移します。EvernoteのOAuthではアプリケーションに"閲覧、作成、更新"の権限を付与することになります。

OAuth承認画面
OAuth承認画面

ユーザが承認した場合は、仮証明取得時にoauth_callbackに指定したURIに転送されてきます。そのURIの処理はOAuthTokenCallbakHandlerクラスで行っています。

class OAuthTokenCallbackHandler(webapp.RequestHandler):
    """OAuthから戻ってきた後の処理"""
    def get(self):
        session = sessions.Session()
	## ユーザが承認した場合はoauth_tokenとoauth_verifierが返ってくる
        oauth_token = self.request.get("oauth_token")
        oauth_verifier = self.request.get("oauth_verifier")
        ## シークレットは使用しないので空で良い
        token = oauth.Token(oauth_token, "")
        ## oauth_verifierは必須
        token.set_verifier(oauth_verifier)

        ## トークン証明(アクセストークン)の取得
        consumer = oauth.Consumer(key=OAUTH_CONSUMER_KEY, secret=OAUTH_CONSUMER_SECRET)
        client = oauth.Client(consumer, token)
        ## oauth.SignatureMethod_HMAC_SHA1()では動かない (要確認?)
        client.set_signature_method(oauth.SignatureMethod_PLAINTEXT())
        response, content = client.request(TOKEN_URI)
        if response.status != 200:
            self.redirect("/error")
            return

        ## トークン証明(アクセストークン)などの抽出
        ## NoteStoreにアクセスするのに必要なのでセッションとして保持
        params = parse_qs(content)
        session["oauth_token"] = params["oauth_token"][0]
        session["edam_shard"] = params["edam_shard"][0]
        session["edam_userId"] = params["edam_userId"][0]
        self.redirect("/")

ユーザ承認済の仮証明 oauth_token とチェックコード oauth_verifier が転送時に渡されてくるので、次にこれらを使用してトークン証明(以前のアクセストークンに相当)を取得します。成功するとトークン証明 oauth_tokenと、NoteStoreのURIを決定するための edam_shard、認証したユーザのIDである edam_userId が返ってきますので、それぞれセッションに保存しておきます。

トークン証明の取得が終わったら一度トップページに飛ぶようにしています。トークン証明が正常に保存されている場合は、トップページに"ノートブック一覧"というリンクが増えているはずです。

承認後のトップページ
承認後のトップページ

ノートブック一覧の処理は以下のListHandlerクラスで行っています。

class ListHandler(webapp.RequestHandler):
    """ノートブックの一覧を表示"""
    def get(self):
        session = sessions.Session()
        ## 必要なデータがセッション内にあることを確認
        for key in ["oauth_token", "edam_shard", "edam_userId"]:
            if key not in session or session[key] == "":
                sys.stderr.write('OAuth認証を先に行う必要があります。\n')
                self.redirect("/error")
                return

        ## NoteStoreからnotebooksを取得
        notestore_uri =  NOTESTORE_BASE_URI + session["edam_shard"]
        notestore_http_client = THttpClient.THttpClient(notestore_uri)
        notestore_protocol = TBinaryProtocol.TBinaryProtocol(notestore_http_client)
        notestore = NoteStore.Client(notestore_protocol)
        notebooks = notestore.listNotebooks(session["oauth_token"])
        self.response.out.write('<html><head><title>ノートブック一覧</title></head>')
        self.response.out.write('<body><h1>ノートブック一覧</h1>')
        self.response.out.write('<p>%d個のノートブック</p><ul>' % len(notebooks))
        for notebook in notebooks:
            self.response.out.write('<li>%s</li>' % notebook.name)
        self.response.out.write('</ul><p><a href="/">トップページに戻る</a></p></body></html>')

Evernoteにおいては、OAuthのトークン証明はUserStoreの認証トークンと同等です。そのため、NoteStoreを使用した処理はこれまでとまったく同じです。成功すると以下のように認証したユーザのノートブック一覧が表示されます。

ノートブック一覧
ノートブック一覧

今回はGoogle App Engine上においてOAuth認証経由でのEvernote APIの使い方を説明しました。次回も引き続きGoogle App Engineでの開発を解説します。

おすすめ記事

記事・ニュース一覧