rmdiary

日記です。

Google Sign-Inを使ってアカウント管理を行う

pythonでwebアプリを作ろうと思い立って、昨日から作り始めました。そこで今回はGoogle Sign-Inを使ってみました。

developers.google.com

Google Sign-Inを使う理由

  •  自前でユーザー登録のフローを実装する手間がなくなる
  • Googleのシステムを利用するためセキュリティを考えても強固
  • ユーザーから見ても利用したサービスを1つのアカウントで管理できて便利

そもそもOAuth2.0とかOpenID Connectとは違うのか?

OAuth2.0とOpenID Connectを利用して作られたのがGoogle Sign-Inという位置付けです。OAuth2.0やOpenID ConnectのAPIを自分で書く必要がほとんど無く、Sign Inの動作を簡単に使えるようになっています。

導入

Google Sign-Inの導入はとても簡単です。上記リンク先のガイドもご覧ください。

<html lang="ja">
 
<head>
   
<meta name="google-signin-scope" content="profile email">
   
<meta name="google-signin-client_id" content="YOUR_CLIENT_ID.apps.googleusercontent.com">
   
<script src="https://apis.google.com/js/platform.js" async defer></script>
 
</head>
 
<body>。
   
<div class="g-signin2" data-onsuccess="onSignIn" data-theme="dark"></div>
   
<script>
     
function onSignIn(googleUser) {
       
// Useful data for your client-side scripts:
       
var profile = googleUser.getBasicProfile();
        console
.log("ID: " + profile.getId()); // Don't send this directly to your server!
        console
.log("Name: " + profile.getName());
        console
.log("Image URL: " + profile.getImageUrl());
        console
.log("Email: " + profile.getEmail());

       
// The ID token you need to pass to your backend:
       
var id_token = googleUser.getAuthResponse().id_token;
        console
.log("ID Token: " + id_token);
     
};
   
</script>
 
</body>
</html>

リンク先にある上記コードをHTMLファイルに貼り付けるだけでGoogleアカウントによるサインインを行えます。

注意点としては、このhtmlファイルはサーバー上で実行しないと動作しません(ローカル含む)。htmlファイルを直接ウェブブラウザで開いても何も起こらないので気をつけましょう。

設定すべき点はメタタグ部分です。

<meta name="google-signin-scope" content="profile email">
<meta name="google-signin-client_id" content="YOUR_CLIENT_ID.apps.googleusercontent.com">

google-signin-scopeではGoogleアカウントで利用する権限を指定します。Sign-Inを利用するためにprofileは必要ですが、emailは不要であれば削除できます。また、Google Driveを利用したいと思ったらhttps://www.googleapis.com/auth/driveを追加すればOKです。

google-signin-client_idはDeveloper ConsoleでCredentialsを作成すると得られるIDです。

 

サーバー側での動作

ここまででもSigned Inという表示は得られますが、javascriptはクライアントサイドで動作するため、このままでは自分のWebアプリのユーザーとして認識することができません。先ほどのjavascriptで得られたid_tokenを自分のサーバーに送ることで、正しくGoogleによって認証が行われたか確認し、ユーザーを識別することができるようになります。

まずは、javascriptのonSignInメソッドの最後に以下を追加します。

var xhr = new XMLHttpRequest();
xhr
.open('POST', 'https://yourbackend.example.com/tokensignin');
xhr
.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr
.onload = function() {
  console
.log('Signed in as: ' + xhr.responseText);
};
xhr
.send('idtoken=' + id_token);

 URL部分は送信先の自分のサーバーを指定します。

サーバー側では、pythonの場合以下の動作を実行します。Google API Client Library for Pythonが必要になるので、インストールしておきましょう。JavaPHP、.NETでもClient Libraryをインストールすれば同じような流れで実装できます。

from oauth2client import client, crypt

# token = POST['idtoken']
CLIENT_ID = "YOUR_CLIENT_ID.apps.googleusercontent.com"
APPS_DOMAIN_NAME = "yourdomain.com"

try:
    idinfo
= client.verify_id_token(token, CLIENT_ID)
   
if idinfo['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
       
raise crypt.AppIdentityError("Wrong issuer.")
   
if idinfo['hd'] != APPS_DOMAIN_NAME:
       
raise crypt.AppIdentityError("Wrong hosted domain.")
except crypt.AppIdentityError:
   
# Invalid token
userid
= idinfo['sub']

idinfoのチェックを行っています。idinfo['hd']は、Google Apps for work等を使っていて特定のドメインからのSign Inしか受け付けない、という場合に指定します。なので、基本的にはこのif文は不要です。

crypt.AppIdentityErrorが発生しなければ正しくログインされたことになります。useridでユーザーを識別できるようになりますので、データベースに保存しておくなりSESSIONを発行するなりしてログイン後の動作に移ります。