LoginSignup
40
41

More than 5 years have passed since last update.

Laravel5.7でソーシャルログイン(Socialite利用)

Last updated at Posted at 2018-10-07

やりたいこと

  • いわゆるソーシャルログイン
  • とりあえずFacebook(とTwitter)に対応したい

方針

make:authで生成した各種ファイルを利用

php artisan make:auth

準備

当然のことながらLaravelをインストール。.envの設定を済ませておく。

socialiteのインストール

composer require laravel/socialite

その他の設定

providers登録等はいらない(恐らくは5.5からのAuto dicoveryのおかげ)。

実装

以下のような感じでソーシャルログインを実装する。

  • SNSログインボタンを設置
  • SNSで認証+ユーザー情報を取得する(emailの取得が必須)
  • 該当のemailがなければ登録後、ログイン(1回目)
  • 該当のemailがあればそのままログイン(2回目以降)

Model

モデルとしてはUser(usersテーブル)を利用するが、標準ではpasswordが必須になっているのでマイグレーションファイルでnullを許可しておく。
(ソーシャルログインでは自社サービスとしてはpasswordを要求しないので)

database/migrations/xxxx_create_users_table.php
<?php

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

class CreateUsersTable extends Migration
{
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
+           $table->string('password')->nullable();
            $table->rememberToken();
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('users');
    }
}

マイグレート実行。

php artisan migrate

Controller

処理はControllerに記述します。以下は最低限度の実装。
ソーシャルログインボタンからのリンク処理とCallback処理の2つ。

App/Http/Controllers/Auth/LoginController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

use Socialite;
use App\User;
use Auth;

class LoginController extends Controller
{
    use AuthenticatesUsers;

    protected $redirectTo = '/home';

    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    //ログインボタンからリンク
    public function socialLogin($social)
    {
        return Socialite::driver($social)->redirect();
    }

    //Callback処理
    public function handleProviderCallback($social)
    {
        //ソーシャルサービス(情報)を取得
        $userSocial = Socialite::driver($social)->stateless()->user();
        //emailで登録を調べる
        $user = User::where(['email' => $userSocial->getEmail()])->first();

        //登録(email)の有無で分岐
        if($user){

            //登録あればそのままログイン(2回目以降)
            Auth::login($user);
            return redirect('/home');

        }else{

            //なければ登録(初回)
            $newuser = new User;
            $newuser->name = $userSocial->getName();
            $newuser->email = $userSocial->getEmail();
            $newuser->save();

            //そのままログイン
            Auth::login($newuser);
            return redirect('/home');

        }
    }
}

Route

必要なルートを定義します。上記メソッドに対応したログインボタンからのリンクとCallbackのURLを定義。

route.php
<?php

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', 'HomeController@index');

+//ログインボタンからのリンク
+Route::get('/login/{social}', 'Auth\LoginController@socialLogin')->where('social', 'facebook|twitter');
+//コールバック用
+Route::get('/login/{social}/callback', 'Auth\LoginController@handleProviderCallback')->where('social', 'facebook|twitter');

View

大げさなコードを貼り付けていますが、基本はlogin.blade.phpの一部にログインボタンとリンクを追加しただけ。
ボタンの引数にfacebookとかtwitterとか渡してます。

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">
                    <form method="POST" action="{{ route('login') }}">
                        @csrf

+                       <div class="form-group row">
+                           <label for="name" class="col-sm-4 col-form-label text-md-right">Login With</label>
+                           <div class="col-md-6">
+                               <a href="{{ url('login/facebook')}}" class="btn btn-social-icon btn-facebook"><i class="fa fa-facebook"></i></a>
+                               <a href="{{ url('login/twitter')}}" class="btn btn-social-icon btn-twitter"><i class="fa fa-twitter"></i></a>
+                           </div>
+                       </div>

                        <div class="form-group row">
                            <label for="email" class="col-sm-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" required autofocus>

                                @if ($errors->has('email'))
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $errors->first('email') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>

                                @if ($errors->has('password'))
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $errors->first('password') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group row">
                            <div class="col-md-6 offset-md-4">
                                <div class="form-check">
                                    <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>

                                    <label class="form-check-label" for="remember">
                                        {{ __('Remember Me') }}
                                    </label>
                                </div>
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Login') }}
                                </button>

                                <a class="btn btn-link" href="{{ route('password.request') }}">
                                    {{ __('Forgot Your Password?') }}
                                </a>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

あと、Facebookとかのアイコンを利用するためにfont-awesomeを導入しておきます。
共通layoutを定義しているapp.blade.phpの<head>部分の最後に入れておきます。

app.blade.php
<head>
.
.
+<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
</head>

config/services.phpと.envの設定

そもそもsocialiteの動作のためには、各SNSサービスのIDやSecret、Callback用のURLが必要になります。
各サービスの開発者向けサイトに行って、取得しておきます。

config/services.php

外部サービスに関する情報はservices.phpに記述します。以下を追記します。
実際の値は.evnから取得するようにしておきます。

    'facebook' => [
        'client_id' => env('FACEBOOK_API_ID'),
        'client_secret' => env('FACEBOOK_API_SECRET'),
        'redirect' => env('FACEBOOK_CALLBACKURL'),
    ],

.env

上記に対応する設定を記述します。

CallbackのURLは下記でngrokで取得した URL+/login/facebook/callback とします。

FACEBOOK_API_ID=23792659xxxxxxxx
FACEBOOK_API_SECRET=9526653842f242201d157402xxxxxxxx
FACEBOOK_CALLBACKURL=https://c3fxxxxx.ngrok.io/login/facebook/callback

Facebook開発者サイトでの情報取得・登録

Callback URLは.envで設定したものと同じである必要があります。

テスト

最近のソーシャルログインや各種アプリ開発は開発環境にもhttpsを要求してきます。要はlocalhostじゃテストしにくい状況となっています。
それを解決するためにngrokを利用します。ngrokはローカルURLをグローバルURLにマップしてくれます。

こちらが大変参考になります。

ngrokのインストール

brew cask install ngrok

ngrokの起動

Laravel(php artisan serve)を利用してるので、8000を指定して起動します。

ngrok http 8000

動作確認

https://hostname/login からソーシャルボタンをクリックしてログインできるか確認します。

40
41
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
41