ウェブアプリで Google Sign-In を使用して、ログインページ(login.html とか)でサインインしたはいいけど、別のページ(home.html とか)でサインアウトする方法で少し悩んだのでメモ_φ(・_・。
背景
ログインしてくる Google のアカウントが本当に存在しているものであるかを確認したい、またメールアドレスの情報など欲しかったので、Google Sign-In を使用してみました。
iOS で Google 認証・認可の実装は行ったことはあったのですが、今回は Google の機能を使いたいというわけではなく、認可までは必要ないなと思ったので、Google Sign-In を採用しました。(アクセストークンやリフレッシュトークンのやり取りをしなくていいのでお手軽!)
加えて、Google Sign-In を実装しようとしているウェブアプリで、「場合によってアカウントを使い分ける人もいるだろうなー」と思い、サインアウトまで実装することにしました。
そして、今回作成したウェブアプリは、今時の SPA(シングルページアプリケーション)ではありません。ログインページ、リスト表示ページや管理ページが、HTMLファイルごとに分かれているという形になっています。
以上、背景をまとめると下記になります。
- Google Sign-In で認証
- サインインとサインアウトの機能
- 複数ページのウェブアプリケーション
Google Sign-In の実装
基本的に用意されたチュートリアルを見ながら、実装すればできます。
下記リンクが、Google 公式のチュートリアルです。
Integrating Google Sign-In into your web app
チュートリアルに従って実装していきます。
まずは、GCP でプロジェクトの構成(チュートリアル上に設定箇所有り)を行い、ウェブアプリケーションの"CLIENT ID"を取得します。
Auth Client の選択は、"Web browser"(実装のやり方によって選ぶものは変わります。"Web Server"の場合、認証後、トークンをサーバーに送る形になります。)、Authorized Javascript Origin は、任意に設定します。
GCP のコンソールページで後から変更できるのでとりあえずは、Auth Client の"Web browser"と"があっていれば OK です。
続いて、サインインを実装する HTML ファイルの script タグに"Google Platform Library"を使うよと宣言。
<script src="https://apis.google.com/js/platform.js" async defer></script>
更に meta タグに、使用するのは"Google Sign-In"であるということを先程取得した"CLIENT ID"と交えてセットします(取得した"CLIENT ID"は、"YOUR_CLIENT_ID"部に代入)。
<meta name="google-signin-client_id" content="YOUR_CLIENT_ID.apps.googleusercontent.com">
そして、サインインボタンを置きたいところに"g-signin2"という class の div タグを挿入。
<div class="g-signin2" data-onsuccess="onSignIn"></div>
上記のように、class に"g-signin2"と書けば、勝手に Google のイイ感じのボタンが設置されます。自分好みにボタンの形を変えたい場合は、Building a custom Google Sign-In button を参照してコツコツやってね!とのことです。
上記 div タグを記述し、どんなものが出るのかなー?と、ローカル環境で確認しようと試しても、何も出てこないです。落ち着いてウェブ上で表示させましょう。
data-onsuccess 属性は、Google サインイン(認証まで)が行われるとセットした処理を行うというものです。上記の場合処理は、onSignIn 関数が実行されます。
ウェブアプリを開くブラウザで、既に Google サインインが完了した状態になっていた場合、ウェブページを読み込む際に、既に Google サインインが行われているものとして、セットした関数が実行されます。普段から Google アカウントを使用(Gmail や Google ドライブなどで)していると、最初からログインされた状態になっていて、なんで勝手にサインイン後の処理が実行されるんだ?となるので注意してください。
サインインの状態は、ボタン上の表示でわかります。"g-signin2"のボタンであれば下図のように、サインインされていない場合は「ログイン」と表示され、既にサインインされている場合は「Signed in」と表示されます。
最後に、サインアウトの実装です。
<a href="#" onclick="signOut();">Sign out</a>
<script>
function signOut() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
console.log('User signed out.');
});
}
</script>
簡単ですね!
しかし、ここで困る。
上記のコードは、「サインインとサインアウトが同じHTMLファイルだった場合」の実装です。
例えば、サインインを「login.html」、サインアウトを「home.html」と分けて実装すると、上記コードのまま実装することはできません。上記コードだけで実装しようとすると、下記のエラーが発生します。
Uncaught ReferenceError: gapi is not defined
"gapi"が未定義と。。
script タグや meta タグは別のHTMLファイル「login.html」上で定義していることになるので、gapi が未定義なのもしかたないかーと思い、「home.html」にも同様の script タグと meta タグを追加。試してみたところ、今度は下記のエラーが発生。
Uncaught TypeError: Cannot read property 'getAuthInstance' of undefined
未定義の"getAuthInstance"プロパティが読めない。。
"getAuthInstance" ってどんなやつやねんということで、公式ドキュメントの Google Sign-In JavaScript client reference を参照にいきます。すると、気になる記述を発見。
このメソッドを呼び出す前に、gapi.auth2.init()でGoogleAuthオブジェクトを初期化する必要があります。
(日本語訳)
ぉ、おう。。。
色々ありましたが、正解は下記。
<a href="#" onclick="signOut();">Sign out</a>
<script>
function signOut() {
gapi.load('auth2', function () {
gapi.auth2.init().then(function () {
var auth2 = gapi.auth2.getAuthInstance()
auth2.signOut().then(function () {
console.log('User signed out.');
});
});
});
}
</script>
"auth2"をロードして、init して、認証インスタンスを取得し、それでサインアウトを行うようです。
正解のサインアウトの手順をまとめると、、
- script タグ、meta タグをサインアウトを実装する HTMLファイルに記述
- "auth2"を load
- GoogleAuth オブジェクト(gapi.auth2)を初期化(init)
- GoogleAuth オブジェクトで認証インスタンスを取得
- 取得した認証インスタンスに対してサインアウト
サインインしたのとは別の HTMLファイルでサインアウトをしようとすると1, 2, 3の手順を余計に行っておく必要があるようです。
まとめ
サインインとサインアウトを別々の HTMLファイルで実施する時は、サインアウト時に認証情報を取り直さないといけません。なのでそれぞれの HTMLファイルで Google Sign-In に関連した meta タグ、script タグを記載する必要があるようです。当然といえば当然の話でしたね。
以上、スタックオーバーフローで集めてきた情報をまとめたメモ書きでした。