Laravel で作ったアプリに Google アカウントでログインする方法（OAuth 2.0）

# やりたいこと

Laravel で作ったウェブアプリに Google でログインする機能を実装します。

OAuth という技術を使うのですが、OAuth について知りたい方は以下の記事をどうぞ。

[一番分かりやすい OAuth の説明](https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be)

# 環境

 - CentOS: 7.6
 - PHP: 7.2
 - Laravel: 5.8
 - socialite: 3.2


# Google での事前準備

## 1. Google Developers Console にアクセス

https://console.developers.google.com/project


## 2. プロジェクトの作成

`プロジェクトを作成` をクリックします。

![google_developers_console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238891/f5d8244b-5f9d-8902-e666-51386a9476c5.png)

プロジェクト名を入力します。

![new_project.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238891/acaa6a5e-26bc-652f-9bdb-7b170e5e7a0c.png)


これでプロジェクトが作成されました。


## 3. クライアントID、クライアント シークレットの発行

`認証情報を作成` > `OAuth クライアント ID` をクリック。

![new_auth_client_id.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238891/81788765-da48-e127-e61f-39dfee28ef48.png)

OAuth 同意画面の設定を行います。
ここでは特に何もいじらず、アプリケーション名だけ入力します。

（※最初は「Google Login App」というアプリ名にしようとしたのですが、「Google と勘違いさせるような名前は許さん」と怒られてしまったので、ここで「OAuth Test App」に変更しました。）

![OAuth_agreement.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238891/0d3e3403-bd7e-1e1a-eea8-aedb16251995.png)

これでクライアント ID とシークレットが発行されます。
後ほど Laravel での設定で使用します。

![client_id_and_secret.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238891/5ac5d9cf-90fe-0b3d-564f-442b1351e74c.png)


## 4. リダイレクト URI の設定

Google での認証が完了したあとにリダイレクトする URI を指定します。
登録したアプリケーション名をクリックします。

![auth_info.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238891/041f4088-d044-d46e-19c8-9ff0d1e7b199.png)

「承認済みのリダイレクト URI」の欄に `http://localhost/login/google/callback` を指定します。
今回は localhost 上でテストするだけですが、本番用の URI も同時に指定できます。

![redirect_uri.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238891/ab11c29a-ccdb-f3d8-0f10-12f9e32174e2.png)


## 5. Google+ API の有効化

API ライブラリから Google+ API を選択し、有効化します。

![enable_google_plus.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238891/e047f9e3-e671-1d19-ed1e-eade77effd5b.png)

# Laravel での処理

だいたい Laravel 公式ドキュメントに書いてあるやり方に従います。

https://readouble.com/laravel/5.8/ja/socialite.html

## 1. Larave のプロジェクトの作成

Laravel のプロジェクトを作成します。

```
$ composer create-project laravel/laravel プロジェクト名 --prefer-dist
```


## 2. `.env` の編集

作成したプロジェクトファイルの直下にある `.env` を編集します。
主に編集が必要なのは、アプリの URL、DB の設定、先ほど発行したクライアント ID とクライアントシークレットです。
DB についてはあらかじめ用意しておいてください。

```.env
...
APP_URL=http://localhost:8000
...
DB_DATABASE=データベース名
DB_USERNAME=DBユーザー名
DB_PASSWORD=DBパスワード
...
GOOGLE_CLIENT_ID=123456789-xxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxx
```

`APP_URL` はそのままだとポート番号が含まれていませんので、ここで付け加えておきます。
アプリを `php artisan serve` で動かすなら 8000、Apatche や Nginx で動かすなら自分で設定したポート番号です。

DB はあらかじめ作っておいた DB の情報を設定します。

最後に、先ほどつくったクライアント ID とクライアントシークレットを設定します。


## 3. 認証機能の実装

次に Laravel 備え付けの認証機能を実装します。

```
php artisan make:auth
```

これで認証まわりで必要なファイルが自動的に用意されます。

```
php artisan migrate
```

マイグレーションを実行してユーザーテーブルを作成します。
これでひとまず Laravel 備え付けの認証機能が使えるようになりました。

一度ユーザー登録してみて正しく動くか確認してみてください。


## 4. Laravel Socialite のインストール

OAuth による認証を簡単に実装するため、Laravel Socialite をインストールします。

> 典型的なフォームベースの認証に付け加え、LaravelはLaravel Socialite(=名士)による、OAuthプロバイダによるシンプルで便利な認証方法も提供しています。Socialiteは現在、Facebook、Twitter、LinkedIn、Google、GitHub、Bitbucketによる認証をサポートしています。

