Edited at

やはりお前らのMulti-Auth は間違っている

More than 1 year has passed since last update.

……と言いたかったけどどうやら5.3で少し変わってたからみたい。

こんにちは、Laravel初心者のHinaloeです。

ブログQiitaにPassportの記事を書いてたら、初心者なのに今月末にLaravel.osaka 2016ってイベントでPassportの話をすることになりました。よろしくお願いします。


Laravelユーザーの皆さん、Multi-Auth、使ってますよね?

Laravel 5.2 でやっとこさ標準で使えるようになったんです。使わない手は無いですよね。

意識していなくても使っているかもしれません。

5.2以降ではデフォルトでwebapiの2つのAuthentication Guardsが定義されてい、それぞれ目的に応じて使い分けているかと思いますがこれもMulti Authです。デフォルトで使えるドライバはsession,tokenですが、たとえばPassportもこれに追加されます。

で、

なんといってもMulti Authの醍醐味は、 これら複数の認証方法を横断して認可することが可能であることですよね。

たとえば以下のように web, admin, api の3つのguardsを用意した場合。


config/auth.php

    'guards' => [

'web' => [
'driver' => 'session',
'provider' => 'admin',
],
'admin' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admin' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
],
],


これらの内コントローラやルーティング、アクション毎に必要に応じてアクセス許可を設定することが簡単にできます。

たとえば



  • webのみ


  • webapi


  • webadmin


  • webadminapi

  • これらの何れも&非ログインも含む

  • いずれでもログインしていない (guest)

などなど。

そもそもweb ミドルウェアグループと api ミドルウェアグループでは大幅に当てているミドルウェアが異なるのでこれらをわざわざ跨がせはしないかもしれませんが、複数のアカウント種別で同一エンドポイントが利用できるというのは重要な事です。

(以降、上で提示した3つのguardを使って説明します)


複数のguardってどうやって受け入れるんだっけ?


単数のguardなら……

単数のguardを受け入れる場合は言うまでもないですよね。

RouterやControllerでmiddlewareに'auth:admin''auth:api'のような値を書いておけばそのルートは指定したメソッドでの認証が必要になり、認証が通らなければ401が投げられ(あるいはリダイレクトされたり)します。まあ当然ですよね。


じゃあuser+adminだと……

まず愚直に考えそうなのはミドルウェアを複数回定義することです。(自分はそうでした)

[

'middleware' => [
'auth:web',
'auth:admin',
]
]

なんだかいけそうな気がする〜〜〜〜〜けど案の定これは通りません。(それもそう)


ところでMulti Authについて書いている記事を読んでみると……

多くの記事ではAuth::guard('admin')->user()とかAuth::guard('admin')->check()のように アクション毎に書いているでは無いですか。それもよく読んでみると、Auth::user()じゃダメだからAuth::guard('admin')->user()使ってね、とまであるじゃないですか。

面倒すぎるだろそれ

どう考えてもAuth::user()が使えないというのは面倒。そもそも$request->user()だって使えないんでしょこれ。

とりあえずひとまず解決には至らなかったので自分は

        $user = Auth::guard('web')->user()??Auth::guard('admin')->user()??false;

if (!$user) {
throw new AuthenticationException();
}

のように書いて一時凌ぎをしていました。

が、これアクション毎に書いていられるわけ無いですよね?w


Laravel 5.3 ではこの心配は必要ないんです

実はLaravelの Authenticate ミドルウェア、5.2と5.3で配置場所がぜんぜん違うことに気づけば答えは出たも同然でした。

このミドルウェア、Laravel5.2ではApp\Http\Middlewareにあったものが5.3よりIlluminate\Auth\Middlewareとコアに移動し、中身が少し変わってます。

特に特徴的なのがhundleの引数。PHP5.6+でサポートされている可変長引数を利用して ミドルウェアに複数のguardを渡せるようになりました。

つまり、'auth:web,admin'のようにルーティングやコントローラで指定してやれば、web, admin 何れの方法でもアクセス可能になるってわけです。(複数の認証を満たせるリクエストならばここで一番前に書いたものが使用されるはず)

渡したguardsリストの内、リクエストに対して有効な物があればそれが$this->auth->shouldUse($guard);されるので各アクションでAuth::user()のようなメソッドをそのまま使用できます。

(どの方法を使用しているかdetectするにはAuth::guard($guard)->check()使うしか無いのかな……前述の 複数満たせる場合を考慮する必要がありそうだけど。)


実はこれ、Laravel 5.2 に適用できるんです

出来る出来るって言いながら検証したわけではないし妄想。

Laravel 5.2 にも Laravel5.3の上記同様のことが可能なメソッドは揃っているので、Authenticateミドルウェア(App\Http\Middleware\ Authenticate.php)の中身をLaravel 5.3のそれ(\Illuminate\Auth\Middleware\Authenticate`)で上書きすれば闇から解放される、はず。

なお要PHP5.6+


Multi-Auth、何気につい最近までなかったのが謎なくらい需要あるので活用していきましょう。

Laravel.osaka 2016 への参加もお待ちしています!

✧Laravelこわいみなさんはこのあたり使いこなしてると思うので🔪指摘大歓迎です✧