Google Sign−In for Websites でページをまたいでサインイン・サインアウトを行うメモ

ウェブアプリで 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](https://developers.google.com/identity/sign-in/web/sign-in)

チュートリアルに従って実装していきます。
まずは、GCP でプロジェクトの構成（チュートリアル上に設定箇所有り）を行い、ウェブアプリケーションの"CLIENT ID"を取得します。
Auth Client の選択は、"Web browser"（実装のやり方によって選ぶものは変わります。"Web Server"の場合、認証後、トークンをサーバーに送る形になります。）、Authorized Javascript Origin は、任意に設定します。
GCP のコンソールページで後から変更できるのでとりあえずは、Auth Client の"Web browser"と"があっていれば OK です。

続いて、サインインを実装する HTML ファイルの script タグに"Google Platform Library"を使うよと宣言。

```html:login.html
<script src="https://apis.google.com/js/platform.js" async defer></script>
```

更に meta タグに、使用するのは"Google Sign-In"であるということを先程取得した"CLIENT ID"と交えてセットします（取得した"CLIENT ID"は、"YOUR_CLIENT_ID"部に代入）。

```html:login.html
<meta name="google-signin-client_id" content="YOUR_CLIENT_ID.apps.googleusercontent.com">
```

そして、サインインボタンを置きたいところに"g-signin2"という class の div タグを挿入。

```html:login.html
<div class="g-signin2" data-onsuccess="onSignIn"></div>
```

上記のように、class に"g-signin2"と書けば、勝手に Google のイイ感じのボタンが設置されます。自分好みにボタンの形を変えたい場合は、[Building a custom Google Sign-In button](https://developers.google.com/identity/sign-in/web/build-button) を参照してコツコツやってね！とのことです。

上記 div タグを記述し、どんなものが出るのかなー？と、ローカル環境で確認しようと試しても、何も出てこないです。落ち着いてウェブ上で表示させましょう。

data-onsuccess 属性は、Google サインイン（認証まで）が行われるとセットした処理を行うというものです。上記の場合処理は、onSignIn 関数が実行されます。
ウェブアプリを開くブラウザで、既に Google サインインが完了した状態になっていた場合、ウェブページを読み込む際に、既に Google サインインが行われているものとして、セットした関数が実行されます。普段から Google アカウントを使用（Gmail や Google ドライブなどで）していると、最初からログインされた状態になっていて、なんで勝手にサインイン後の処理が実行されるんだ？となるので注意してください。

サインインの状態は、ボタン上の表示でわかります。"g-signin2"のボタンであれば下図のように、サインインされていない場合は「ログイン」と表示され、既にサインインされている場合は「Signed in」と表示されます。
<img width="645" alt="Screen Shot 2019-07-16 at 14.16.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92506/6aabe512-1eda-f4fd-5288-4ab209951f3d.png">

最後に、サインアウトの実装です。

```html:login.html
<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](https://developers.google.com/identity/sign-in/web/reference#gapiauth2getauthinstance) を参照にいきます。すると、気になる記述を発見。

>このメソッドを呼び出す前に、gapi.auth2.init（）でGoogleAuthオブジェクトを初期化する必要があります。
（日本語訳）

ぉ、おう。。。

色々ありましたが、正解は下記。

```html:home.html
<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 して、認証インスタンスを取得し、それでサインアウトを行うようです。

正解のサインアウトの手順をまとめると、、

1. script タグ、meta タグをサインアウトを実装する HTMLファイルに記述
2. "auth2"を load
3. GoogleAuth オブジェクト（gapi.auth2）を初期化（init）
4. GoogleAuth オブジェクトで認証インスタンスを取得
5. 取得した認証インスタンスに対してサインアウト

サインインしたのとは別の HTMLファイルでサインアウトをしようとすると1, 2, 3の手順を余計に行っておく必要があるようです。

# まとめ

サインインとサインアウトを別々の HTMLファイルで実施する時は、サインアウト時に認証情報を取り直さないといけません。なのでそれぞれの HTMLファイルで Google Sign-In に関連した meta タグ、script タグを記載する必要があるようです。当然といえば当然の話でしたね。

以上、スタックオーバーフローで集めてきた情報をまとめたメモ書きでした。