*参照：https://readouble.com/laravel/5.8/ja/socialite.html*

自分の環境ではバージョン指定しないとエラーが発生したため、バージョン 3.2 を指定してインストールしました。
（最新の4系はそもそもインストールできず、3.0 はインストールできるのですが、グーグルからのコールバックを受け取るタイミングでエラーが発生しました。）

```
composer require laravel/socialite 3.2
```

次に Socialite をファサードとして使えるようにするために `config/app.php` を編集します。

```config/app.php
'providers' => [
    // 省略
    Laravel\Socialite\SocialiteServiceProvider::class, // 'providers' 内に追記
],

'aliases' => [
    // 省略
    'Socialite' => Laravel\Socialite\Facades\Socialite::class, // 'aliases' 内に追記
],
```


## 5. OAuth 認証情報の設定

Google から取得した認証情報を `config/services.php` に設定します。

```config/services.php
// 以下を追記
'google' => [
    'client_id' => env('GOOGLE_CLIENT_ID'),
    'client_secret' => env('GOOGLE_CLIENT_SECRET'),
    'redirect' => env('APP_URL') . '/login/google/callback',
]
```


## 6. コントローラの設定

Google ログイン用のコントローラを作成してもいいのですが、今回はあらかじめ用意されている `LoginController` に処理を書いていきます。

```app/Http/Controllers/Auth/LoginController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Socialite; // 追記

class LoginController extends Controller
{
    // 省略
    public function redirectToGoogle()
    {
        // Google へのリダイレクト
        return Socialite::driver('google')->redirect();
    }

    public function handleGoogleCallback()
    {
        // Google 認証後の処理
        // あとで処理を追加しますが、とりあえず dd() で取得するユーザー情報を確認
        $gUser = Socialite::driver('google')->stateless()->user();
        dd($gUser);
    }
}
```


## 7. ルーティングの設定

Google へリダイレクトするためのルートと、Google での認証後にコールバックを受けるルートを設定します。
下記の２行を追記します。

```routes/web.php
Route::get('login/google', 'Auth\LoginController@redirectToGoogle');
Route::get('login/google/callback', 'Auth\LoginController@handleGoogleCallback');
```


## 8. ビューの編集

Google でログインするためのボタンを設置します。
ここらへんは適当ですが、`<form>...</form>` のすぐ下にボタンを設置しました。

```resources/views/auth/login.blade.php
<form>
    <!-- 省略 -->
</form>
<div class="form-group row mt-2">
    <div class="col-md-8 offset-md-4">
        <a href="/login/google" class="btn btn-secondary" role="button">
            Google Login
        </a>
    </div>
</div>
```

こんな感じ。

![google_login_view.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238891/cf52bfa4-e8df-82b0-b7ff-c34d05cf72b0.png)

うまくいけば、この状態で「Google Login」を押すと Google へのログインを求められるかと思います。ログインに成功すると `LoginController` の `handleGoogleCallback` アクション内に記載した `dd($gUser)` によって `$gUser` 内の情報が見れます。

## 9. アプリケーションへのログイン機能を実装

`LoginController` をさらに編集して、アプリへログインできるように設定します。
メールアドレスを照合して、DB 内にユーザーが存在すればそのままログイン、存在しなければ新たにユーザーを作成してログインとします。

```app/Http/Controllers/Auth/LoginController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use App\User; // 追記
use Socialite; // 追記

class LoginController extends Controller
{
    // 省略
    public function redirectToGoogle()
    {
        return Socialite::driver('google')->redirect();
    }

    public function handleGoogleCallback()
    {
        $gUser = Socialite::driver('google')->stateless()->user();
        // email が合致するユーザーを取得
        $user = User::where('email', $gUser->email)->first();
        // 見つからなければ新しくユーザーを作成
        if ($user == null) {
            $user = $this->createUserByGoogle($gUser);
        }
        // ログイン処理
        \Auth::login($user, true);
        return redirect('/home');
    }

    public function createUserByGoogle($gUser)
    {
        $user = User::create([
            'name'     => $gUser->name,
            'email'    => $gUser->email,
            'password' => \Hash::make(uniqid()),
        ]);
        return $user;
    }
}
```

パスワードの初期値は `uniqid()` で適当に生成してハッシュ化していますが、本来であればよりセキュアな文字列を使った方がいいかと思われますのでご注意ください。

# 感想

OAuth を利用したログイン機能を初めて実装してみましたが、Socialite のおかげで想像以上に簡単に実装できました。
だいたいの人が Google アカウントを持っていると思いますので、こういった機能があるととても便利ですよね。
今後もバンバン利用していきたいと思います！