目的
開発プロジェクトでSNSログインを行う場合、
- SSLが必要になる事
- ローカル専用ドメインでSSLがしたい
本番用のドメインでSSLを作成して、hosts等で本番ドメインをローカルホストにアクセスするように設定すれば良いが、それはそれで後々問題が起きそうなので、今回は
- ローカル専用ドメインを用意
- ローカル専用SSLを作成
- Docker上で使用する
この条件で、開発環境の構築からLaravelでSNSログインの実装までを説明する。
前提
下記の環境で実装した。
- ローカルはmacos
- Dockerコンテナはnginx+phpfpm
- DockerコンテナのnginxにローカルSSLを組み込む
- ローカル専用ドメインはsnslogin.dev.cdeとする
- コンテナはdocker-composeを使用する
- UIはとりあえずbootstrapで行う
ローカルホスト側の設定
ローカル専用SSLはmkcertを使用する。
よってmacosのターミナルで
% brew install mkcert # mkcertをhomebrewからインストール
% mkcert --install # ローカルSSL初期設定
% mkcert localhost dev.cde *.dev.cde # ローカルドメイン用のSSLを作成
% mkcert -cert-file ./snslogin_dev_cde.crt.pem \
-key-file ./snslogin_dev_cde.key.pem snslogin.dev.cde # key,certファイルの作成
以上で、SSLの必要なファイルを作成した。
作成したファイルはSslディレクトリにコピーしておく。
nginxコンテナ
nginxコンテナの作成は下記の通りである。
services:
nginx:
image: nginx
container_name: "snslogin-nginx"
ports:
- "80:80"
- "443:443"
volumes:
- ./svr:/svr
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./Ssl/snslogin_dev_cde.crt.pem:/etc/nginx/conf.d/snslogin_dev_cde.cert.pem
- ./Ssl/snslogin_dev_cde.key.pem:/etc/nginx/conf.d/snslogin_dev_cde.key.pem
- ./weblog:/var/log/nginx
depends_on:
- phpfpm
phpfpm:
build: ./phpfpm
次にnginxコンテナ上のdefault.confにてSSLの設定を行う。
server {
index index.php index.html;
root /svr/app/public;
listen 443 ssl;
server_name snslogin.dev.cde;
ssl_certificate /etc/nginx/conf.d/snslogin_dev_cde.cert.pem;
ssl_certificate_key /etc/nginx/conf.d/snslogin_dev_cde.key.pem;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
〜以下省略〜
Laravelの設定
ここで、一旦コンテナを起動して、Laravelの設定を行う。
phpfpmに接続して、コマンドラインから
% laravel new app
% composer require laravel/socialite
% composer require laravel/ui
% php artisan ui bootstrap
% php artisan ui bootstrap --auth
% npm install
% npm run dev
以上を行う。
Laravelのパッケージはlaravel/socialite、laravel/uiをインストールする。
UIはbootstrapを使用し、認証スカフォールドを生成する。
テーブル設定
laravel標準のusersを使用するが、SNSログインの場合、パスワードがNULLになるため、マイグレーションを変更しておく。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password')->nullable(); // ここ。
$table->rememberToken();
$table->timestamps();
});
}
〜以下省略〜
passwordをnullableしておく。
これが済めば、.envにDB接続情報を設定してから、マイグレーションを実行する。
% php artisan migrate
ルーティングとサービスの設定
まず、route/web.phpにルーティングの設定を行う。
Route::get('/login/{social}', 'Auth\LoginController@socialLogin')
->where('social', 'facebook|twitter');
Route::get('/login/{social}/callback', 'Auth\LoginController@handleProviderCallback')
->where('social', 'facebook|twitter');
SNSログインのURLとコールバックのURLを設定した。
次にサービスの設定をconfig/service.phpに記述する。
<?php
return [
/*
|--------------------------------------------------------------------------
| Third Party Services
|--------------------------------------------------------------------------
|
| This file is for storing the credentials for third party services such
| as Mailgun, Postmark, AWS and more. This file provides the de facto
| location for this type of information, allowing packages to have
| a conventional file to locate the various service credentials.
|
*/
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
],
〜中略〜
// 以下を追加
'facebook' => [
'client_id' => env('FACEBOOK_API_ID'),
'client_secret' => env('FACEBOOK_API_SECRET'),
'redirect' => env('FACEBOOK_CALLBACKURL'),
],
];
とりあえず、facebookの設定のみを行った。
実際の値は、.envにて設定する。
コントローラとViewの編集
コントローラーは認証スカフォールドで作成されたLoginControllerを使用する。
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
// ここから
use Socialite;
use App\User;
use Auth;
// ここまで追加
class LoginController extends Controller
{
〜中略〜
// SNSログインのメソッド
public function socialLogin($social)
{
return Socialite::driver($social)->redirect();
}
// SNSログインコールバックのメソッド
public function handleProviderCallback($social)
{
$userSocial = Socialite::driver($social)->stateless()->user();
$user = User::where(['email' => $userSocial->getEmail()])->first();
if ($user) {
Auth::login($user);
} else {
$newuser = new User;
$newuser->name = $userSocial->getName();
$newuser->email = $userSocial->getEmail();
$newuser->save();
Auth::login($newuser);
}
$uri = "/home";
if (session()->has("login_after_url")) { // ログイン後リダイレクト
$uri = session("login_after_url");
session()->forget("login_after_url");
}
return redirect($uri);
}
}
ここでは、SNSログインの開始とコールバックの処理を行う。
コールバック時にログインユーザがusersになければ、作成して、
Auth::login()にて強制ログインする。
SNSログイン開始前にセッション変数login_after_urlにログイン後のリダイレクト先URLを
設定しておくと、そのURLに遷移する。
Viewは同じく認証スカフォールドで作成されたresources/views/auth/login.blade.php
にSNSログインのリンクを記述する。
<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>Facebookログイン
</a>
</div>
</div>
以上で、ローカル開発環境にSNSログインの組み込みは完了である。
実際に実行する前にSNS側のアプリ設定とログインコールバックのURLを設定すればOKである。
考察
意外と簡単にできたのである。
laravel/socialiteで他に何ができるかは未調査であるが、少なくともSNSログインは簡単にできたのである。
むしろdocker-compse.ymlやローカルSSLの方が大変だった。
作成したプロジェクトはGitHubにアップしたので、必要であれば参照していただきたい。