GITkitを使ってみる Part3
最後にOAuth2.0を使ったユーザ認証を実装していきます。結構ハマったので、自分の頭の中を整理する意味でもまとめていきます。
まずはいつものUIから見て行きます。
右側のペインはGITkitを使ってみる Part2でも書いた、そのサイト固有のアカウントでログインするためのインターフェイスです。そして左側のペインが今回実装していくOAuth2.0を使ってログインするためのインターフェイスです。まずはGMailのボタンを押してみます。
てな感じでおなじみのGMailログインの画面がポップアップで表示されます。メールアドレスとパスワードを入力してログインします。
という具合にこれまたおなじみのGoogleアカウントへのアクセス許可画面が表示されます。もちろん許可します。すると、ここでようやくアプリケーション側に制御が回ってきます。Account Chooserの初期設定で指定したcallbackUrlに対してGETもしくはPOST要求が投げられます。ここが結構難しかった。ハマってしまいました。
公式ドキュメントによると、まずはverifyAssertionというAPIを叩け、とあります。このAPI、リファレンスを確認するとWeb APIです。
identitytoolkit.relyingparty.verifyAssertion
POSTメソッドで、JSON形式で二つのパラメータを伴います。postBodyの方はcallbackUrlに渡されたHTTPリクエストのBodyをそのまま渡せばよさそうです。
def get(self): self.verify(self.request.body) def post(self): self.verify(self.request.body) def verify(self, body): pass
問題はrequestUriです。リファレンスによると
The URI to which the IDP authentication page redirects.
1日悩みました。でも意味を素直に解釈すると簡単なことでした。
def verify(self, body): data = { 'requestUri': self.request.url, 'postBody': body}
ということですね。callbackUriがコールバックされた時のURL全体のことを意味します。これでverifyAssertion APIをurlfetchサービスを使って呼び出します。
def verify(self, body): data = { 'requestUri': self.request.url, 'postBody': body} params = simplejson.dumps(data) response = urlfetch.fetch( url='https://www.googleapis.com/identitytoolkit/v1/relyingparty/verifyAssertion?key=Your API key', payload=params, method = urlfetch.POST, headers = {'Content-Type': 'application/json'}) if response.status_code != 200: logging.error('Body:%s' % response.content) self.redirect('failed') return
うまく行けばステータスコード200が返ってきます。200が返ってこなくても引数 (response.content) が付いてきて、エラーの理由なんかが記載されているので、200が返らない場合は引数を出力してみるといいかもです。
ここまで行けば後は簡単・・・と言いたいところですが、ハマってしまいました。こちらは2時間ぐらい。
公式ドキュメントによれば、この後
- verifiedEmailが登録済みならログインさせる
- verifiedEmailが未登録なら (追加情報があれば) アカウント登録画面に遷移させる
とあります。今回は簡単に試したいため、必要な情報はメールアドレスだけとしているので、追加情報はありません。つまりやりたいのは、DBにアカウント情報を登録して、ポップアップウィンドウを消して、ログイン成功画面に遷移 (リダイレクト) させたいのです。それにはポップアップウィンドウ上で以下のスクリプトをレンダリングします。レンダリングする、と書くと仰々しいですが、要はGET/POSTメソッドの返りとして次のHTMLファイルを返せばよいのです (これに気づくのに時間がかかったわけです、ハイ) 。
<!doctype html> <html lang=en> <head> <meta charset=utf-8> <title>OAuth2.0 Result</title> <script type='text/javascript' src='https://ajax.googleapis.com/jsapi'></script> <script type='text/javascript'> google.load("identitytoolkit", "1.0", {packages: ["notify"]}); </script> <script type='text/javascript'> window.google.identitytoolkit.notifyFederatedSuccess({ "email": "{{ email }}", "registered": true }); // use window.google.identitytoolkit.notifyFederatedError(); in case of error </script> </head> <body> </body> </html>
emailはテンプレートパラメータとして渡してあげる必要があります。こんな感じ。
def verify(self, body): # ... 省略 ... query = UserInformation.all().filter('email = ', email) if query == None or query.count() != 1: record = UserInformation(email=email, isAuth2=True) record.put() params = {"email" : email} path = os.path.join(os.path.dirname(__file__), "oauth2.html") self.response.out.write(template.render(path, params))
そうすると、ポップアップウィンドウは消え、/homeにリダイレクトされました。
めでたし、めでたし。