Implementing Laravel's built-in token authentication
Tags: laravel, api, authentication
Published: 2021-03-04 02:07:55

{% github grantholle/laravel-token-auth-example no-readme %}

One of the great things about Laravel is its mission to provide developers with the tools they need out of the box, as easily as possible.

More often than not when developing an application you're going to need some mechanism of authentication. Up until recently, Laravel shipped with a complete authentication toolbox: controllers, routes and views. Recently Laravel migrated a lot of its backend authentication functionality into [Laravel Fortify](https://laravel.com/docs/fortify) and provided a frontend simple implementation using [Breeze](https://laravel.com/docs/starter-kits). There's also a more opinionated auth setup using [JetStream](https://jetstream.laravel.com/) which combines Fortify and other currently-popular frontend tools [Livewire](https://laravel-livewire.com/) and (my personal favorite) [Inertiajs](https://inertiajs.com/).

But what about API's? Laravel provides two solutions, [Sanctum](https://laravel.com/docs/sanctum) and [Passport](https://laravel.com/docs/passport). Both of these are quite fully featured; they come with all the tools for users to generate tokens for themselves to interact with your application.

But recently I needed to implement very simple API authentication for interacting with an application _from a different_ application. Think "machine to machine" interaction. Passport ships with an entire OAuth implementation, which includes [client credentials grant tokens](https://laravel.com/docs/passport#client-credentials-grant-tokens). The gist is that you provide a token to authenticate without any of the traditional OAuth flow.

While Passport is a great tool, it includes way more than I needed. Lucky for you and me, Laravel already ships with a token-based authentication mechanism. Unfortunately it's actually quite undocumented.

# Begin!

I'm starting with a clean project using PHP 8 (version isn't important, just being thorough) for this tutorial, but I added it to my existing project using the same logic. What's really great is that there are **no packages** we need to install. Laravel ships with all the tools we need.

The important thing to note here is that for this example, I needed to perform some actions at the admin level, not anything based on a particular _user_ account. I actually don't care _who_ is doing this API action (i.e. what the value of `$request->user()` is), just that I needed to do something remotely that is triggered by a different system, sort of like a microservice.

## Create the sample project

Create the new [project](https://github.com/grantholle/laravel-token-auth-example).

```
laravel new token-auth-example
```

You'll need to set up your own database and know how to configure it to get it going. I'm not going to cover that here.

Since we don't care about users right now, we can ignore that entirely.

## Create the token table

Next, let's create a new migration for a table called `api_clients`.

```
 php artisan make:migration --create=api_clients create_api_clients_table
```

I'll modify it to have just a column, `api_token`. This is the column that Laravel will use to look up the token:

```php
Schema::create('api_clients', function (Blueprint $table) {
    $table->id();
    $table->string('api_token')->unique();
});
```

Now we'll run the migrations.

```
php artisan migrate
```

## Configure the api guard and provider

We need to make the necessary changes to `config/auth.php` to configure our token authentication. This was confusing for me for a while to understand what the different keys mean, but I'll try myself to explain it.

The guards are what protect parts of your application. Laravel allows you to create separate guards so theoretically you could have different user models that use different parts of your application. I've done this and it can get a little messy, but it's nice that Laravel is flexible enough to allow this. By default it has two generic guards, "web" and "api". The web guard is for authenticating into your web app via the browser and the api guard is for, you guessed it, api access. There are separate guards because a browser and api are two different mediums of interacting with your application. 

The default configuration is pretty close to what we need.

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

    'api' => [
        'driver' => 'token',
        'provider' => 'api_clients', // was users
        'hash' => true, // was false
    ],
],
```

We've modified the `provider` key to be `api_clients` (more on that next) and then set `hash` to be true. The `hash` option means that we're going to store the token hashed (i.e. not in plain text)

Next we'll need to configure its provider. The provider "provides" who the user is that is being authenticated. Your users (in our case clients) are stored in the database, and we need to tell Laravel _how_ to retrieve the user to verify they're valid.

The default `users` provider is that it's your `App\Models\User` Eloquent model. For our api client, we could use Eloquent too, but again, we _don't care_ who it is. We don't need Eloquent's bells and whistles. We just need a table, which we've already created.

```php
'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],

    'api_clients' => [ // <- Add the api_clients that was configured in our `api` guard
        'driver' => 'database', // We don't need eloquent
        'table' => 'api_clients', // Change to be our table name, which happens to be the same as our provider name
    ],
],
```

Believe it or not, that's all we need for Laravel to automatically do token authentication in our api routes. But... how do we create clients?

## Creating API clients

For our simple use-case, we don't need routes to manage tokens and all of that jazz. We need _one_ token to exist at a time. In times like this, I reach for an [Artisan command](https://laravel.com/docs/artisan).

```
php artisan make:command GenerateAuthToken
```

This will give us all the functionality we need: remove old tokens and create a new one, providing the new token to me to use. In `app/Console/Commands/GenerateAuthToken.php` we can add this in a few lines.

Change the signature and description to something more meaningful:

```php
/**
 * The name and signature of the console command.
 *
 * @var string
 */
