Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
15
Help us understand the problem. What is going on with this article?
@tetsu-upstr

Laravelログイン機能に複数のSNS認証を追加する(socialite)

More than 1 year has passed since last update.

socialiteでSNSログインを実装

Laravelで、SNSのアカウントを使ったログインの実装をした時のメモです。

socialiteとは

soscialiteとは、Laravelで簡単にOAuth(SNS認証)を可能にするパッケージツールです。

はじめに、composerを使ってsocialiteをインストールします。

$ composer require laravel/socialite

モデル

各テーブルに紐付くUser.phpモデルとIdentityProvider.phpのリレーションを設定します。

$ php artisan make:model IdentityProvider -m

Userモデルの設定

ログイン時に新規作成するUserテーブルは、挿入できるように追記しておきます。

App/User.php

class User extends Authenticatable
{
    use Notifiable;

     function IdentityProviders()
    {
        // IdentityProviderモデルと紐付ける 1対多の関係
        return $this->hasMany(IdentityProvider::class);
    }

ユーザーに対して各プロバイダーは「1対多」の関係になるのでhasManyを設定します。

IdentityProviderモデルの設定

App/IdentityProvider.php
class IdentityProvider extends Model
{
    protected $fillable = ['user_id', 'provider_name', 'provider_id'];

    function user()
    {
        return $this->belongsTo(User::class);
    }
}

プロバイダーの情報はユーザーに属するのでbelongsToを設定します。

マイグレーションの設定

ソーシャルログインは、メールアドレスやパスワードがない場合があるのでnullable()にしておきます。

database/migrations/xxxx_xx_xx_xxxxxx_create_users_table.php
class CreateUsersTable extends Migration
{
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('email')->unique()->nullable(); // 追記
            $table->string('password')->nullable(); // 追記
            $table->rememberToken();
            $table->timestamps();
        });
    }

スクリーンショット 0032-05-29 18.06.44.png
usersに紐付けるプロバイダー情報のテーブルも作成します。
今回はテーブル名をidentity_providersとしました。

database/migrations/xxxx_xx_xx_xxxxxx_create_Identity_Providers_table
class CreateIdentityProvidersTable extends Migration
{
    public function up()
    {
        Schema::create('identity_providers', function (Blueprint $table) {
            $table->unsignedBigInteger('user_id');
            $table->foreign('user_id')->references('id')->on('users'); // 外部キー制約
            $table->string('provider_id');
            $table->string('provider_name');
            $table->primary(['provider_name', 'provider_id']); // 複合キー
            $table->unique(['user_id', 'provider_name']); // ユニーク制限
            $table->timestamps();
        });
    }

usersテーブルのuser_ididentity_providersidを紐付けて管理したいので、
$table->foreign('user_id')->references('id')->on('users');のように、
マイグレーションの外部キー制約を設定します。
スクリーンショット 0032-05-29 18.06.26.png

ルーティング

ユーザーを認証する準備ができたら、2つのルートを設定します。

1つはユーザをOAuthプロバイダにリダイレクトするためのもので、
もう1つは認証後にプロバイダからのコールバックを受け取るためのものです。

web.phpの設定

web.phpにてログイン画面でボタンをクリックしたときのリンクを設定します。
たとえば、シンプルにGitHubだけで実装する場合には下記のようになります。

routes/web.php
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');

Route::get('login/github', 'Auth\LoginController@redirectToProvider');
Route::get('login/github/callback', 'Auth\LoginController@handleProviderCallback');

複数のプロバイダーで認証を実装する場合は、引数に変数を用いて変換します。

routes/web.php
Route::get('/login/{provider}', 'Auth\LoginController@redirectToProvider')->where('social', 'github|google|facebook');
Route::get('/login/{provider}/callback', 'Auth\LoginController@handleProviderCallback')->where('social', 'github|google|facebook');

Controllerの設定

設定した情報からSNS認証ログインを実装します。

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

namespace App\Http\Controllers\Auth;

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

use Auth;
use Socialite;
use App\User;
use App\IdentityProvider;

class LoginController extends Controller
{
    use AuthenticatesUsers;

