Laravel: Simple cookie auth for your simple APIs

Laravel uses token auth by default, here’s how to switch it out for simple cookie-based auth for quick and easy API tinkering for logged-in users.

Note: I’m not a general PHP security expert. There may be good reasons not to do this aside from the fact that it’s not stateless (please comment if so!) and definitely use it over HTTPS if you can!!

I’ve been through this several times and I forget about it each time.  So it’s worth writing down because it doesn’t seem to be well documented or explained even on Laracasts or Stack Overflow.

The issue

Let’s say you’re in the early stages of creating a simple app and you want to allow some actions to be taken using AJAX so you want to write some simple API routes that will return JSON.

Laravel has an api.php routes file which automatically applies some useful middlewares to requests, such as request throttling, and returns responses in JSON with appropriate HTTP headers.  But it also uses per-user token auth by default using a token guard. A part of the framework that doesn’t even seem to be documented.  The best I could find was a few tutorials, and the alternative seems to be to implement oAuth2 using Passport, which seems like overkill for smaller, simpler apps.

So perhaps this method really is frowned upon because it’s not stateless and violates REST principles. In any case, I’m pragmatic and I want to get started with my requests. So I want to switch this out for session based auth.

The config in question

Note: this is Laravel 5.5 – things may have changed.

So you put your route into routes/api.php and you may note that that has the auth:api middleware applied.  Though I’m not quite sure (and am really struggling to find out) how this happens and exactly what gets applied.

It also has a bunch of other middleware applied that we can control, as we’ll see later.

Let’s deal with the latter first.  The “other middleware” used are specified in app/Http/Kernel.php

You’ll see in there the $middlewareGroups property which looks like this:

protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        // \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    'api' => [
        'throttle:60,1',
        'bindings',
    ],
]

And you’ll hopefully note that there’s a web and an api group in here. The api group includes the throttling and something called bindings that remains mysterious to me.

Now, I THINK that the auth:api middleware that you see mentioned in routes/api.php refers to the guards listed in config/auth.php. These look like:

'guards' => [
    'web'=>[
        'driver'=>'session',
        'provider'=>'users',
    ],
'api'=>[
     'driver'=>'token',
     'provider'=>'users',
    ],
],

Note that the driver for the api guard here is token.  Now I think this refers to a per-user-token style of authentication (a bit like the method described here), but this token-driver-auth is not, as far as I can tell, document ANYWHERE, even though it seems to be the default for routes in routes/api.php.

So that what we seem to be dealing with. BUT…something either is not right with my understanding of the auth:api middleware or it doesn’t work, or something else is going on that I don’t know about (and that no one seems to be able to tell me about).

The solution

Given what I found about the config, you’d think that the solution is to set the api driver in config/auth.php to be session, but this doesn’t work.

One reason this doesn’t work is that the other lot of middleware defined in Kernel.php doesn’t include the middleware needed to start sessions and use cookies.  To do that you need to add the right middleware in Kernel.php

'api' => [
    \App\Http\Middleware\EncryptCookies::class,
    \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
    \Illuminate\Session\Middleware\StartSession::class, 
    'throttle:60,1',
    'bindings',
],

But this STILL doesn’t work.

And in the absence of any further information about how the auth:api middleware works, I can only get this working by changing auth:api to just auth in routes/api.php.

So…steps to get this working

  1. Add the cookies and session middlewares to the api group in Kernel.php
  2. Change api:auth to just api in routes/api.php
  3. Add your routes and controllers and away you go!
  4. Oh, and make sure you’re using HTTPS. You don’t want your cookies being intercepted!

Finally…

If you know how the auth:api middleware works, or if you can explain why any of what I’ve suggested is a bad idea, please leave comments.