Laravel SocialiteをベースにしてLaravelにLINE loginを実装するための手順

Laravelにはオフィシャルパッケージとして Socialite というのが用意されています。これを使うと Facebook, Google, linkedIn, GitHub, Bitbucket といったサービスでの OAuth 認証を簡単に実装してくれます。プロバイダーとして使えるサービスはこれ以外にもたくさんあって、Socialite Providers にいくと有名どころのサービスのプロバイダーがもれなく用意されています。残念ながら LINE の OAuth 認証がこのリストになかったので、自分で用意することになりました。ということで Laravel に LINE login を実装する手順をまとめてみました。ここでは Laravel 側の手順を説明するため、LINE 側でのビジネスアカウントの作成、チャンネルの作成、LINE Login の有効化までは終わっているものとします。

Laravel プロジェクトの作成

Laravel がないと始まらないので、global にインストールした laravel を

laravel new example

で new します。

Laravel Socialite のインストールと LINE 用アダプターの追加

composer require laravel/socialite

で Socialite を追加します。composer require が終わったら、SOcialiteにLINE用のアダプターを追加します。まずは vendor/laravel/socialite/src/SocialiteManager.php に以下のプライベートメソッドを実装します:

<?php
/**
 * Create an instance of the specified driver.
 *
 * @return \Laravel\Socialite\Two\AbstractProvider
 */
protected function createLineDriver()
{
    $config = $this->app['config']['services.line'];

    return $this->buildProvider(
        'Laravel\Socialite\Two\LineProvider', $config
    );
}

さらに vendor/laravel/socialite/src/Two/LineProvider.php を以下の内容で作成します:

<?php

namespace Laravel\Socialite\Two;

use Exception;
use Illuminate\Support\Arr;

class LineProvider extends AbstractProvider implements ProviderInterface
{
    /**
     * The scopes being requested.
     *
     * @var array
     */
    protected $scopes = [];

    /**
     * {@inheritdoc}
     */
    protected function getAuthUrl($state)
    {
        return $this->buildAuthUrlFromBase('https://access.line.me/dialog/oauth/weblogin', $state);
    }

    /**
     * {@inheritdoc}
     */
    protected function getTokenFields($code)
    {
        return [
            'grant_type' => 'authorization_code',
            'client_id' => $this->clientId,
            'client_secret' => $this->clientSecret,
            'code' => $code,
            'redirect_uri' => $this->redirectUrl,
        ];
    }

    /**
     * {@inheritdoc}
     */
    protected function getTokenUrl()
    {
        return 'https://api.line.me/v1/oauth/accessToken';
    }

    /**
     * {@inheritdoc}
     */
    protected function getUserByToken($token)
    {
        $userUrl = 'https://api.line.me/v1/profile';

        $response = $this->getHttpClient()->get(
            $userUrl, $this->getRequestOptions($token)
        );

        $user = json_decode($response->getBody(), true);

        return $user;
    }

    /**
     * {@inheritdoc}
     */
    protected function mapUserToObject(array $user)
    {
        return (new User)->setRaw($user)->map([
            'id' => $user['mid'],
            'name' => $user['displayName'],
            'nickname' => $user['displayName'],
            'email' => null,
            'avatar' => $user['pictureUrl'],
            'avatar_original' => $user['pictureUrl'],
        ]);
    }

    /**
     * Get the default options for an HTTP request.
     *
     * @return array
     */
    protected function getRequestOptions($token)
    {
        return [
            'headers' => [
                'Authorization' => 'Bearer '.$token,
            ],
        ];
    }
}

Socialite側の修正は以上です。

Laravel Socialite を Laravel 側で有効化させる

次は Socialite の Laravel 側の設定です。config/app.phpSocialiteServiceProvider を登録します:

<?php
// ...

'providers' => [
    // Other service providers...

    Laravel\Socialite\SocialiteServiceProvider::class,
],

// ...

同じファイルに定義されているエイリアス配列に Socialite ファサードを追加します:

<?php

// ...

'aliases' => [
    // Other aliases...

    'Socialite' => Laravel\Socialite\Facades\Socialite::class,
],

// ...

.env に LINE Login の Channel ID, Channel Secret, Callback URL を追加します:

LINE_CHANNEL_ID=[Channel ID]
LINE_CHANNEL_SECRET=[Channel Secret]
LINE_CALLBACK_URL=[Callback URL]

[Channel ID], [Channel Secret], [Callback URL] には取得したものを入力してください。

config/services.php にクレデンシャルを追加します:

<?php

// ...

'line' => [
    'client_id' => env('LINE_CHANNEL_ID'),
    'client_secret' => env('LINE_CHANNEL_SECRET'),
    'redirect' => env('LINE_CALLBACK_URL'),
],

// ...

以上で Socialite での LINE ログイン実装の準備が終わりました。

デフォルトのモデルの修正

最低限の実装をするために、デフォルトで用意されている app/User.phpUser モデルを修正します:

<?php

// ...

protected $fillable = [
    'name', 'mid', 'avatar',
];

// ...

protected $hidden = [
    'mid', 'remember_token',
];

// ...

マイグレーションもあらかじめ用意されている database/migrations/2014_10_12_000000_create_users_table.php を修正します:

<?php

// ...

public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->string('mid')->unique();
        $table->string('avatar');
        $table->rememberToken();
        $table->timestamps();
    });
}