    protected $redirectTo = '/home';

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

    public function redirectToProvider($social)
    {
        return Socialite::driver($social)->redirect();
    }

    public function handleProviderCallback($provider)
    {
        try {
            $user = Socialite::driver($provider)->user();
        } catch (Exception $e) {
            return redirect('/login');
        }

        $authUser = $this->findOrCreateUser($user, $provider);
        Auth::login($authUser, true);
        return redirect($this->redirectTo);
    }

    public function findOrCreateUser($providerUser, $provider)
    {
        $account = IdentityProvider::whereProviderName($provider)
                    ->whereProviderId($providerUser->getId())
                    ->first();

        if ($account) {
            return $account->user;
        } else {
            $user = User::whereEmail($providerUser->getEmail())->first();

            if (!$user) {
                $user = User::create([
                    'email' => $providerUser->getEmail(),
                    'name'  => $providerUser->getName(),
                ]);
            }

            $user->IdentityProviders()->create([
                'provider_id'   => $providerUser->getId(),
                'provider_name' => $provider,
            ]);

            return $user;
        }
    }
}

redirectToProvider()メソッドは、ユーザーをOAuthプロバイダーに送信します。
handleProviderCallback()メソッドは、プロバイダーからユーザーの情報を取得します。

findOrCreateUser()メソッドは、ユーザーがまだ存在しない場合にユーザーを作成して、
プロバイダーから取得した情報をidentity_providersテーブルに登録します。

コンフィグレーション

Socialiteを使用する前に、アプリケーションが使用するOAuthサービスの認証情報を設定します。

services.phpの設定

認証情報を必要とする各プロバイダーの設定を行います。

config/services.php
return [
    'github' => [
        'client_id' => env('GITHUB_CLIENT_ID'),
        'client_secret' => env('GITHUB_CLIENT_SECRET'),
        'redirect' => env('APP_URL') . '/login/github/callback',
    ],

    'google' => [
        'client_id' => env('GOOGLE_CLIENT_ID'),
        'client_secret' => env('GOOGLE_CLIENT_SECRET'),
        'redirect' => env('APP_URL') . '/login/google/callback',
    ],

    'facebook' => [
        'client_id' =>  env('FACEBOOK_APP_ID'),
        'client_secret' => env('FACEBOOK_APP_SECRET'),
        'redirect' =>  env('APP_URL') . '/login/facebook/callback',
    ],
];

app.phpでsocialiteの設定を行います。

config/app.php
    'providers' => [
        // 追加
        Laravel\Socialite\SocialiteServiceProvider::class,
    ],

    'aliases' => [
        // 追加
        'Socialite' => Laravel\Socialite\Facades\Socialite::class,
    ],

.envの設定

.envファイルにて、各プロバイダーで取得したクライアントIDとシークレットIDを設定します。
- GitHub
- Google
- Facebook

.env
GITHUB_CLIENT_ID=xxxxxxxxxxxxxxxxxxxx
GITHUB_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

GOOGLE_CLIENT_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxx

FACEBOOK_APP_ID=xxxxxxxxxxxxxxx
FACEBOOK_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Viewの設定

ログイン画面のテンプレートに追記します。
(※下記の例では、Bootstrapを使用しています。)

login.blade.php
    <div class="form-group row mt-5">
        <label for="name" class="col-sm-4 col-form-label text-md-right">SNSログイン</label>
        <div class="col-md-6">
            <a href="{{ url('login/google')}}" class="btn btn-danger"><i class="fa fa-google"> Google</i></a>
            <a href="{{ url('login/facebook')}}" class="btn btn-primary"><i class="fa fa-facebook"> Facebook</i></a>
            <a href="{{ url('login/github')}}" class="btn btn-secondary"><i class="fa fa-github"> GitHub</i></a>
        </div>
    </div>

デフォルトのログインテンプレートに追記すると、このように表示されます。
スクリーンショット 0032-05-23 8.16.02.png

これで、各プロバイダーの情報を使ってログインができるようになります。

参考

15
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
15
Help us understand the problem. What is going on with this article?