2
4

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 3 years have passed since last update.

Laravel 6.x SessionGuardをカスタムする(Cookie jar has not been set の対処法)

Posted at

#やりたいこと
Laravelでは「どのようにユーザを認証するか」ということをガード(Guard)という概念で実装している。つまり、認証に関してカスタムしたいことがあればガードを自作し、それに基づいて認証を行うように設定すれば良い。
Laravelにはデフォルトのガードが二つ存在し、一つはSessionGuardクラス、もう一つはTokenGuardクラスである。それぞれデフォルトの web 用のガード、api 用のガードとして実装されている。そこで、それらのサブクラスを実装してデフォルトのGuardと入れ替えればさくっと認証処理をカスタムできる。便利だ。

と思っていたら、意外なところでハマったので、この記事はそれに関するレポートである。より具体的には、

Cookie jar has not been set

というエラーが出た時の対処法である。

#結論
上記のエラーに当たったということは基本的なGuardのカスタムはできていて、いざ認証しようとした時(特にRememberMe機能を使用しようとした時)に上記のエラーに当たったものと思われるので、解決策を先に述べる。

おそらくapp/Providers/AuthServiceProvider.phpbootメソッドを用いて自作のガード(CustomGuardクラスとする。このクラスはSessionGuardクラスを拡張しているものとする。)を以下のように登録していると考えられる。

    public function boot()                                                                                                                                                         
    {                                                                                                                                                                              
        $this->registerPolicies();                                                                                                                                                 
                                                                                                                                                                                   
        // 自作ガードの登録
        Auth::extend('custom_guard', function($app, $name, array $config) {
            return new CustomGuard($name, Auth::createUserProvider($config['provider']), $app['session.store']);                                                                                                                                                     
        });                                                                                                                                                                        
    }                                                                                                                                                                              

そしてこのcustom_guardconfig/auth.phpでガードのドライバーとして指定しているはずである。世の中のカスタムガード作成の指南書にはほぼこれに準ずることが書かれている。

この状態で問題の Cookie jar has not been set エラーが出た時には、bootメソッド中の自作ガードの登録部分を以下のように変更する。

    public function boot()                                                                                                                                                         
    {                                                                                                                                                                              
        $this->registerPolicies();                                                                                                                                                 
                                                                                                                                                                                   
        // 自作ガードの登録
        Auth::extend('custom_guard', function($app, $name, array $config) {
            $guard = new CustomGuard($name, Auth::createUserProvider($config['provider']), $app['session.store'])

            if (method_exists($guard, 'setCookieJar')) {
                $guard->setCookieJar($app['cookie']);
            }

            if (method_exists($guard, 'setDispatcher')) {
                $guard->setDispatcher($app['events']);
            }

            if (method_exists($guard, 'setRequest')) {
                $guard->setRequest($app->refresh('request', $guard, 'setRequest'));
            }

            return $guard;
        });                                                                                                                                                                        
    }                                                                                                                                                                              

以上。

#やっていること
コードの中身を見てもらえれば一目瞭然であるが、変更前はただガードクラスを生成して返しているだけだが、変更後は生成したインスタンスのsetCookieJarsetDispatchersetRequestメソッドを呼び出してから返している。おそらく Cookie jar has not been set エラーはsetCookieJarを呼び出すことによって解決する。

さて、この解決策がどこから来たかという話であるが、それはデフォルトのSessionGuardを登録してるIlluminate\Auth\AuthManagerresolveメソッド及びそこから呼び出されるcreateSessionDriverメソッド、callCustomCreatorメソッドを見ていくと解決する。

まずresolveメソッドであるが、これはメソッド名の通りガード名(上記の例で言えばcustom_guard)からガードクラスのインスタンスを解決するためのメソッドである。その中身を見てみると、処理は

if (isset($this->customCreators[$config['driver']])) {
   return $this->callCustomCreator($name, $config);
} 

という条件分岐によって、自作されたガードであるか否かで二つに分かれる。自作のガードであればメンバ変数$customCreators配列にその名前が登録されているためcallCustomCreatorメソッドが呼ばれ、デフォルトのSessionGuardTokenGuardを使用している場合には、それぞれcreateSessionDriverメソッドやcreateTokenDriverメソッドが呼ばれることになる。

callCustomCreatorメソッドとこのcreateSessionDriverメソッドとを見比べてみると、前者はAuth::extendメソッドに渡したコールバックを呼び出す、つまり変更前のコードであれば単にカスタムガードクラスを生成して返すだけなのに対し、後者は

    public function createSessionDriver($name, $config)
    {
        $provider = $this->createUserProvider($config['provider'] ?? null);

        $guard = new SessionGuard($name, $provider, $this->app['session.store']);

        // When using the remember me functionality of the authentication services we
        // will need to be set the encryption instance of the guard, which allows
        // secure, encrypted cookie values to get generated for those cookies.
        if (method_exists($guard, 'setCookieJar')) {
            $guard->setCookieJar($this->app['cookie']);
        }

        if (method_exists($guard, 'setDispatcher')) {
            $guard->setDispatcher($this->app['events']);
        }

        if (method_exists($guard, 'setRequest')) {
            $guard->setRequest($this->app->refresh('request', $guard, 'setRequest'));
        }

        return $guard;
    }

のように記述されており、すなわち変更後のコードと同様生成したインスタンスに対して三つのメソッドを呼び出している。(というより、変更後のコードはAuthManagerのこのメソッドをコピーしただけである。)

三つのメソッドの中身は追いかけてはいないが、名前から察するに問題の Cookie jar has not been set エラーはsetCookieJarメソッドの呼び出しに起因することは明らかである。

#まとめ
SessionGuardのカスタム時に陥りやすいと思われるエラーの対処方法について解説した。おそらくTokenGuradのカスタムも同じようなことが言えると思うが、どのようなエラーが出るのかは不明、対処方法は同じ(createTokenDriverの処理を参考にすればよい。)と思われる。

#参考資料

2
4
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
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?