5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Laravel+Nuxt.jsでGoogleログイン認証

Posted at

はじめに

LaravelとNuxt.jsのリポジトリを完全に分離してSNSログイン認証を実装してみた。
SocialiteはLaravelリポジトリ内のbladeを使う前提で設計されており、紹介されている記事もこの方法ばかりで、結構苦労した。
Firebaseのログイン認証やAuth0を利用する方法もあるようだが、今回はSocialiteを使用する。

SocialiteをそのままAPIとして流用すると、セッションエラーや、CORSエラーが出る。リダイレクトによるCORSエラーは解決方法がないようなので、リダイレクト先をフロントエンドにし、フロントエンドからクエリをAPIに送るようにする。

主にこちらの記事を参考にしてます。ほぼ同じです。ありがとうございます。

使用環境

  • laravel v9.5.1
  • Nuxt.js v2.15.7

Google Cloud Platform の登録

登録方法はこちらを参照する。

注意点は、「承認済みのリダイレクト URI」には、フロントエンドのコールバック先を入力する。
ファイルは後ほど作成するが、 http://localhost:3000/login/googleCallback にしておく。

忘れないうちに、laravelの.envファイルにクライアントIDとクライアントシークレットを保存する。CALLBACKは、「承認済みのリダイレクト URI」に登録したものと同じにする。

.env
GOOGLE_CLIENT_ID=******
GOOGLE_CLIENT_SECRET=******
GOOGLE_CALLBACK=http://localhost:3000/login/googleCallback

Laravel側の設定

users_tableの編集

まず、デフォルトのusersテーブルはpasswordが必須になっている。Googleログインではデータベースにパスワードを保存しないため、nullableの設定にしておく。
アバター画像、プロバイダー、プロバイダー先のIDも保存したいので追加する。

database/migrations/2014_10_12_000000_create_users_table.php
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password')->nullable();
            $table->string('avatar')->nullable();
            $table->string('provider')->nullable();
            $table->string('provider_id')->nullable()->unique();
            $table->rememberToken();
            $table->timestamps();
        });
    }

migrateを実行する。

php artisan migrate

モデルのfillableに先程のカラムを追加する。

app/Models/User.php
    protected $fillable = [
        'name',
        'email',
        'password',
        'avatar',
        'provider',
        'provider_id'
    ];

Socialiteの設定

インストール

composer require laravel/socialite

app.phpにproviderとaliaseを追加する。

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

    'aliases' => Facade::defaultAliases()->merge([
        'Socialite' => Laravel\Socialite\Facades\Socialite::class, // 追加
    ])->toArray(),

services.phpに追加。

config/services.php
    'google' => [
        'client_id'     => env('GOOGLE_CLIENT_ID'),
        'client_secret' => env('GOOGLE_CLIENT_SECRET'),
        'redirect'      => env('GOOGLE_CALLBACK'),
    ],

新しくコントローラーを作成する。

php artisan make:controller SocialController
app/Http/Controllers/SocialController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Socialite;
use Auth;
Use App\Models\User;

class SocialController extends Controller
{
    public function redirect($provider)
    {
     return Socialite::driver($provider)->redirect()->getTargetUrl();
    }

    public function Callback($provider){
        $userSocial  =   Socialite::driver($provider)->stateless()->user();
        $userAcount  =   User::where(['email' => $userSocial->getEmail()])->first();

        if($userAcount){
            $user = User::find($userAcount->getAttribute('id'));
            Auth::login($userAcount);
        }else{
            $user = User::create([
                'name'          => $userSocial->getName(),
                'email'         => $userSocial->getEmail(),
                'avatar'        => $userSocial->getAvatar(),
                'provider_id'   => $userSocial->getId(),
                'provider'      => $provider,
            ]);
        }

        return [
            'user'  => $user,
            'access_token' => $user->createToken('user')->plainTextToken,
        ];
    }
}

API側でリダイレクトさせると、CORS設定をしていてもエラーが出る。リダイレクトして認証サーバーを経由したときはCORSエラーが出てしまう。これの解決方法はないらしい。

そのため、一旦フロントでURLを受け取り、フロントのドメインからコールバック処理を行うようにする。
redirect関数ではredirect()->getTargetUrl() でURLのみをレスポンスに返す。

Callback関数では、ユーザー登録を行い、レスポンスでaccess_tokenを返す。

ルートの設定

普通にapi.phpに追加すると、Session store not set on request. のエラーが出る。
セッション許可を与えるためmiddlewareGroupsを編集。apiグループに追加すると、他のAPIにも影響してしまうため、sessionグループを新たに追加する。

app/Http/Kernel.php
    protected $middlewareGroups = [
        // 中略
        'session' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
        ],
    ];

routes/api.php
use App\Http\Controllers\Auth\SocialController;

Route::group(['middleware' => ['session']], function () {
    Route::get('login/{provider}', [SocialController::class,'redirect']);
    Route::get('login/{provider}/callback',[SocialController::class,'callback']);
});

CORSの設定

config/cors.php
'allowed_origins' => explode(',', env('ALLOWED_ORIGINS', '')),
.env
ALLOWED_ORIGINS=http://localhost:3000

Nuxt.jsの設定

axiosは入っている前提で省略。

CORSの設定

nuxt.config.js
  publicRuntimeConfig: {
    axios: {
      baseURL: process.env.API_URL,
      credentials: true // 追加
    },
  },

Googleログインボタンの実装

pages/login/googleLogin.vue
<template>
  <v-btn @click="login">Google login</v-btn>
</template>

<script>
  export default {
    methods: {
       async login(provider) {
        try {
          const response = await this.$axios.$get('login/' + provider)
          window.location.href = response
        } catch (err) {
          console.log(err)
        }
      },
    },
  }
</script>

ボタンを押すと、api/login/{provider}を呼び、リダイレクト先のURLをレスポンスから取得する。
URLはhttps://accounts.google.com/o/oauth2/auth/oauthchooseaccount?client_id=******&redirect_uri=******の形式。
レスポンスを取得できたら、そのままリダイレクトし、Googleログイン画面を表示させる。
アカウントを選択してログインが成功すると、「承認済みのリダイレクト URI」として登録したhttp://localhost:3000/login/googleCallbackに移動する。

callback先のページ実装

pages/login/googleCallback.vue
<template>
  <div>
    認証中
  </div>
</template>

<script>
  export default {
    async mounted () {
      try {
        const response = await this.$axios.$get('login/google/callback', { params: this.$route.query })
        this.$store.commit('setToken', {token: response.access_token})
        this.$store.commit('setUser', {user: response.user})
        this.$router.replace('/')
      } catch (error) {
        console.log(error)
      }
    }
  }
</script>

Googleログイン画面から戻ると、URLにパラメータが追加されているので、それをそのままAPIに渡す。
api/login/{provider}/callbackを呼び、ユーザー登録処理を行う。

これでusersテーブルにデータが登録される。
ログイン後の処理は、VuexにAccessTokenを保存する。Vuexについてはもとの参考記事が分かりやすくておすすめです。

参考記事

5
3
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
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?