protected $signature = 'make:token';

/**
 * The console command description.
 *
 * @var string
 */
protected $description = 'Generates a new api client auth token to consume our api';
```

Remove the code for the constructor because we don't need it. In `handle()` we need to create a token, remove old clients and create the new client with the new token.

```php
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

public function handle()
{
    // Generate a new long token
    // Be sure to import Illuminate\Support\Str at the top
    $token = Str::random(60);

    // Delete existing tokens, maybe we lost the token
    // so we don't want existing ones floating around somewhere
    DB::table('api_clients')->whereNotnull('api_token')->delete();
    
    // Create a new entry with the hashed token value
    // so we don't store the token in plain text
    DB::table('api_clients')->insert([
        'api_token' => hash('sha256', $token),
    ]);

    // Spit out the token so we can use it
    $this->info($token);

    return 0;
}
```

We can try it out and generate a token:

```
➜ php artisan make:token
YHEt7CNf1SBmQs6JbTPf7qMK8FgnynI5SiPmyJELrbAO61heKy0eKuiXrxBJ
```

If we check out the database, the value for `api_token` is hashed as `277b302457eeccc1607b024b16f6c50bfaf37d7db028f9bf1daf4add58282a57`. When we make a request, Laravel will hash our token to match it against what's in the database, rather than comparing the raw token. This also means that you better put that token somewhere safe, as that's the only time you'll be able to see it.

## Implement a route

Head on over to `routes/api.php` and change the route to something else:

```php
Route::middleware('auth:api')->get('/test', function (Request $request) {
    return 'Authenticated!';
});
```

For this simple example, we are using a Closure. You would probably use a real controller, such as a [single action controller](https://laravel.com/docs/controllers#single-action-controllers).

There are a few ways we can use our token to access this route. The main ways are either passing a variable in the query string of the request or using an `Authentication` header `Bearer` token. I'll just use the browser with a query string. I'm going to use Laravel's `php artisan serve` to test it out quickly.

I can now go to `http://localhost:8000/api/test?api_token=YHEt7CNf1SBmQs6JbTPf7qMK8FgnynI5SiPmyJELrbAO61heKy0eKuiXrxBJ` to see if it worked.

I do in fact see "Authenticated!". Awesome! I'm going to modify the token, or even remove it entirely, to see what happens.

You're going to see a "Route [login] not defined" error. This just means that we _weren't_ authenticated and it tried to redirect us to a login page, which doesn't exist.

## Conclusion

For my use case of performing an admin task in my application from a _different_ application (server to server/machine-to-machine), this token authentication is just what I needed. The best part was that I didn't need any additional package as Laravel already had the token driver I needed. An Artisan command gave me all the functionality I needed to manage clients and tokens. We're all set!