// ...

モデルとマイグレーションの修正が終わったら、php artisan migrate でデータベースを用意してmigrateします。これでデータベース内にしかるべきテーブルが作成されます。

コントローラの追加

モデルの次はコントローラにルーターを追加します。OAuth provider へリダイレクトするためのルーターと、認証された後にコールバックを受け取るルーターの二つが必要となります。app/Http/Controllers/AuthController.php に以下の内容を記述します:

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Socialite;

class AuthController extends Controller
{
    /**
     * Redirect the user to the LINE authentication page.
     *
     * @return Response
     */
    public function redirectToProvider()
    {
        return Socialite::driver('line')->redirect();
    }

    /**
     * Obtain the user information from LINE.
     *
     * @return Response
     */
    public function handleProviderCallback()
    {
        try {
            $user = Socialite::driver('line')->user();
        } catch (Exception $e) {
            return redirect()->intended('/');
        }

        $authUser = $this->findOrCreateUser($user);
        Auth::login($authUser, true);

        return redirect()->intended('dashboard');
    }

    /**
     * Logout
     *
     * @return Response
     */
    public function logout()
    {
        Auth::logout();
        return redirect()->intended('/');
    }

    /**
     * Return user if exists; create and return if doesn't
     *
     * @param object $user
     * @return User
     */
    private function findOrCreateUser($user)
    {
        if ($authUser = \App\User::where('mid', $user->id)->first()) {
            return $authUser;
        }

        return \App\User::create([
            'mid' => $user->id,
            'name' => $user->name,
            'avatar' => $user->avatar
        ]);
    }
}

ログアウト用の処理も追加してあります。上記のログイン処理、ログイン後のコールバック処理に加えて、ログイン後のダッシュボード用のコントローラも用意します:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class DashboardController extends Controller
{

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return view('dashboard');
    }
}

コンストラクタで auth ミドルウェアを呼び出すことで、ログインしていないユーザが dashboard にアクセスできないようにしています。

ルーターの追加

routes/web.php にルーターを追加します:

<?php

// ...

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

Route::get('dashboard', 'DashboardController@index');

Route::get('login', 'Auth\AuthController@redirectToProvider')->name('login');
Route::get('callback', 'Auth\AuthController@handleProviderCallback');
Route::post('logout', 'Auth\AuthController@logout')->name('logout');

// ...

ビューの作成

最後にビューを作成します。とりあえず最低限ということで、まずはログインボタンを設置するトップページとして、デフォルトの welcome.blade.php を触ります:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>LINE Login</title>
        <link href="https://fonts.googleapis.com/css?family=Raleway:100,600" rel="stylesheet" type="text/css">
        <style>
            html, body {
                background-color: #fff;
                color: #636b6f;
                font-family: 'Raleway', sans-serif;
                font-weight: 100;
                height: 100vh;
                margin: 0;
            }
            .full-height {
                height: 100vh;
            }
            .flex-center {
                align-items: center;
                display: flex;
                justify-content: center;
            }
            .position-ref {
                position: relative;
            }
            .top-right {
                position: absolute;
                right: 10px;
                top: 18px;
            }
            .links > a {
                color: #636b6f;
                padding: 0 25px;
                font-size: 12px;
                font-weight: 600;
                letter-spacing: .1rem;
                text-decoration: none;
                text-transform: uppercase;
            }
        </style>
    </head>
    <body>
        <div class="flex-center position-ref full-height">
            @if (Route::has('login'))
                <div class="top-right links">
                    @if (Auth::check())
                        <a href="{{ url('/dashboard') }}">Dashboard</a>
                    @endif
                </div>
            @endif
            <div class="title m-b-md">
                <a href="{{ url('/login') }}"><img src="images/btn_login_base.png"></a>
            </div>
        </div>
    </body>
</html>

LINE のログインボタン用の画像は LINE の開発者ページからダウンロードしてきて、public/images ディレクトリを作成して画像ファイルを入れておいてください。

もう一つ必要なのが、ログイン後のダッシュボード画面です。これも最低限ということでこんな感じです:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <title>{{ config('app.name', 'Laravel') }}</title>
    <link href="/css/app.css" rel="stylesheet">
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-default navbar-static-top">
            <div class="container">
                <div class="navbar-header">
                    <a class="navbar-brand" href="{{ url('/') }}">
                        {{ config('app.name', 'Laravel') }}
                    </a>
                </div>
                <div class="nav navbar-nav navbar-right">
                    <form style="padding:7px 0" action="{{ url('/logout') }}" method="POST">
                        {{ csrf_field() }}
                        <button class="btn btn-link" type=submit>Logout</button>
                    </form>
                </div>
            </div>
        </nav>
        <div class="container">
            <div class="row">
                <div class="col-md-8 col-md-offset-2">
                    <div class="panel panel-default">
                        <div class="panel-heading">Dashboard</div>

                        <div class="panel-body">
                            Logged in as {{ Auth::user()->name }}!
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

ログインしてみる

php artisan serve

でローカルサーバを立ち上げて、ブラウザで http://localhost:8000 へアクセスしたら、LINE login ボタンをクリックしてください。LINE のログイン画面が現れるので、メールアドレスとパスワードを入力して LOGIN をクリックすると http://localhost:8000/dashboard へ飛んでダッシュボードが表示されると思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です