Jan 22, 2016 | laravel, laravel 5.2, authentication

Multiple authentication guard drivers (including API) in Laravel 5.2

Series

This is a series of posts on New Features in Laravel 5.2.

!
Warning: This post is over a year old. I don't always update old posts with new information, so some of this information may be out of date.

Let's get back to Laravel 5.2 features, shall we? 5.2 introduced a significant boost to the power of the entire authentication system, including making it much simpler to have multiple "guards" running at once.

Why should you care?

The default authentication guard in Laravel prior to 5.2 (now named the web guard) is your traditional web-based application authentication layer: username and password post to a controller, which checks the credentials and redirects if they are invalid; if valid, the user information gets saved to the session. Not all of those pieces are absolutely necessary but that's the general mindset.

But what if you want to have an API running in the same app, and it uses JSON web tokens (or some other stateless, non-session authentication mechanism)? In the past you'd have to jump through a lot of hoops to have multiple authentication drivers running at the same time.

Laravel 5.2's default auth guards

In 5.2, not only is it simple to have multiple auth drivers running, it actually already works that way out of the box.

If you check config/auth.php, you'll see two guards set out of the box: web, which is the classic Laravel authentication layer, and api, which is a stateless (no session memory) token-based driver.

Both, as you can see, connect to the same "provider".

Auth providers are also customizable. They're the definition of how the system should store and retrieve information about your users. Each is defined by an instance of Illuminate\Contracts\Auth\UserProvider.

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

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
    ],

If you look up higher in config/auth.php, you can see that the default Auth guard will be "web". That means any time you use Auth functions, middleware, or façades inside your application, they will default to the web guard unless you explicitly specify otherwise.

Introducing the token auth driver

So, if web uses the classic session driver, what's this new token driver we're seeing powering the api guard?

Jacob Bennett has written a fantastic post on that already: API Token Authentication in Laravel 5.2.

Check out his post to learn more about how it works, but here's the short of it:

  • Add an api_token column to your users table. 60-character string, unique.
  • Instead of using the auth middleware in your route definition, use the auth:api middleware.
  • In your API routes, use Auth::guard('api')->user() to get your user instead of Auth::user().

As you can see, we need to store an api_token for each user, and every incoming request that's guarded by the token-driven api guard will require a query parameter named api_token with a valid API token set to authenticate that user. And since it's stateless, every request will need to have this API token set; one successful request won't affect the next request.

If you're not familiar with token-based authentication, the consuming application (e.g. an iOS application) will have gotten, and saved, the token for the authenticating user prior to this request, so it will be creating its API calls using that known token as a part of the URL. For example, an iOS app might want to get a list of its user's friends; when the user first authenticated the application with your web site/API the app received a token and stored it. Now, it will generate requests using URLs like this: http://yourapp.com/api/friends?api_token=STORED_TOKEN_HERE

Using non-default drivers

As you can see in the token example above, there are two primary places we're going to be using drivers other than the default: in the auth guard middleware, and when we're using convenience features like Auth::check() and Auth::user() in our code.

You can choose which guard you're using to protect your routes by adding a colon and the guard name after auth in the middleware key (e.g. Route::get('whatever', ['middleware' => 'auth:api'])).

You can choose which guard you're calling manually in your code by making guard('guardname') the first call of a fluent chain every time you use the Auth façade (e.g. Auth::guard('api')->check()).

Creating your own guards and drivers

Creating your own guard is simple, beause each guard is just a key (web, api) that points to a specific configuration of a driver (session, token) and a provider (users). They're configured, as mentioned above, in config/auth.php:

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

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
        'matts-fancy-api-guard' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
    ],

But as you can tell, that doesn't really do much unless you're changing the driver or the provider.

Creating your own driver is not quite as simple as creating your own guard. The docs have a spot about Creating your own auth driver, and you're essentially going to be creating your own implementation of Illuminate\Contracts\Auth\Guard and then registering it as a driver in a service provider somewhere.

The docs also cover how to create your own user provider.

Concludinal

That's it. Enjoy.


Comments? I'm @stauffermatt on Twitter


Tags: laravel  •  laravel 5.2  •  authentication


This is part of a series of posts on New Features in Laravel 5.2:

  1. Dec 17, 2015 | laravel, laravel 5.2
  2. Dec 18, 2015 | laravel, laravel 5.2
  3. Dec 19, 2015 | laravel, laravel 5.2, API
  4. Dec 22, 2015 | laravel, laravel 5.2, middleware
  5. Jan 8, 2016 | laravel, laravel 5.2
  6. Jan 22, 2016 | laravel, laravel 5.2, authentication

Subscribe

For quick links to fresh content, and for more thoughts that don't make it to the blog.