Webサイトにグーグルアカウントでログインする機能を実装する[実装編]

グーグル

ウェブサイトにグーグルアカウントでのログイン機能「Google Sign-In」を実装する、

の「実装編」です

前回の準備編はこちらです

概要

簡単に概要を説明します

今回の実装に登場するのは以下の2ファイルです

  • login.html ← ログインボタンがあるページ
  • server-side.php ← ユーザ情報を取得して登録処理などをするサーバ側のPHPファイル

ユーザはログインページであるlogin.html内のグーグルログインボタンを押してアカウントを選択します

そのデータを取得したサーバ側のserver-side.phpで、登録処理なんかをします

Google Sign-Inの実装

1. クライアントIDの定義

login.html

<head>
  <meta name="google-signin-client_id" content="準備編で取得したクライアントID">
</head>

準備編で取得したクライアントIDをMetaタグで定義します

Metaタグなので、HTMLのheadタグの間に書きます

2. Google Platformライブラリのロード

login.html

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

JavascriptでGoogle Platform Libraryをロードします

3. 「グーグルでログインする」ボタンの設置

ログインボタンをHTMLで設置します

login.html

<div class="g-signin2" data-onsuccess="onSignIn"></div>

オプション

ダークモード

<div class="g-signin2" data-onsuccess="onSignIn" data-theme="dark"></div>

data-theme="dark"を設定すると見た目が変わります(全然ダークじゃないけど)

オフライン

<div class="g-signin2" 
    data-onsuccess="onSignIn" 
    data-scope="https://www.googleapis.com/auth/plus.login"
    data-accesstype="offline"
    data-redirecturi="https://example.com/redirect-path"></div>

data-accesstypeでofflineを指定すると、通常のポップアップでのアカウント選択画面ではなく、画面遷移型に切り替えることができます

4. プロフィール情報の取得

login.html

<script>
  function onSignIn(googleUser) {
 
    // プロフィールの取得
    var profile = googleUser.getBasicProfile();
    console.log('ID: ' + profile.getId());// このIDをサーバに送ってはいけない
    console.log('Name: ' + profile.getName());
    console.log('Image URL: ' + profile.getImageUrl());
    console.log('Email: ' + profile.getEmail());
 
    // トークンの取得(サーバーにはこちらを送信)
    var id_token = googleUser.getAuthResponse().id_token;
    console.log('token: ' + token);
  }
</script>

3で設置したボタンを押下すると、このコールバックが呼び出されます

ここでユーザのプロフィール情報が取得できます、が、

ここで必要なのはトークンだけです

公式ドキュメントにも書いてありますが、profile.getId()で取得できるIDをサーバに送ってはいけません

サーバに送るのはgoogleUser.getAuthResponse().id_tokenで取得したトークンのみです

(ここで取得できる名前やメールアドレスは、サーバ側でも別途取得できます)

5. サーバへ送信

4で取得したトークンを、サーバへ送信します

login.html

<script>
  function onSignIn(googleUser) {
 
    // トークンの取得(サーバーにはこちらを送信)
    var id_token = googleUser.getAuthResponse().id_token;
 
    // 接続を解除して、2回目以降の自動ログインを防止
    var auth2 = gapi.auth2.getAuthInstance();
    auth2.disconnect();
 
    // 5.サーバへ送信
    var xhr = new XMLHttpRequest();
 
    // open()で指定するのは、サーバ側のURL
    xhr.open('POST', 'https://example.com/server-side.php');
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
 
    // 処理が終わった後に呼ばれるコールバック
    xhr.onload = function() {
      if(xhr.readyState == 4 && xhr.status == 200 && response){
        // ログイン完了後にリダイレクト
        window.location.href = 'https://example.com';
      }
      else{
        console.log('エラー');
      }
    };
    xhr.send('idtoken=' + id_token);
  }
</script>

次の6の処理が正常に終了すると、xhr.onloadで指定したfunction()が呼ばれます

その中で、ログインした後に表示したいページへリダイレクトします(上の例ではhttps://example.com)

6.サーバ側の処理

5で送信したユーザ情報を、サーバ側で受け取ります

server-side.php

require_once 'vendor/autoload.php';
 
// POSTで送られてくるトークンを取得.
$id_token = filter_input(INPUT_POST, 'idtoken');
 
$client = new Google_Client(['client_id' => '準備編で取得したクライアントID']);
$payload = $client->verifyIdToken($id_token);
 
if ($payload) {
  $userid = $payload['sub'];
  // ユーザ登録やログイン処理など
  // 結果を出力
  echo $userid;
} else {
  // Invalid ID token
  // 結果を出力
  echo null;
}

ユーザを識別するIDにはpayloadのsubを利用します

メールアドレスは変更される可能性があるのでユーザの識別に利用してはいけません

成功するとpayloadには以下のような情報がセットされます

(
    [iss] => accounts.google.com
    [azp] => 0123456789.apps.googleusercontent.com
    [aud] => 0123456789.apps.googleusercontent.com
    [sub] => 1234567890123456789
    [email] =>accounts@mail.com
    [email_verified] => 1
    [at_hash] => lkejfaiowef__fsdlDFJf
    [name] => Boku No Namae
    [picture] => https://lh3.googleusercontent.com/0123456789
    [given_name] => Boku
    [family_name] => Namae
    [locale] => ja
    [iat] => 1523456789
    [exp] => 1534567890
    [jti] => 23kljfewlklkj32jf2i3jfl2kjfsdklfjs
)

処理が終わったら、5のJavascriptで定義したxhr.onloadへ返却するためのパラメーターを出力(echo)します