laravel5.5でemeil認証＋ソーシャルアカウント認証

#前提

見た目を作ることしかわからない自分が勉強がてらやってみてログってる中身です。
これをやったらマズイのよ、という点がありましたらご指摘いただけると幸いですmm

#やること

・laravelデフォルト認証（email+password）とソーシャルアカウント認証、両方を可能にする
・デフォルトなページ遷移（メール認証がAPIだったりSPAでは無い）
・データベースは１テーブルに収める
イメージ

| id |username|email|password|facebook_id|facebook_name|以下略SNS_id|以下略SNS_name|
|:----|:----|:----|:----|:----|:----|:----|:----|

・以下はFacebook&Twitterを認証に追加で記載

#環境

laravel5.5 インストール済み
SQlite3を利用

#参考

[（WPJ）面倒なソーシャルログインをLaravelでサクッと実装！Socialiteが便利だ:https://www.webprofessional.jp/easily-add-social-logins-to-your-app-with-socialite/](https://www.webprofessional.jp/easily-add-social-logins-to-your-app-with-socialite/)

https://readouble.com/laravel/5.5/ja/authentication.html
https://readouble.com/laravel/5.5/ja/socialite.html

#事前準備

##基本設定
データベースをsqliteに設定

##ユーザー認証機能を有効化

```termcap
$ php artisan make:auth　//→認証用のビュー、コントローラーをインストール
$ php artisan migrate //→デフォルトで作成されているuser関連のmigrationファイルのマイグレート
```

##パッケージの追加

Socialite：laravel公式パッケージ。
>現在、Facebook、Twitter、LinkedIn、Google、GitHub、Bitbucketによる認証をサポートしています。

```termcap
$ composer require laravel/socialite
```

```php:config/app.php
'providers' => [
    〜略〜
    Laravel\Socialite\SocialiteServiceProvider::class,　//追記
],
'aliases' => [
    〜略〜
    'Socialite' => Laravel\Socialite\Facades\Socialite::class,　//追記
],
```

※ソーシャルプロバイダープロジェクトを活用することで、もっと様々なサービスの連携も可能
https://socialiteproviders.github.io/


##連携サービスの認証情報

やり方省略

・facebook
https://developers.facebook.com/
・twitter
https://apps.twitter.com/

##認証情報をアプリ側に持たせる

configファイルで環境設定を呼び出す記述追加

```php:config/service.php
return [
~略~　以下追記
    'facebook' => [
        'client_id' => env('FACEBOOK_CLIENT_ID'),
        'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
        'redirect' => env('FACEBOOK_CLIENT_CALLBACK'),
    ],

    'twitter' => [
        'client_id' => env('TWITTER_CLIENT_KEY'),
        'client_secret' => env('TWITTER_CLIENT_SECRET'),
        'redirect' => env('TWITTER_CLIENT_CALLBACK'),
    ],
]
```

```php:.env
以下追記（＆自分環境に応じて書き換え）
#facebook
FACEBOOK_CLIENT_ID=FACEBOOKのClientID
FACEBOOK_CLIENT_SECRET=FACEBOOKのClientSecret
FACEBOOK_CLIENT_CALLBACK=FACEBOOKのCallBackURL

#twitter
TWITTER_CLIENT_KEY=TWITTERのconsumerkey
TWITTER_CLIENT_SECRET=TWITTERのconsumersecret
TWITTER_CLIENT_CALLBACK=TWITTERのCallBackURL
```

#ルーティングの設定

```php:routes/web.php
<?php

Route::get('login/{provider}', 'Auth\SocialAccountController@redirectToProvider');
Route::get('login/{provider}/callback', 'Auth\SocialAccountController@handleProviderCallback');
```

・各サービスに向かうやつと、帰ってきた（コールバック）時用の２つを記載。
・どの認証サービスでも同じく使えるように、ルートパラメーター{provider}で指定。

#ビューの設定

ユーザー認証のビューのファイルが自動でできているので、とりあえず、↓を追記。
（あとで整える！…は省略。）

```php:resouces/views/auth/login.blade.php
<a href="auth/login/facebook">Sign in with Facebook</a>
<a href="auth/login/twitter">Sign in with Twitter</a>
```

ルーティングに設定したURLを元に記載。

```
ルーティング：　login/{provider}
↓
ビュー：　auth/login/プロバイダー（サービス）名
```

…なんか```auth/```が必要やった。

#データベースの設定

(↓※2018.3.4追記)データベース修正用に[doctrine/dbal]パッケージインストール

```termcap
$ composer require doctrine/dbal
```


```termcap
$ php artisan make:migration prepare_users_table --table users
```
→タイムスタンプ_prepare_users_table.phpが作成される

```php:database/migrations/タイムスタンプ_prepare_users_table.php
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('facebook_id')->unique()->nullable();          
        $table->string('facebook_name')->nullable();
        $table->string('twitter_id')->unique()->nullable();          
        $table->string('twitter_name')->nullable();
    });
}
```

【抜粋】

```php:デフォルトで入っているカラムを変更。
//SNS経由での登録はパスワードがいらなかったり、emailがなかったりするので、nullableに変更
        $table->string('email')->nullable()->change();
        $table->string('password')->nullable()->change();
```

```php:カラム追加
//ユーザーサムネイル設定できるように追加
        $table->string('avatar')->nullable();
```

```php:SNSアカウントを保存するカラム追加
//それぞれのSNSコラムになんか入ってたらそのSNS認証使える、みたいにできないかと…な、こんな設定。
        $table->string('facebook_id')->unique()->nullable();          
        $table->string('facebook_name')->nullable();
        $table->string('twitter_id')->unique()->nullable();          
        $table->string('twitter_name')->nullable();
```

設定したら、マイグレート

```termcap
$ php artisan migrate
```

#コントロラーの設定

##コントローラファイル作成

```php:routes/web.php（設定済）
Route::get('login/{provider}', 'Auth\SocialAccountController@redirectToProvider');
Route::get('login/{provider}/callback', 'Auth\SocialAccountController@handleProviderCallback');
```

↑で設定したように、「SocialAccountController」コントローラを作成し、
コントローラアクション
・redirectToProvider
・handleProviderCallback
を設定できるようにする

```termcap
$ php artisan make:controller 'Auth\SocialAccountController'
```

→app/Http/Contorollers/Auth/SocialAccountController.phpが作成される

※認証系コントローラを同じディレクトリにまとめるため、/auth内に作成。


(↓※2018.3.4追記)socialite呼びだし

```php:SocialAccountController.php
namespace App\Http\Controllers\Auth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

use Socialite; //⇦追記

class SocialAccountController extends Controller
```


##自分のサービス→認証サービス

各認証サービスへリダイレクト。

```php:SocialAccountController.php
class SocialAccountController extends Controller
{
    public function redirectToProvider($provider) {
        return Socialite::driver($provider)->redirect();
    }
}
```


##認証サービス→自分のサービス


```php:SocialAccountController.php
class SocialAccountController extends Controller
{
  public function handleProviderCallback($provider) {
    try {
      $providerUser = Socialite::driver($provider)->user();

      $user = DB::table('users')->where('email', $providerUser->getEmail())->first();

      if( is_null($user) ){

        if( is_null($providerUser->getNickname()) ){
          $providerUserNickName = $providerUser->getName();
        }else{
          $providerUserNickName = $providerUser->getNickname();
        }

        $userd = User::create([
          'name' => $providerUserNickName,
          'email' => $providerUser->getEmail(),
        ]);

      }else{
        $userd = User::find( $user->id );
      }

      switch ($provider) {
        case "facebook":
          if( is_null($userd->facebbook_id) ){
            $userd->facebook_id = $providerUser->getId();
            if( is_null($providerUser->getNickname()) ){
              $userd->facebook_name = $providerUser->getName();
            }else{
              $userd->facebook_name =$providerUser->getNickname();
            }
          }
          break;
        case "twitter":
          if( is_null($userd->twitter_id) ){
            $userd->twitter_id = $providerUser->getId();
            if( is_null($providerUser->getNickname()) ){
              $userd->twitter_name = $providerUser->getName();
            }else{
              $userd->twitter_name = $providerUser->getNickname();
            }
          }
          break;
      }
      $userd->save();

      auth()->login($userd, true);
      return redirect()->to('/home');
         
    } catch (\Exception $e) {
      return redirect("/");
    }

  }
}
```



取得した連携SNSアカウントのmailがすでに登録されているユーザーのemailと同じなら、そのユーザーのSNSカラムに追記できるようになりました。
同じemailが無かったら新しいアカウントです。

ところが…

#twitterはデフォルトではemail情報が取れない

参照：https://qiita.com/wheatandcat/items/fe66c7ee2521a6966505

取得するには利用規約とプライバシーポリシーを準備する必要があるようです。