Google App Engine (GAE)上で提供されるようになった OpenID API について、前回の記事 で解説した。
今回は少し実用的なサンプルを作りながら、気になった点など書いていきたい。
OpenID を利用した“ちょこっと”実用的なサンプルの概要
OpenID を使って認証したユーザが、自分の自己紹介文を登録するアプリケーションを考えてみる。
とりあえず、OpenID認証用と自己紹介文登録用の機能を用意すればよいのがわかる。(下図)
前回の記事の最後にも書いたが、OpenID プロバイダによって認証の仕方には違いがある。OpenID を入力する方式を採用しているプロバイダに対しては、ID入力用の機能を用意する必要がある。このため、ID入力が付いた認証機能を付け足す(下図)。
もちろん OpenID入力方式を採用しているプロバイダを認証から外すのであれば、この機能は必要無い。
これらの各機能(モジュール)について、コードを作成しながら説明していきたい
「OpenID認証」モジュール
OpenID認証モジュールは、認証が必要になった時点でコールされる。前回の記事でも説明したが、GAEでは app.yaml のURLパターン・スクリプトハンドラの下に login: required と記述すれば認証プログラムをコールする。この時コールされる認証プログラムが 今回のOpenID認証モジュールになる。
OpenID認証は、create_login_url関数が吐き出すURLにアクセスすることにより行う。Googleのサンプルプログラムではcreate_login_url関数が出力したURLをHTMLページに出力することに認証を行っていた。この方法は簡単で良いのだが、HTMLページのデザインがどうしても定形のものになってしまう。今回はデザインの変更が容易になるように、HTMLページからのパラメーターでcreate_login_url関数を呼び出し、関数が出力したURLでリダイレクトを行う方法でコーディングを行う(下図)。
認証画面のデザインはユーザに判りやすいように、アイコンを組み合わせたものにする(下イメージ)。
それでは上記の仕様に従って作ったソースを示していきたい。まず OpenID認証モジュールのテンプレートである。
-
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Select login</title> </head> <body> <table cellpadding="5"> <tr><td><form method="post" action="{% url SelectLoginHandler %}"> <input type="hidden" name="cont" value="{{ cont|escape }}"> <input type="hidden" name="url" value="google.com/accounts/o8/id"> <input type="image" name="button" src="/images/google.png" width="36" hegiht="36" alt="Google"> Googleでログイン </form></td></tr> <tr><td><form method="post" action="{% url SelectLoginHandler %}"> <input type="hidden" name="cont" value="{{ cont|escape }}"> <input type="hidden" name="url" value="aol.com"> <input type="image" name="button" src="/images/aol.png" width="36" hegiht="36" alt="AOL"> AOLでログイン </form></td></tr> <tr><td><form method="post" action="{% url SelectLoginHandler %}"> <input type="hidden" name="cont" value="{{ cont|escape }}"> <input type="hidden" name="url" value="myspace.com"> <input type="image" name="button" src="/images/myspace.png" width="36" hegiht="36" alt="MySpace"> MySpaceでログイン </form></td></tr> <tr><td><form method="post" action="{% url SelectLoginHandler %}"> <input type="hidden" name="cont" value="{{ cont|escape }}"> <input type="hidden" name="url" value="yahoo.co.jp"> <input type="image" name="button" src="http://i.yimg.jp/images/login/btn/btnXSYid.gif" width="241" hegiht="28" alt="yahoo!Japan"> </form></td></tr> <tr><td><form method="post" action="{% url SelectLoginHandler %}"> <input type="hidden" name="cont" value="{{ cont|escape }}"> <input type="hidden" name="url" value="mixi.jp"> <input type="image" name="button" src="http://developer.mixi.co.jp/wp-content/uploads/2010/01/login_btn002.gif" alt="Mixi"> </form></td></tr> <tr><td> <a href="{% url InputOpenidHandler %}?cont={{ cont|escape }}&provider=hatena" target="_self"> <img src="/images/oi_hatena.gif" alt="Hatena"></a> </td></tr> </form></td></tr> </table> </form> </body> </html>
- ソース1 解説
- GoogleからMixiまでのアイコン(ボタン)には、const 及び url という2つのパラメーターが設定されている(例 10・11行目)。これらはクリックするとPost関数に渡され、関数内で create_login_url のパラメータになる。
- 「はてな」は ID入力モジュール に渡す必要があるため、アンカーリンクでリンク先を ID入力モジュール に指定している(35・36行目)。
- テンプレート内で使用しているアイコンは、Yahoo!Japan mixi などは自社で提供している。他のプロバイダ分は静的ファイルとして Vector Social Media Icons をアップロードして利用した。「はてな」については akahoshitakuya.com から拝借した。
Vector Social Media Icons
Google App Engine - 静的ファイルの使用
Yahoo! JAPAN ID ログインボタン
mixi Platform用素材利用ガイドライン
akahoshitakuya.com - 読書メーターがOpenIDに対応しました!(mixi Yahoo はてな)
次に、OpenID認証モジュールのプログラムである。
-
# -*- coding: utf-8 -*- import os from google.appengine.api import users from google.appengine.ext import webapp from google.appengine.ext.webapp import template from google.appengine.ext.webapp.util import run_wsgi_app from base_handler import BaseHandler from input_openid import InputOpenidHandler class SelectLoginHandler(BaseHandler): def get(self): param = {'cont': self.request.get('continue')} # 'continue' is api parameter template_values = self.make_parameter_dict(**param) path = os.path.join(os.path.dirname(__file__), 'templates', 'select_login.html') self.response.out.write(template.render(path, template_values)) def post(self): cont = self.request.get('cont') url = self.request.get('url') login_url = users.create_login_url(dest_url = cont, federated_identity = url) self.redirect(login_url) def main(): url_map = [('/_ah/login_required', SelectLoginHandler), ('/login/input_openid', InputOpenidHandler)] application = webapp.WSGIApplication(url_map, debug=True) run_wsgi_app(application) if __name__ == '__main__': main()
- ソース2 解説
- Get関数でテンプレートファイルを書き出し、Post関数で選択したプロバイダのURLを create_login_url で生成しリダイレクトする(20・21行目)。
- Get関数内で continue 変数を取り出しているが、これは認証要求時に AppEngine がセットする元画面のURLが入った変数である(12行目)。continue という名前のままだと、Pythonの予約語に重なるため cont に名前を変更している。
- BaseHandlerというクラスを使用している。これは今回セッションを使用していないので、Get及びPost変数を受け渡す関数を定義しているクラスだ(7・10行目)。
- main関数でURLパターンを設定している。/_ah/login_required は AppEngineで定義している認証用のURLのため、これに認証用クラスを割り当てる必要がある(24行目)。他に ID入力モジュール のURLパターンもここで定義している(25行目)。
あまりにも長くなったので、後編(中編?)に続きます。