2011年4月19日火曜日

外部認証を使ったアプリケーション④ Google OpenID API サンプル(B 中編)

前編 に引き続いて、Google OpenID API を使ったサンプルについて書いていきます。

「OpenID認証(ID入力)」モジュール

前回記事でも触れたが、OpenID認証(ID入力) はID入力を必要とするプロバイダ向けのモジュールである。
入力したIDを create_login_url関数にパラメーターとして渡し、URLを生成しリダイレクトする動作となる。

まずID入力画面だが、OpenID Foundation の画面を真似てみた(下イメージ)。

参考: OpenID Foundation - How do I log in with OpenID?

今回のモジュールの画面(下イメージ)。



それでは OpenID認証(ID入力) モジュールのテンプレートを次に示してみる。

<!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>openid login</title>
      <link rel="stylesheet" type="text/css" href="/css/openid.css">
</head>
<body>
<form method="post" action="{% url InputOpenidHandler %}">    
<table class="openid" hspace=30px vspace=30px height=100px width=500px >
    <tr align="left"><th width=10px></th><th>{{ title }}</th><th width=10px></th></tr>
    <tr align="left"><td></td><td valign="bottom">{{ text }}</td><td></td></tr>
    <tr><td></td>
    <td  valign="top">
        <input type="hidden" name="cont" value="{{ cont|escape }}">
        <input type="hidden" name="provider" value="{{ provider|escape }}">
        <input type="text" size="30" id="openid_url" name="openid_url" class="openid" value="{{ def_url }}"/>
        <input type="submit" id="openid_url_submit" name="openid_url_submit" class="openid" value="Sign in"/>
    </td><td></td>
    </tr>
    <tr><td></td><td class="error">{{ error }}</td><td></td></tr>
</table>
</form> 
</body>
</html>
ソース3 解説
  • OpenID は URL だが、そのまま項目だけ用意しても入力へのハードルが高い。このためプロバイダー毎のガイダンスを用意した。
    ガイダンスは、入力項目上のメッセージ(11・12行目) 及び 入力項目中の URL文字列(16・17行目)だ。
  • Sign in ボタンを押すと Post関数が呼び出される(9行目)。
以上、シンプルである。

テンプレート用CSSソースも示す。

.openid {
  font-family: 'Helvetica' sans-serif;
  font-size:100%;
  behavior: url(/css/PIE.htc);
}
.error {
  padding: 0px 0px 0px 25px;
  color: red;
  font-weight: bolder;
}
table.openid {
  background-color: #ccccff;
  border-radius: 5px;
  behavior: url(/css/PIE.htc);
}
input#openid_url {
  padding: 2px 5px 2px 25px;
  border-style: solid;
  border-width: 1px;
  background-image: url(/images/openid-icon.gif);
  background-repeat: no-repeat;
  background-position: left center;
  background-size:contain;
  behavior: url(/css/PIE.htc);
}
input#openid_url_submit {
  padding: 2px 8px;
}

次に、OpenID認証(ID入力) のプログラムである。

# -*- coding: utf-8 -*-
import os
import urllib2
from google.appengine.api import users
from google.appengine.ext.webapp import template
from base_handler import BaseHandler
 
class InputOpenidHandler(BaseHandler):
    def set_message_parameter(self, provider, **kwargs):
        prov_msg = {u'hatena':{'def_url':u'http://www.hatena.ne.jp/','title':u'はてなでログイン','text':u'はてな OpenID は、 <u>http://www.hatena.ne.jp/はてなID/</u> です。'},
                   u'defalut':{'def_url':u'http://','title':u'OpenID ログイン','text':u'あなたの OpenID を入力してください'}}
 
        if provider not in prov_msg:
            provider = u'defalut'
        for k, v in prov_msg[provider].iteritems():
            kwargs.update({k:v})
        return kwargs
 
    def get(self):
        provider = self.request.get('provider')
        template_values = self.set_message_parameter(provider)
        template_values = self.make_parameter_dict('cont', 'error', 'provider', **template_values)
        path = os.path.join(os.path.dirname(__file__), 'templates', 'input_openid.html')
        self.response.out.write(template.render(path, template_values))
 
    def post(self):
        cont = self.request.get('cont')
        openid_url =  self.request.get('openid_url')
        try:
            if not openid_url:
                raise urllib2.URLError
            login_url = users.create_login_url(dest_url = cont, federated_identity = openid_url)
            urllib2.urlopen(urllib2.Request(login_url))
            self.redirect(login_url)
        except:
            url = self.make_url(**self.make_parameter_dict('cont','provider', error = 'OpenID url error'))
            self.redirect(url)
ソース5 解説
  • select_login.html から provider 変数を受け取る(20行目)。
  • Get関数で画面を呼び出すが、その時 set_message_parameter 関数でプロバイダー毎のメッセージをセットする(21行目)。
  • Post関数内で、create_login_url を使って URL を生成する(32行目)。
  • 生成した URL を urlib2.urlopen を使って妥当性をチェックする(33行目)。
  • URL へリダイレクトする(34行目)。
以上が OpenID認証(ID入力) モジュールだ。

前回の記事でも触れた、Get/Postでの変数受け渡し用の関数を定義している BaseHandler クラスについても触れておく。

「BaseHandler」モジュール

このモジュールは認証とは関係無い。セッションを使っていないため、関数(画面)間で変数を受け渡すために使用している。

# -*- coding: utf-8 -*-
from google.appengine.ext import webapp

class BaseHandler(webapp.RequestHandler):
    def make_parameter_dict(self, *args, **kwargs):
        ''' make parameter for post method '''
        for arg in args:
            kwargs.update({arg: self.request.get(arg)})
        return kwargs
    
    def make_url(self, **kwargs):
        ''' make url string ( parameter for get method ) '''
        param = ''
        for k, v in kwargs.iteritems():
            if v is not None:
                param += ('?%s=%s' % (k,v)) if param == '' else ('&%s=%s' % (k,v))
        return self.request.url + param
ソース6 解説
  • BaseHandler クラスは webapp.RequestHandler クラスを継承している(4行目)。
  • make_parameter_dict は、変数名と値を渡すと辞書形で変数を返す(5行目)。
  • make_url は、現在の URL を返す(11行目)。辞書型変数を渡した場合、Get関数用の変数付き URL を返す。

前回の記事と今回で、認証関係のモジュールについて触れた。次回の記事では、アプリケーションへの組み込みについて書いていきたい。