LaravelアプリケーションにLINEログインを実装しよう!
前置き
はじめまして!
最近ようやく重い腰を上げてLaravelの勉強を始めた、エンジニアのかずみです。よろしくお願いします。
Laravelなかなか難しいですね…。とりあえずということで、ログイン認証機能を試して、色々作って試して壊して(?)…と進めていた中で、最近色々なサイトで使われている「ソーシャルログイン」を使った連携ログインを試してみたくなりました。
PHPのWebアプリケーションフレームワークであるLaravelには、『Laravel Socialite』というOAuth2認証によるソーシャルログインを行うためのオプションパッケージが用意されています。このパッケージを利用すると、TwitterやFacebookなど6種サービスによるソーシャルログイン機能が簡単に実装できます。また、『Socialite Providers』には多数のOAuth2サービスプロバイダに対応したアダプタが用意されており(名前も知らないようなサービスまでたくさん)、非常に便利なのですが…。
LINEが用意されていません…。
他にも、Yahoo! Japan ID(Y! Connect)とか、mixiとか、はてなとか、日本でよく使われるサービスは軒並みありません…。
…無かったら作ればいいじゃない!
ということで、『Laravel Socialite』を利用した、LINEなどのアダプタが公式に用意されていないサービスでのソーシャルログイン機能の実装について書きたいと思います。今回はLINEを例として扱いますが、Yahoo!Japanも同じ手順で実装できるはずです。
※LINEログインを利用するには登録が必要です。LINE developersのドキュメントに沿って登録しましょう。
目次
1. 概要
今回はLaravel標準のフォーム認証機能を元に、LINEログインによる認証ができるようにします。基本的にはhttps://www.webprofessional.jp/easily-add-social-logins-to-your-app-with-socialite/を参考に進めますが、
- LINEログインによる新規登録は不可
- 登録済みのアカウントでログイン後に、LINEログインを用いて連携することで、次回からLINEログインによるサイトへのログインが可能になる
という作りにするため、若干変更します。(さっさとLINEログインの実装見せろ!という方は5.へどうぞ)
2. インストールと設定
Laravel Socialiteのインストールをします。Composerコマンドでインストールするだけです。
1 |
$ composer require laravel/socialite |
Laravel5.5ではパッケージディスカバリーの自動化機能が追加され、app.phpに下記を追加する必要がなくなりました(5.4以前の場合は追加が必要です)
1 2 3 4 5 6 7 8 |
providers' => [ //中略 Laravel\Socialite\SocialiteServiceProvider::class, ], 'aliases' => [ //中略 'Socialite' => Laravel\Socialite\Facades\Socialite::class, ], |
3. テーブルとモデルの準備
LINEログイン連携用のテーブルを作成します。せっかくなので、Laravelの機能を使ってテーブルを作成しましょう。まずはartisanを利用して、モデルとマイグレーションファイルを生成して…
1 |
$ php artisan make:model LinkedSocialAccount --migration |
作成された database/migrations/yyyy_mm_dd_hhiiss_create_linked_social_accounts_table.php を下記のように編集します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateLinkedSocialAccountsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('linked_social_accounts', function (Blueprint $table) { $table->increments('id'); $table->bigInteger('user_id'); $table->string('provider_name'); $table->string('provider_id'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('linked_social_accounts'); } } |
provider_nameカラムには、連携したサービスの名称を格納します。LINEログインしか利用しないのであれば不要ですが、それ以外のサービス利用も想定して作成しています。
編集が終わったら、マイグレーションを実行してテーブルを作成しましょう!
1 |
$ php artisan migrate |
続いて、モデルを編集します。まずは標準のUserモデルの修正。LinkedSocialAccountモデルに対して1対多のリレーションを貼るために関数を追加します。
1 2 3 |
public function accounts(){ return $this->hasMany('App\LinkedSocialAccount'); } |
そして先ほど作成したLinkedSocialAccountモデルからは、逆向きのリレーションを貼ります。ついでに、複数代入を許可するためにfillable属性も追加してしまいましょう。
1 2 3 4 5 |
protected $fillable = ['provider_name', 'provider_id']; public function user() { return $this->belongsTo('App\User'); } |
4.コントローラの作成
まずは、コントローラ内部で利用するSocialAccountsServiceクラスを作成します。
ソーシャルログインにより提供されたアカウントが紐付いているユーザを返すfind関数と、提供されたアカウントを新規にログイン中のユーザに紐付けるlink関数を用意しています。ユーザが連携可能なアカウントは、ソーシャルログインサービスごとに1つだけとしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?php namespace App; use App\Eloquents\User; use App\Eloquents\LinkedSocialAccount; use Laravel\Socialite\Contracts\User as ProviderUser; class SocialAccountsService { public function find(ProviderUser $providerUser, $provider) { $account = LinkedSocialAccount::where('provider_name', $provider) ->where('provider_id', $providerUser->getId()) ->first(); return $account ? $account->user : null; } public function link(User $user, ProviderUser $providerUser, $provider) { if (LinkedSocialAccount::where('provider_name', $provider) ->where('user_id', $user->id)->exists()) { return null; } else { $user->accounts()->create([ 'provider_id' => $providerUser->getId(), 'provider_name' => $provider, ]); return $user; } } } |
続いて、app/Http/Controllers/Auth/以下にSocialAccountController.phpを作成します。
redirectToProvider関数は各サービスのログイン画面へ遷移するための関数です。引数providerの値はルーティングの際に設定されるパラメータで、各サービスの名称が入るようにしています。(詳しくは6.にて)
handleProviderCallback関数は、サービスへのログイン成功後のコールバック処理用の関数になります。今回のサンプルでは、LINEログインを利用したサイトへのログイン時と、ログイン済みアカウントとLINEログインの連携時、両方で共通として利用します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<?php namespace App\Http\Controllers\Auth; use App\SocialAccountsService; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class SocialAccountController extends Controller { public function redirectToProvider($provider) { return \Socialite::driver($provider)->redirect(); } public function handleProviderCallback(Request $request, SocialAccountsService $accountService, $provider) { try { $user = \Socialite::with($provider)->user(); } catch (\Exception $e) { return redirect('/login'); } $authUser = $accountService->find($user, $provider); if ($authUser) { // 連携アカウントが登録済み auth()->login($authUser, true); return redirect('/home'); } else { // まだ登録なし if ($request->user()) { // 既ログインの場合、連携アカウントとして登録し、アカウントに紐付ける if ($accountService->link($request->user(), $user, $provider)) { return redirect('/home'); } else { // 既に同じサービスのアカウントでの登録があり、紐付け失敗 return redirect('/home')->withErrors(['social' => '同一サービスの連携ログインが既に登録されています']); } } else { // ログインしていない場合 return redirect('/login'); } } } } |
5. LINE用プロバイダ・サービスプロバイダなどの作成
ようやく本編ですね…。
今回はLINEログイン用のファイルはまとめて、addon/Socialite/src/以下に置きたいと思います。ディレクトリ構造と作成するファイルは下記の通りです。
1 2 3 4 5 6 |
addon/ └ Socialite/ └ src/ └ Line/ ├ LineProvider.php └ LineServiceProvider.php |
では実装…の前に、LINEログイン用設定を追加しましょう。
.envファイルに、LINEログイン用のパラメータを追加します。コールバックURLは環境に応じて適宜読み替えてください。また、作成したLINEログイン用Channelのアプリ設定から、Callback URLの欄に登録することをお忘れなく。登録していないとログインに失敗するようです。
1 2 3 |
LINE_CLIENT_ID=(LINEログインのChannel ID) LINE_CLIENT_SECRET=(LINEログインのChannel Secret) LINE_REDIRECT=http://localhost/login/line/callback |
続いてconfig/services.phpがこの情報を返却するように登録します。
1 2 3 4 5 |
'line' => [ 'client_id' => env('LINE_CLIENT_ID'), 'client_secret' => env('LINE_CLIENT_SECRET'), 'redirect' => env('LINE_REDIRECT'), ], |
さて、実装です!まずはLINE用のプロバイダを作成します。
vendor/laravel/socialite/src/の中に、Socialiteがサポートするサービス対応のプロバイダクラスのソースが入っているので、それを参考にして実装していきましょう。今回はLINEログインv2を使って認証しますので、それに基いて処理を記述していきます。LINEログインv2のAPI仕様については、こちらで確認できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
<?php namespace Addon\Socialite\Line; use Laravel\Socialite\Two\AbstractProvider; use Laravel\Socialite\Two\ProviderInterface; use Laravel\Socialite\Two\User; class LineProvider extends AbstractProvider implements ProviderInterface { protected function getAuthUrl($state) { return $this->buildAuthUrlFromBase('https://access.line.me/dialog/oauth/weblogin', $state); } protected function getTokenUrl() { return 'https://api.line.me/v2/oauth/accessToken'; } public function getAccessTokenResponse($code) { $response = $this->getHttpClient()->post($this->getTokenUrl(), [ 'headers' => [ 'Content-Type' => 'application/x-www-form-urlencoded', ], 'form_params' => [ 'grant_type' => 'authorization_code', 'code' => $code, 'redirect_uri' => $this->redirectUrl, 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret ], ]); return json_decode($response->getBody(),true); } protected function getUserByToken($token) { $response = $this->getHttpClient()->get('https://api.line.me/v2/profile', [ 'headers' => [ 'Authorization' => 'Bearer ' . $token, ], ]); return json_decode($response->getBody(), true); } protected function mapUserToObject(array $user) { return (new User())->setRaw($user)->map([ 'id' => $user['userId'], 'name' => $user['displayName'], 'avatar_original' => $user['pictureUrl'], ]); } } |
続いて、そのプロバイダを読み込むためのサービスプロバイダを、SocialiteServiceProviderを継承して作成します。Qiitaの投稿『Laravel Socialite 独自ドライバの作り方』を参考にさせていただきました。(最初、register関数に書いて悪戦苦闘してました…)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php namespace Addon\Socialite\Line; use Laravel\Socialite\SocialiteServiceProvider; class LineServiceProvider extends SocialiteServiceProvider { /** * Bootstrap the service provider. * * @return void */ public function boot() { \Socialite::extend('line', function ($app) { $config = $this->app['config']['services.line']; return \Socialite::buildProvider(LineProvider::class, $config); }); } } |
config/app.phpに、このサービスプロバイダを読み込むための設定を追加します。(まとめてパッケージ化して、Socialiteのように自動化させてもいいのですが、今回はとりあえず手動で)
1 2 3 4 |
'providers' => [ //中略 Addon\Socialite\Line\LineServiceProvider::class ] |
あと、今回は一連のソースをAddon/以下のディレクトリに作ったので、オートローダ設定用にcomposer.jsonを更新しましょう。更新後に設定を反映させるために、composer dump-autoloadなりcomposer updateなりを叩くのをお忘れなく。
1 2 3 4 5 6 |
"autoload": { "psr-4": { "App\\": "app/", "Addon\\Socialite\\" : "addon\\Socialite\\src" } }, |
6.ルーティングの設定とビューの準備
あと一息です。今回追加したソーシャルログイン機能へのルーティングを追加しましょう。前述したとおり、URLからサービスをパラメータとして取得して関数の引数として渡しています。
1 2 |
Route::get('/login/{provider}', 'Auth\SocialAccountController@redirectToProvider'); Route::get('/login/{provider}/callback', 'Auth\SocialAccountController@handleProviderCallback'); |
あとはビューですね。標準ログイン画面の「Forgot Your Password?」の下にLINEログインボタンをさくっと追加します。
1 2 3 4 5 6 7 |
<div class="form-group"> <div class="col-md-8 col-md-offset-4"> <a class="btn btn-default" href="/login/line"> Line Login </a> </div> </div> |
同じく、ログイン後の画面にもボタンを追加します。ついでに、アラート表示部分(既にLINEアカウント連携をしていた場合のエラー)も追加しておきます。
1 2 3 4 5 6 7 8 9 10 11 |
@if ($errors->has('social')) <div class="alert alert-warning"> {{ $errors->first('social') }} </div> @endif <div class="col-md-8 col-md-offset-4"> <a class="btn btn-default" href="/login/line"> Add Line Login </a> </div> |
7. 完成!動作確認
これで完成です!動かしてみましょう。
まずはLaravel標準のフォーム認証機能と同じように、メールアドレスでアカウント登録⇒ ログインします。右上のRegisterからですね。『Line login』のボタンも表示されて押したくなりますが、先にアカウントを登録してLINEログインとの連携をしなければ使えませんので、はやる気持ちを抑えて新規登録しましょう。
ログインするとホーム画面に移動します。先ほど追加した『Add Line Login』ボタンが表示されていると思いますのでクリックしましょう。
すると、LINEログインのページへ遷移しますので、LINEアカウントを使ってログインします。(アプリへの初回ログイン時は、ログイン後に連携許可を促す画面が表示されると思いますので、許可します。)
正しくログインできれば以下のような画面に移動します。これでLINEログインにより得られたアカウント情報が、今ログインしているアカウントに連携されました!
…されましたが、このままでは本当にできているかわからないので、一旦ログアウトしてLINEログインを使ってログインできるか確認しましょう。(右上のアカウント名のところからログアウトできます)
最後に、トップ画面のLine Loginボタンを押して、LINEログインをすることでホーム画面に移動することが確認できたら完了です!
まとめ
思ったより簡単に実装できました。Laravel Socialiteさまさまです。
同じ手順で、APIの仕様どおりにプロバイダなどを作成すればYahoo! Japan IDでのログイン(Y! Connect)も実装できるはずです。
TwitterやFacebookの場合は標準でプロバイダが準備されており、サービスプロバイダもLaravel Socialiteの標準のものをそのまま利用するだけなので、もっと手軽に実装できます。最近はソーシャルログインを使ったサイトも多く、必須技術になってきていますので、こうやって簡単に利用できるのは嬉しいですね。
2017年1月にeF-4入社後は主にPHPによるシステム開発に従事中。
髪が長くて派手。爪も超長くて超派手。インターン生が入るたびに「その爪でキーボードを打てるんですか?」と聞かれるのが若干の悩みのタネ…(´・ω・`)
利用言語:C / PHP / Java / JavaScript / Perl
覚えたい:Python / Go / Lua