Laravel 7.xで初めてアプリケーションを作ってみる。その１ SNS認証

## はじめに
[Laravelを知らない中級（中年）プログラマーがマイグレーションファイルの仕組みを調べてみたけど全くわからない](https://qiita.com/xioncc/items/1dcec2fbe3485b7e769a) という記事がまだ途中なのですが、せっかく少しだけLaravelのことがわかってきたので小さなアプリケーションでも作ってみようと思います。

## 前提
僕は「**Laravel**」はクイックスタートを数回読んだくらいで全く利用経験がありません。普段から使ってる方からしたら見当違いな事を書くかもしれません。
この記事の対象は PHP 中級者でLaravel未経験者及び初心者を想定しています。PHPの基礎的なシンタックスの説明とかは割愛しますのでご了承ください。
今回は認証がテーマですが、 [Laravel Socialite](https://github.com/laravel/socialite) の説明を読んでその通りに組んでみただけです。ソースも追っていませんしリクエストやレスポンスもチェックしていません。本格運用に使った際の事故などの保証はしません！ご了承ください☆

## 環境
Laravel Framework 7.2.0
PHP 7.3.15

## さぁ行ってみよう！
今回の内容は [こことか](https://readouble.com/laravel/7.x/ja/authentication.html)、[こことか](https://laravel.com/docs/7.x/socialite) を読みながらその通りにやってみたという感じのものです。WEBにもlaravelのSNS認証の記事はいっぱいありますので目新しいものでもないのですが、7.x系でやっている記事はまだあまり見かけません。あと、たいていSNS１種類の説明で済ませているところが多いので、やってみたサービス全部のせておきますね。中級者対象ですので composer のインストールの説明とかは割愛させていただきます。

## Laravel環境を作ろう
さて、初めてのLaravelのアプリケーションを作ってみます。チュートリアルはやってみましたが目的を持って触るのは初めてです。ワクワクしますね。
とりあえず、[公式のインストールの説明](https://readouble.com/laravel/7.x/ja/installation.html) を見ながら環境を作ってみましょう。

適当なワークディレクトリに移動してこんな感じ。

```bash
$ laravel new myApp

...略...

Package manifest generated successfully.
Application ready! Build something amazing.
```

これでオッケーだそうです。簡単ですね！素晴らしい。
組込み開発サーバも用意されているようです。
プロジェクトディレクトリに移動してサーバを動かしてみましょう。
デフォルトポートは8000らしいのですが、いろいろ動かしているので8899あたりを指定してみます。

```bash
$ cd myApp
$ php artisan serve --port=8899
```
http://localhost:8899/ にアクセスしてみます。

<img width="1111" alt="スクリーンショット 2020-05-06 18.28.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/66c4f6af-d639-2ab1-182c-0c05303f0e0b.png">

これで Laravel環境ができあがりです！サクサクですね☆


## 認証の準備
さて、認証の下準備です。
Laravel6 までは ``php artisann make:auth`` というコマンドで認証の基本的なセットアップをしていたらしいのですが、Laravel7 からは [**laravel/ui**](https://readouble.com/laravel/7.x/ja/frontend.html) というのに組み込まれたそうです。
[ドキュメント](https://readouble.com/laravel/7.x/ja/frontend.html) によると、

>LaravelはJavaScriptやCSSプリプロセッサの使用を規定してはいませんが、開発時点の元としてほとんどのアプリケーションで役立つだろうBootstrapやReact、Vueを提供しています。これらのフロントエンドパッケージをインストールするため、LaravelはNPMを使用しています。

とのことです。よく使うライブラリがすでに準備されていてコマンドを叩くだけでベースが出来てしまうみたいです。VueもReactもチャチャッとセッティング出来るっぽい？楽しそうですね。今回はさわりませんが、そのうちこの辺やってみます。

で、認証は **laravel/ui** に **--auth** パラメータを付けて用意するっぽいです。
まずは、 **laravel/ui** をインストールしてその後にartisanコマンドを叩く。
こんな感じ？

```bash
$ composer require laravel/ui 

...略...

Package manifest generated successfully.
30 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

$ php artisan ui vue --auth

...略...

Vue scaffolding installed successfully.
Please run "npm install && npm run dev" to compile your fresh scaffolding.
Authentication scaffolding generated successfully.

$ npm install && npm run dev

...略...

 DONE  Compiled successfully in 6544ms                                           6:47:04 PM

       Asset     Size   Chunks             Chunk Names
/css/app.css  177 KiB  /js/app  [emitted]  /js/app
  /js/app.js  1.4 MiB  /js/app  [emitted]  /js/app
```

<img width="1067" alt="スクリーンショット 2020-05-06 18.48.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/d04a94c4-2302-6128-5dd2-6f5eb4db6c83.png">

あっという間にログインフォームが用意できてしまいましたね。
Bootstrap感が強すぎてアレな感じですが、とりあえずはまぁいいでしょう。

## データベース接続設定
LaravelはMySqlがデフォルトになっているのですが、今回はちょっと作りたいものの事情でSQLite3を使おうと思います。環境設定ファイルを変更します。
``.env`` ファイルをこんな感じに書き換えてしまいます。

```php:.env
DB_CONNECTION=sqlite
#DB_CONNECTION=mysql
#DB_HOST=127.0.0.1
#DB_PORT=3306
#DB_DATABASE=laravel
#DB_USERNAME=root
#DB_PASSWORD=
```

接続設定を変更したら、組込み開発サーバを再起動しておきます。「Control+C」で止めて以下のコマンドを叩きます。

```bash
$ php artisan serve --port=8899
```


## SQLite 3 データベースファイルの作成
接続設定を書き換えた時に、データベースパスを指定していませんでしたね。無指定だと ``PROJECT_ROOT/datebase/database.sqlite`` を参照するそうです。もし指定したい場合は ``.env`` を良しなにどうぞ。
データベースファイルを作ります。

```sql
$ sqlite3 database/database.sqlite
SQLite version 3.28.0 2019-04-15 14:49:49
Enter ".help" for usage hints.

sqlite> .table
sqlite> .q
```

これで空のデータベースファイルができました。

## マイグレーションの実行
Laravelにはデータベース管理用にマイグレーションが用意されています。
SQLite3はいまいちこのマイグレーションの機能が使いにくいみたいなのですが、初期化するために使ってみます。

```php
$ php artisan migrate

Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0 seconds)
```

何個かテーブルが出来たようですね。
ここには出力されてないですが、``migrations``と``sqlite_sequence``というデータベース管理履歴用のテーブルも出来てます。


## 通常認証
ユーザーテーブルが出来ていますので、通常認証がもうできそうな雰囲気ですね。試してみましょう。
ブラウザの右上に 「**Register**」 というリンクがありますのでそこから登録画面に飛びます。
URIは ``http://localhost:8899/register`` こんな感じだと思います。

<img width="797" alt="スクリーンショット 2020-05-06 20.12.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/acf20089-428f-dcbd-5931-c48c3f559ad2.png">

登録フォームの「Register」ボタンを押下すると、ダッシュボードに遷移しました。

<img width="905" alt="スクリーンショット 2020-05-06 20.42.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/cbb01074-9f42-d8a4-4beb-09b2606d8184.png">

通常認証があっという間に用意できましたね。便利ですねぇ。


## Usersテーブルにカラム追加
SNS認証のためにデフォルトのUsersテーブルの構造を少し変更します。
SNSが提供するOAuthの中にはemail情報が返ってこないものもあるとのことで、``null``を受け入れるようにします。また、SNS認証にはパスワードという概念自体ありません。カラム自体を削除するか悩むところですが、とりあえず``null``を受け入れるようにします。追加カラムとしてSNS名とSNSで割り振られたID、あと面白いところでアバター画像を大抵のSNSから取得できるようなのでそれも格納することにします。
以下のようなイメージ。

```sql
    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT
    "name" VARCHAR(255) NOT NULL COLLATE BINARY
    "email_verified_at" DATETIME DEFAULT NULL
    "remember_token" VARCHAR(255) DEFAULT NULL COLLATE BINARY
    "created_at" DATETIME DEFAULT NULL
    "updated_at" DATETIME DEFAULT NULL
-   "email" VARCHAR(255) NOT NULL COLLATE BINARY
-   "password" VARCHAR(255) NOT NULL COLLATE BINARY
+   "email" VARCHAR(255) DEFAULT NULL COLLATE BINARY
+   "password" VARCHAR(255) DEFAULT NULL COLLATE BINARY
+   "avatar" VARCHAR(255) DEFAULT NULL COLLATE BINARY
+   "social_name" VARCHAR(255) NOT NULL COLLATE BINARY
+   "social_id" VARCHAR(255) NOT NULL COLLATE BINARY
```

では、migrationファイルを用意します。
Laravelのマイグレーションでカラム属性を変更する場合はcomposerで **doctrine/dbal** を追加する必要があるそうです。

```bash
$ composer require doctrine/dbal

...略...

Package manifest generated successfully.
31 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
```

入りました。マイグレーションファイルの生成をします。

```php
$ php artisan make:migration prepare_users_table --table users
Created Migration: 2020_05_06_122609_prepare_users_table
```

生成されたマイグレーションファイルを編集します。

```bash
$ vim database/migrations/2020_05_06_122609_prepare_users_table.php
```
※ タイムスタンプは生成時のものになります。

```php:database/migrations/2020_05_06_122609_prepare_users_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class PrepareUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('email')->nullable()->change();
            $table->string('password')->nullable()->change();
            $table->string('avatar')->nullable();
            $table->string('social_name', 255)->nullable();
            $table->string('social_id', 255)->nullable();
            $table->dropIndex('users_email_unique');
        });
        Schema::table('users', function (Blueprint $table) {
            $table->string('social_name')->nullable(false)->change();
            $table->string('social_id')->nullable(false)->change();
        });

    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            //
        });
    }
}
```

down() を書いていません。LaravelのマイグレーションはSQLite使用時に、一つのマイグレーションによる複数カラム削除／変更はサポートされてないそうです。（[くわしくはこちら](https://readouble.com/laravel/5.6/ja/migrations.html)）。本格運用時にマイグレーションでデータベースを管理する場合は、しっかりと手順を踏んだほうが良いですが、この記事では冗長になりすぎるのでロールバックは割愛します。
Larabelのデフォルトで生成された通常認証では ``email`` のカラムにインデックスが貼ってありユニークになっています。今回は別のSNSから戻り値として同じ``email``が返ってくる可能性があるのでユニークインデックスを削除します。
``social_name``と``social_id``は``NOT NULL``にしたいのですが、すでにあるテーブルにカラムを追加する時にいきなり``NOT NULL``のものを追加はできないので``nullable``で追加した後に``NOT NULL``に変更しています。
migrationファイルの編集を終えたら実行の準備です。
追加カラムがNOT NULLなので、先程 Register フォームから登録してしまったデータを削除しておきます。

```sql
$ sqlite3 database/database.sqlite
SQLite version 3.28.0 2019-04-15 14:49:49
Enter ".help" for usage hints.
sqlite> DELETE FROM USERS;
sqlite> VACUUM;
sqlite> .q
```

テーブルレコードの削除を終えたら migration コマンドを叩きます。

```bash
$ php artisan migrate
Migrating: 2020_05_06_122609_prepare_users_table
Migrated:  2020_05_06_122609_prepare_users_table (0.06 seconds)
```

テーブル設計更新はこれで完了です。テーブル構造がこれで良いのか、インデックスは等いろいろと無いわけではないですが、とりあえずSNS認証を実装するのが今回の目的なので次に行きます。


## Socialite のインストール
いよいよSNS認証の実装です。実装と言ってもロジックは [Laravel Socialite](https://github.com/laravel/socialite) に全部任せてしまいます。では、composerでSocialiteをインストールしましょう。

```bash
$ composer require laravel/socialite
Package manifest generated successfully.
31 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
```

とりあえずこれでSNS認証の下準備は終わりです。設定をしていきましょう！


## 実装の計画
今回は以下の４つのサービスで認証を実現したいと思います。

- Twitter
- facebook
- Google
- GitHub

設定が必要なファイルは以下です。

```bash
PROJECT_ROOT
├ .env
├ app
│ ├ User.php
│ └ Http
│ 　 └ Controllers
│ 　 　 └ Auth
│ 　 　 　 └ LoginController.php
├ config
│ └ services.php
├ resources
│ └ views
│ 　 └ auth
│ 　 　 └ login.blade.php
└ routes
　 └ web.php
```

全部設定し終わらないと動きませんので順序考えずに上からサクサク見ていきます。

## /.env
環境設定ファイルですね。以下を追記します。

```php:/.env
TWITTER_CLIENT_ID=Twitterから取得したID
TWITTER_CLIENT_SECRET=Twitterから取得したKEY
TWITTER_CALLBACK_URL="https://my-domain.com/login/twitter/callback"

FACEBOOK_APP_ID=facebookから取得したID
FACEBOOK_APP_SECRET=facebookから取得したKEY
FACEBOOK_CALLBACK_URL="https://my-domain.com/login/facebook/callback"

GOOGLE_CLIENT_ID=Googleから取得したID
GOOGLE_CLIENT_SECRET=Googleから取得したKEY
GOOGLE_CALLBACK_URL="https://my-domain.com/login/google/callback"

GITHUB_CLIENT_ID=GitHubから取得したID
GITHUB_CLIENT_SECRET=GitHubから取得したKEY
GITHUB_CLIENT_CALLBACK="https://my-domain.com/login/github/callback"
```

代入値のIDとKEYは各種サービスから割り当てられたものを記述します。
取得方法は長くなるので、この記事の末尾に一つずつ紹介します。
コールバックURIのドメインはご自分の環境に合わせて設定するのですが、httpsでないとだめなサービスもあった気がします。ローカル環境で構築したい場合は以下のような感じです。

```php
GITHUB_CLIENT_CALLBACK="http://localhost:8899/login/github/callback"
```
ポートは環境に合わせてください。ただ、localhostで実装が難しいサービスもあります。もしローカル環境で試したい場合はLravelの公式で紹介しているようにGitHubでまずは試してみるのがおすすめです。GitHubはさすが開発の事情をわかっているのか localhost での実装がやりやすくなっているようです。
定数名がFACEBOOKだけAPPなのがちょっと迷ったのですが、まぁいいでしょう。

## /app/User.php
Laravel触るのが初めてなのでしっかり理解しているわけではないのですが、EloquentModelの``$fillable``に更新したいカラムを登録するのがお約束みたいです。マイグレーションでテーブル構造変更をしましたので、取り扱いたい情報を追記しておきましょう。

```php
    protected $fillable = [
-       'name', 'email', 'password',
+       'name', 'email', 'password', 'social_name', 'social_id', 'avatar',
    ];
```

## /app/Http/Controllers/Auth/LoginController.php
このファイルがこの記事の肝ですね。このクラス以外は要するに設定だけですしね。
以下の２メソッドを追記します。

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

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Support\Facades\DB;
use Socialite;
use App\User;

...略...

    /**
     * 各SNSの認証ページヘリダイレクト
     *
     * @param string $social sns service name.
     * @return \Illuminate\Http\Response
     */
    public function redirectToProvider($social)
    {
        return Socialite::driver($social)->redirect();
    }

    /**
     * 各SNSから返されるユーザー情報を処理
     *
     * @param string $social sns service name.
     * @return \Illuminate\Http\Response
     */
    public function handleProviderCallback($social)
    {
        if (!in_array($social, ['github', 'facebook', 'google', 'twitter'], true)) {
            return redirect()->to('/home');
        }
        try {
            $socialAccount = $social == 'twitter' ?
                Socialite::driver($social)->user() :
                Socialite::driver($social)->stateless()->user();
        } catch (\Exception $e) {
            return redirect('/home');
        }
        $where = [
            ['social_name', '=', $social],
            ['social_id', '=', $socialAccount->getId()]
        ];
        $userName = $socialAccount->getNickname() ?? $socialAccount->getName();
        $countUser = DB::table('users')->where($where)->count();

        if ($countUser === 1) {
            ($user = User::where($where)->first())
                ->update([
                    'name' => $userName,
                    'email' => $socialAccount->getEmail(),
                    'avatar' => $socialAccount->getAvatar(),
                ]);
        } else {
            ($user = new User([
                'name' => $userName,
                'email' => $socialAccount->getEmail(),
                'social_name' => $social,
                'social_id' => $socialAccount->getId(),
                'avatar' => $socialAccount->getAvatar(),
            ]))->save();
        }

        auth()->login($user, true);
        return redirect()->to('/home');
    }

...略...

```
(*@Xyasya さんのご指摘で修正いたしました。ありがとうございます。2020.07.01*)



上から読んでいきましょう。

#### redirectToProvider()
引数として受け取った文字列をSocialiteクラスのstaticメソッドのdriver()に引数として渡します。Socialiteクラスは渡された引数、つまりSNS名と相応のプロバイダークラスを戻します。その後、メソッドチェーンで戻ってきたプロバイダークラスのredirect()メソッドをコールしています。
この引数``$social``はルート定義でURIから渡されるように設計します。
やっていることは見ただけでなんとなくはわかりますね。

#### handleProviderCallback()
redirectToProvider()で指定されたSNSでOAuth認証処理を終え、戻ってきたらこのメソッドが叩かれます。
引数としてコールバックURIから取得したSNS名が渡されるので一応念の為ホワイトリストでフィルタしておきます。
Sociarite::driver()の戻り値のuser()メソッドをコールします。おそらく認証が失敗すると例外``InvalidStateException``が返ってくるのでそれをキャッチする前提なのでしょう。例外の場合は``/``にリダイレクトで跳ね返します。
今回のSNS4つのうちtwitterだけ仕様が違うので三項演算子で切り分けます。
あとは、データベースに指定したSNS名、SNSIDがあればそれを、なければレコードを登録しログイン処理し``/home``にリダイレクトという感じです。簡単ですね。

## /config/services.php
これは先に見たほうが早いですね。
以下を配列の末尾に追加します。

```php:/config/services.php
    'github' => [
        'client_id' => env('GITHUB_CLIENT_ID'),
        'client_secret' => env('GITHUB_CLIENT_SECRET'),
        'redirect' => env('GITHUB_CLIENT_CALLBACK'),
    ],

    'facebook' => [
        'client_id' =>  env('FACEBOOK_APP_ID'),
        'client_secret' => env('FACEBOOK_APP_SECRET'),
        'redirect' =>  env('FACEBOOK_CALLBACK_URL'),
    ],

    'google' => [
        'client_id' => env('GOOGLE_CLIENT_ID'),
        'client_secret' => env('GOOGLE_CLIENT_SECRET'),
        'redirect' => env('GOOGLE_CALLBACK_URL'),
    ],

    'twitter' => [
        'client_id' => env('TWITTER_CLIENT_ID'),
        'client_secret' => env('TWITTER_CLIENT_SECRET'),
        'redirect' => env('TWITTER_CALLBACK_URL'),
    ],
```

最初に``.env``で設定したものをそのまま追加しているだけですね。
説明はいらないと思います。

## /resources/views/auth/login.blade.php
ログイン用テンプレートファイルですね。
いろいろ書いてありますが、ざっくり削除してボタンだけにしちゃいましょう。
ボタンと言っても見た目だけで単なる Aタグでリンクを貼っているだけですね。
URIは次で定義するルーティングのものです。

```php:/resources/views/auth/login.blade.php
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Login') }}</div>

                <div class="card-body">
                    <a class="btn btn-primary" href="/login/twitter">twitter</a>
                    <a class="btn btn-primary" href="/login/facebook">facebook</a>
                    <a class="btn btn-primary" href="/login/google">google</a>
                    <a class="btn btn-primary" href="/login/github">github</a>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

```

見た目やいろいろは、次の機会に触ってみたいと思います。今回はこれで我慢します。



## /routes/web.php
これもまぁ、先に見てみましょう。
以下をルーティングファイルに追記します。

```php:/routes/web.php
Route::get('login/{provider}', 'Auth\LoginController@redirectToProvider');
Route::get('login/{provider}/callback', 'Auth\LoginController@handleProviderCallback');
```

OAuth認証を実行するURIと認証後戻ってくるURIをルーティングしています。
``{provicer}``に``['github', 'facebook', 'google', 'twitter']``のどれかが入り、その文字列が指定されたコントローラーのメソッドに引数として渡されるという流れですね。

-----

実装は以上です！簡単ですね。ほぼ設定しかしてない印象です。
残るは各SNSでOAuth認証登録してIDとKEYをもらってくるだけです！
そこは別にいらないよーという方はお疲れ様です☆


## 各SNSのOAuth認証登録
各SNSの認証用のIDとKEYの取得のしかたです。わりとページのレイアウトデザインは変わるようなので、2020年5月現在の情報という感じです。見ていきましょう。

### GitHub
まずは GitHub です。設定ページ https://github.com/settings/profile を開きます。
![socialite-github-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/e3f505e0-b589-ad75-0677-ed2dc77c8ac4.png)
右上のプルダウンから Setting から入れます。
![socialite-github-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/59c969ca-891f-9941-61ad-c50faa94d1d9.png)
設定ページのプロフィールに遷移しますので、左のメニューから、開発者設定をクリックします。
![socialite-github-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/735690b5-064b-4cb1-514f-7fba94ee2ff6.png)
開発者設定ページの左メニューから 「OAuth Apps」 をクリックします。
![socialite-github-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/7c84b42a-4738-c3d7-9830-1af49698f40b.png)
OAuth Appsのページを開き、右側にある緑色のボタン、「Register a new application」を押下します。![socialite-github-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/b67c7678-93df-cc46-6a2b-eeaac3692067.png)
アプリケーション情報登録フォームが表示されますので、必要事項を埋めて「Register application」ボタンを押下します。
特に問題のある項目はないと思います。
Authorization callback URL は当然先程設定した GitHub用のコールバックURLです。
![socialite-github-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/f0311b17-79bb-e477-8faf-ccb99dcbad98.png)
**Client ID** と **Client Secret** が発行されましたね。
これを、``.env`` に記述すればオッケーです。

### facebook
次は facebook です。イメージなのかもしれないですが、facebookは開発者用のページレイアウトやGUIが割と頻繁に変わる気がします。
参考になるといいのですが。。。
https://developers.facebook.com/
![socialite-facebook-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/49054651-44a3-ec2d-8d95-0df3de89fae4.png)
facebook開発者ページの右上、マイアプリプルダウンをクリックします。
![socialite-facebook-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/f9b59373-dddf-55fe-e12a-db4b0a2cd1d6.png)
プルダウンメニューから「アプリの作成」メニューを押下します。
![socialite-facebook-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/b3c09ab7-bf91-7cfe-ff8a-59e67ff36e44.png)
アプリの表示名、連絡先メールアドレスを記入して「アプリIDを作成してください」ボタンを押下します。
![socialite-facebook-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/0ed035f1-6fb5-1872-a605-64c99cf37887.png)
ロボットではないよーチェックボックスをチェックして「送信」ボタンを押下します。
![socialite-facebook-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/b53c0d26-469e-fae7-816d-df2248cf80f0.png)
製品を追加画面で「Facebookログイン」を選択します。
![socialite-facebook-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/e4c140d2-e5af-1ad1-087f-2bacdae75cf9.png)
アプリ・プラットフォームの選択で「ウェブ」を選択します。
![socialite-facebook-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/4a57fa8d-8dcb-4f6b-e20e-0158699502af.png)
有効なOAuthリダイレクトURIを記載して「変更を保存」ボタンを押下します。
他の設定は良しなにしてください。
![socialite-facebook-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/5ca1c8bc-fecb-7eb3-e79a-51a3e16a05eb.png)
「ウェブ」のタブでサイトURLを記載して「Save」ボタンを押下します。
![socialite-facebook-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/5147ea64-3bf4-7b9d-3fed-3863f9c889b2.png)
**アプリID** と **app secret** が発行されました。
facebookの場合、色々設定項目があり、どこからどこまでを入力しなくていはいけないか、いまいちわかりにくいところがあるのですが、とりあえず僕の環境では以上の設定でOAuth認証を実装できました。

### Google
Google先生です。サービスによっては結構ニーズがあるかもしれませんね。
https://console.developers.google.com/project
開発者コンソールにアクセスします。
![socialite-google-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/2756d169-c2da-466e-fc28-43f159a3b960.png)
リソース管理で「プロジェクトを作成」を押下します。
![socialite-google-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/7aaf8b61-44d6-49fa-419a-df69147a8bab.png)
新しいプロジェクトのプロジェクト名を入力して「作成」ボタンを押下します。場所は組織なしでも実装できました。
![socialite-google-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/6805e446-acc0-2da6-9ce4-1b6db6cb1fa7.png)
作成したプロジェクトの画面で左上のハンバーガーメニューから「APIとサービス > OAuth同意画面」を押下します。

![socialite-google-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/f8d669f3-34a1-0474-9a25-717f8165d593.png)

OAuth同意画面で外部にチェックを入れて「作成」ボタンを押下します。場合によっては内部なんですかね。今回は深追いしません。
![socialite-google-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/83a40f6d-87b4-faa3-6d9b-5828aca3cfb5.png)
OAuth同意画面を生成するのに使う情報を登録します。
![socialite-google-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/6543cdfc-ca18-93cd-38a5-60734aac1137.png)
同意画面情報を登録したら、「認証情報を作成 > OAuthクライアントID」を押下します。
![socialite-google-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/6fa2d60b-f927-17a2-b243-e68f3c757e52.png)
アプリケーションの種類は「ウェブアプリケーション」を選択し、アプリケーション名を入力。リダイレクトURIも登録してください。承認済みのJavaScript生成元は今回は未入力。「作成」ボタンを押下します。
![socialite-google-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/58ea99bb-f790-2d6a-c494-208539703605.png)
OAuthクライアントのIDとシークレットが発行されました。

Googleのサービスは高機能でとてもありがたいのですが、GUIで迷子になることが多い気がします。僕だけですかね。

### Twitter
最後にTwitterなんですが、ちょっと気をつけないとならない事があります。APIの使用許可の審査が割と厳しいらしく、「勉強のため」「実験で」「とりあえず使ってみたい」などだと弾かれます。しかも、一度弾かれるとその開発者アカウントでのAPI使用ができなくなり、再申請の方法もないようです。僕は一回「とりあえず触ってみたい」と申請してtwitterアカウント一つだめにしました。
とりあえず行ってみましょう。

https://apps.twitter.com/
![socialite-twitter-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/107a94a4-2875-2e40-8864-8ad99016f500.png)
まずは開発者アカウントを申請します。右上のAppから入ります。
![socialite-twitter-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/10a61184-8ffe-88e7-6b87-e217d5b57e8a.png)
Twitterの開発者ツールを使う理由はなんなの？と聞かれ選択する画面になります。
前回の反省から、いい子ですよ。ビジネスとして真剣に考えてますよをアピールするためにＢ２Ｂ製品を作るためと選択しました。
まぁ、何でも良いんでしょうけど、Twitter社を説得するつもりで選んだほうが良いかもです。
![socialite-twitter-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/f2646d49-67ab-187a-db8d-280d47173a61.png)
このTwitterアカウントであってる？と聞かれます。アカウントを切り替えたいなどあったらここで切り替えるんでしょう。オッケーで次に進みます。
![socialite-twitter-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/8a05a4e5-d7bf-1ac0-4538-c9ae0005cb7f.png)
組織情報を教えてよと聞かれます。出来る限りそれっぽいほうが良いと思います。ビジネスホームページなど持っていたらそれを記入したほうが良いかも。
![socialite-twitter-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/b17a990e-61f3-ed92-4a35-1679a361fd5d.png)
TwitterのAPIをなんで使いたいの？と聞かれます。
今回は認証なんで、自社サービスの認証にTwitterのOAuth認証を使わせてほしいとかなんとか。いい子で真摯な姿勢をアピールします。英文で書かなければなりませんが、Google翻訳とかで大丈夫そうですね。
Twitterのデータを分析する？と聞かれます。マーケティング系のお仕事でAPIを使いたいなら、是非分析するにしたいと思うところですね。ただ、昨今の事情で政治関係やらなんやらでTwitter社的に都合の悪い使われ方をすることにかなり警戒しているっぽい感じです。全く予定がないなら「いいえ」にしたほうが良いかもしれません。
Twitterのツイートやリツイート、イイね等いろいろな機能を使いたい？と聞かれます。
せっかくだから使わせてほしいと言っておいたほうが良さそうです。が、真面目に使いたいんです、悪い事はしません！アピールが必要かも？
Twitterのデータを外部公開する？と最後に政府機関で利用する？と聞かれます。
うーん、これを「はい」にする場合はしっかりと理由を作っておいたほうが良いかもしれません。
次に進みます。
![socialite-twitter-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/a9d73cf2-31ba-96f5-3d96-73a4ccd2c0d6.png)
利用規約ですね。
同意して次に進みましょう。
![socialite-twitter-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/790b31c7-7b88-a4d8-9123-d92c549d6cb3.png)
申請が出来ました。確認メールが送信されているので、メールを受信したら「Confirm your email」のリンクを押下して申請完了しましょう。
このあと、申請内容に突っ込みどころがあると「そこんところどうなってるの？」とTwitter社からメールが来ます。英語で返信しないとなりません。僕も数回やり取りしたのですが、却下されました。結構そのパターンは辛いので、申請時に一発クリアを目指したほうが良いと思います。

管理者アカウントが取得できましたので早速アプリケーションを登録してみましょう。
![socialite-twitter-13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/9ef5c542-5477-f5b1-9401-7d89e686bdbf.png)

初回にスクリーンショットを撮り忘れてしまい、すでにひとつアプリケーションがある状態です。
初回はレイアウトが違ったような気がします。
「Create an app」ボタンを押下します。

![socialite-twitter-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/aeaa5c91-fef7-ccbe-379a-65830b1293a1.png)

上から、アプリケーション名、アプリケーションの説明、APIを利用するURLと続きます。
ローカル環境からの呼び出しは出来ないようです。
ツイッター認証するかどうかのチェックボックスです。もちろんチェックします！
コールバックのURLは設定したものを記入します。
利用規約のURL、プライバシーポリシーのURL、組織名、組織URLと入力します。
ここは、可能な限りビジネスライクな情報を入れたほうが良いと思います。
最後にアプリケーションの使用方法を記入します。
英語で入力しなければなりませんが、Google翻訳で問題ありません。
内容はできる限り正直に真摯にビジネスライクに書きましょう！
「Create」ボタンを押下して次に進みます。
![socialite-twitter-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/bc8529cd-12b6-2f1b-5501-57710d43403a.png)
開発者規約です。
データの使い方やオートメーションツイートなどのルールについて書いてあります。
違反しないように気をつけましょう。
「Create」ボタンを押下することでアプリケーション登録の完了です。
![socialite-twitter-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/5372d149-b1e5-47b0-8e35-8255c1092eaa.png)
アプリケーション情報です。
![socialite-twitter-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/2b007003-2c82-2267-91cb-05d74d484393.png)
IDとキーが取得できました。

もう一点このままではTwitterでのOAuth認証時にコールバックにemail情報が返ってきません。
以下を設定しましょう。
![socialite-twitter-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70576/c227de4f-057f-c8d4-5f8f-799b756c09ba.png)
下部の 「Request email address from users」 のチェックボックスをチェックします。

これで目標の４サービスのOAuth認証の実装が完了しました！
むしろ各SNSへの申請の方が大変でしたね。

## コールバックで取得できるレスポンスについて
おまけです。
各SNSから戻ってくる情報について載せておきます。
参考にしていただけたら幸いです。

### GitHub

```php
  +token: "****************************************"
  +refreshToken: null
  +expiresIn: null
  +id: 12345678
  +nickname: "hideyukiMORI"
  +name: "HideyukiMORI"
  +email: "info@xion.cc"
  +avatar: "https://avatars0.githubusercontent.com/u/14159242?v=4"
  +user: array:31 [▼
    "login" => "************"
    "id" => 12345678
    "node_id" => "********************"
    "avatar_url" => "https://avatars0.githubusercontent.com/u/14159242?v=4"
    "gravatar_id" => ""
    "url" => "https://api.github.com/users/hideyukiMORI"
    "html_url" => "https://github.com/hideyukiMORI"
    "followers_url" => "https://api.github.com/users/hideyukiMORI/followers"
    "following_url" => "https://api.github.com/users/hideyukiMORI/following{/other_user}"
    "gists_url" => "https://api.github.com/users/hideyukiMORI/gists{/gist_id}"
    "starred_url" => "https://api.github.com/users/hideyukiMORI/starred{/owner}{/repo}"
    "subscriptions_url" => "https://api.github.com/users/hideyukiMORI/subscriptions"
    "organizations_url" => "https://api.github.com/users/hideyukiMORI/orgs"
    "repos_url" => "https://api.github.com/users/hideyukiMORI/repos"
    "events_url" => "https://api.github.com/users/hideyukiMORI/events{/privacy}"
    "received_events_url" => "https://api.github.com/users/hideyukiMORI/received_events"
    "type" => "User"
    "site_admin" => false
    "name" => "HideyukiMORI"
    "company" => "AYANE International Inc."
    "blog" => "hideyuki-mori.com"
    "location" => "Japan"
    "email" => "info@xion.cc"
    "hireable" => true
    "bio" => "Professional illustrator. Born in Tokyo in 1973. Graduated from Department of Japanese Painting, Musashino Art University. "
    "public_repos" => 2
    "public_gists" => 0
    "followers" => 0
    "following" => 0
    "created_at" => "2015-09-07T08:32:51Z"
    "updated_at" => "2020-05-09T12:48:29Z"
```

### facebook

```php
  +token: "*****************************************************************************************************************************************************************************************"
  +refreshToken: null
  +expiresIn: 1234567
  +id: "1234567890123456"
  +nickname: null
  +name: "森 秀之"
  +email: "xi@xion.cc"
  +avatar: "https://graph.facebook.com/v3.3/3303148003029667/picture?type=normal"
  +user: array:3 [▼
    "name" => "森 秀之"
    "email" => "xi@xion.cc"
    "id" => "1234567890123456"
  ]
  +"avatar_original": "https://graph.facebook.com/v3.3/3303148003029667/picture?width=1920"
  +"profileUrl": null
```

### Google

```php
  +token: "***************************************************************************************************************************************************************************"
  +refreshToken: null
  +expiresIn: 1234
  +id: "123456789012345678901"
  +nickname: null
  +name: "彩イラストレーター"
  +email: "**********@gmail.com"
  +avatar: "https://lh3.googleusercontent.com/a-/AOh14GjfWbBWspqJs6p5rtlb_D_2ToanTkkdDdkW29TNOw"
  +user: array:11 [▼
    "sub" => "123456789012345678901"
    "name" => "彩イラストレーター"
    "given_name" => "イラストレーター"
    "family_name" => "彩"
    "picture" => "https://lh3.googleusercontent.com/a-/AOh14GjfWbBWspqJs6p5rtlb_D_2ToanTkkdDdkW29TNOw"
    "email" => "**********@gmail.com"
    "email_verified" => true
    "locale" => "ja"
    "id" => "123456789012345678901"
    "verified_email" => true
    "link" => null
  ]
  +"avatar_original": "https://lh3.googleusercontent.com/a-/AOh14GjfWbBWspqJs6p5rtlb_D_2ToanTkkdDdkW29TNOw"
```

### Twitter

```php
  +token: "**************************************************"
  +tokenSecret: "*********************************************"
  +id: "1234567890"
  +nickname: "ayane_inc"
  +name: "彩音インターナショナル株式会社"
  +email: "*****@xion.cc"
  +avatar: "http://pbs.twimg.com/profile_images/1205169805948411905/E_tsyv7t_normal.jpg"
  +user: array:42 [▼
    "id_str" => "1234567890"
    "entities" => array:2 [▼
      "url" => array:1 [▼
        "urls" => array:1 [▼
          0 => array:4 [▼
            "url" => "https://t.co/UNrUdL5qX3"
            "expanded_url" => "https://www.youtube.com/channel/UCgARD8FD-VZFzRlFLgo4oAA?view_as=subscriber"
            "display_url" => "youtube.com/channel/UCgARD…"
            "indices" => array:2 [▼
              0 => 0
              1 => 23
            ]
          ]
        ]
      ]
      "description" => array:1 [▼
        "urls" => []
      ]
    ]
    "protected" => false
    "followers_count" => **
    "friends_count" => **
    "listed_count" => *
    "created_at" => "Thu Sep 05 18:27:31 +0000 2013"
    "favourites_count" => ***
    "utc_offset" => null
    "time_zone" => null
    "geo_enabled" => false
    "verified" => false
    "statuses_count" => ***
    "lang" => null
    "status" => array:24 [▼
      "created_at" => "Thu Apr 30 06:16:44 +0000 2020"
      "id" => 1234567890123456789
      "id_str" => "1234567890123456789"
      "text" => """
        RT @*************: 合皮コインケース | WakiWaki Party https://t.co/Kzi4qhJ6Vm @ayane_incさんから
        #AYANE_online
        """
      "truncated" => false
      "entities" => array:4 [▼
        "hashtags" => array:1 [▼
          0 => array:2 [▼
            "text" => "AYANE_online"
            "indices" => array:2 [▼
              0 => 84
              1 => 97
            ]
          ]
        ]
        "symbols" => []
        "user_mentions" => array:2 [▼
          0 => array:5 [▼
            "screen_name" => "*************"
            "name" => "*******"
            "id" => 1234567
            "id_str" => "1234567"
            "indices" => array:2 [▼
              0 => 3
              1 => 17
            ]
          ]
          1 => array:5 [▼
            "screen_name" => "ayane_inc"
            "name" => "彩音インターナショナル株式会社"
            "id" => 1234567890
            "id_str" => "1234567890"
            "indices" => array:2 [▼
              0 => 69
              1 => 79
            ]
          ]
        ]
        "urls" => array:1 [▼
          0 => array:4 [▼
            "url" => "https://t.co/Kzi4qhJ6Vm"
            "expanded_url" => "https://ayane.co.jp/product/coin-case_wakiwakiparty"
            "display_url" => "ayane.co.jp/product/coin-c…"
            "indices" => array:2 [▼
              0 => 45
              1 => 68
            ]
          ]
        ]
      ]
      "source" => "<a href="https://mobile.twitter.com" rel="nofollow">Twitter Web App</a>"
      "in_reply_to_status_id" => null
      "in_reply_to_status_id_str" => null
      "in_reply_to_user_id" => null
      "in_reply_to_user_id_str" => null
      "in_reply_to_screen_name" => null
      "geo" => null
      "coordinates" => null
      "place" => null
      "contributors" => null
      "retweeted_status" => array:23 [▼
        "created_at" => "Mon Apr 27 04:04:25 +0000 2020"
        "id" => 1234567890123456789
        "id_str" => "1234567890123456789"
        "text" => """
          合皮コインケース | WakiWaki Party https://t.co/Kzi4qhJ6Vm @ayane_incさんから
          #AYANE_online
          """
        "truncated" => false
        "entities" => array:4 [▼
          "hashtags" => array:1 [▼
            0 => array:2 [▼
              "text" => "AYANE_online"
              "indices" => array:2 [▼
                0 => 65
                1 => 78
              ]
            ]
          ]
          "symbols" => []
          "user_mentions" => array:1 [▼
            0 => array:5 [▼
              "screen_name" => "ayane_inc"
              "name" => "彩音インターナショナル株式会社"
              "id" => 1234567890
              "id_str" => "1234567890"
              "indices" => array:2 [▼
                0 => 50
                1 => 60
              ]
            ]
          ]
          "urls" => array:1 [▼
            0 => array:4 [▼
              "url" => "https://t.co/Kzi4qhJ6Vm"
              "expanded_url" => "https://ayane.co.jp/product/coin-case_wakiwakiparty"
              "display_url" => "ayane.co.jp/product/coin-c…"
              "indices" => array:2 [▼
                0 => 26
                1 => 49
              ]
            ]
          ]
        ]
        "source" => "<a href="http://twitter.com" rel="nofollow">Twitter Web Client</a>"
        "in_reply_to_status_id" => null
        "in_reply_to_status_id_str" => null
        "in_reply_to_user_id" => null
        "in_reply_to_user_id_str" => null
        "in_reply_to_screen_name" => null
        "geo" => null
        "coordinates" => null
        "place" => null
        "contributors" => null
        "is_quote_status" => false
        "retweet_count" => 2
        "favorite_count" => 7
        "favorited" => true
        "retweeted" => true
        "possibly_sensitive" => false
        "lang" => "ja"
      ]
      "is_quote_status" => false
      "retweet_count" => 2
      "favorite_count" => 0
      "favorited" => true
      "retweeted" => true
      "possibly_sensitive" => false
      "lang" => "ja"
    ]
    "contributors_enabled" => false
    "is_translator" => false
    "is_translation_enabled" => false
    "profile_background_color" => "C0DEED"
    "profile_background_tile" => false
    "profile_link_color" => "1DA1F2"
    "profile_sidebar_border_color" => "C0DEED"
    "profile_sidebar_fill_color" => "DDEEF6"
    "profile_text_color" => "333333"
    "profile_use_background_image" => true
    "has_extended_profile" => true
    "default_profile" => true
    "default_profile_image" => false
    "following" => false
    "follow_request_sent" => false
    "notifications" => false
    "translator_type" => "none"
    "suspended" => false
    "needs_phone_verification" => false
    "url" => "https://t.co/UNrUdL5qX3"
    "profile_background_image_url" => "http://abs.twimg.com/images/themes/theme1/bg.png"
    "profile_background_image_url_https" => "https://abs.twimg.com/images/themes/theme1/bg.png"
    "profile_image_url" => "http://pbs.twimg.com/profile_images/1205169805948411905/E_tsyv7t_normal.jpg"
    "profile_image_url_https" => "https://pbs.twimg.com/profile_images/1205169805948411905/E_tsyv7t_normal.jpg"
    "profile_banner_url" => "https://pbs.twimg.com/profile_banners/1732746061/1576170124"
    "location" => ""
    "description" => """
      AYANE は イラストレーションとシステム両面からエンターテインメントを提案します。日本のエンターテインメントを世界へ発信することに取り組み続けます。
      代表的なプロジェクト スーパーねねにゃん @nenenyan_com / 桜Exhibition @hyacca_info
      """
  ]
  +"avatar_original": "http://pbs.twimg.com/profile_images/1205169805948411905/E_tsyv7t.jpg"
```

Twitterはかなりな情報が取得できるようですね。
申請が難しいだけあって、なかなかおもしろいことができそうな感じです。
やりがいがありそうですね！

以上、何かのお役に立てたら幸いです。

次回は、画面系をやってみたいと思っています。


## 参考
以下の記事を参考にさせていただきました。
ありがとうございます。

https://qiita.com/u-dai/items/91df3b923dc82fed5b76
https://qiita.com/KeisukeKudo/items/18dd8a342a4bdd43913c
https://qiita.com/zaburo/items/26e94bce0068f479a8e8