Laravel API — Authenticate user with custom driver & different table using auth middleware

Syed Sirajul Islam Anik
5 min readSep 26, 2017

--

Image from: http://www.cloudways.com/blog/wp-content/uploads/Laravel-Login-Authentication-Banner.jpg

Laravel is a good web framework. (I am not sure. People say so. It’s the only framework I know and I do for living). It comes with almost everything you need as well as simple to extend the functionality you need.

Laravel Middleware — Web & API

If we just have a look at Laravel’s App\Providers\RouteServiceProvider class, we will see Laravel binds web middleware to web routes to routes/web.php & api middleware to routes/api.php. Web middleware provides session & cookie — Check THIS. Where API middleware provides throttling — Check THIS.

For both the web & api, Laravel provides an Authentication Middleware. If you say a group of routes should be available only for guest users & another group for logged in users then your can define routes like below.

<?php
// routes/web.php
Route::group(['middleware' => 'auth'], function(){
// AUTHENTICATED USER
// ENLIST routes here
});
Route::group(['middleware' => 'guest'], function(){
// GUEST/UNAUTHORIZED USERS
// ENLIST routes here
});

This auth is a middleware. Laravel uses some guards to validate or invalidate the user. By default, Laravel ships with two guards. web & api guards. If you use auth middleware & you don’t define what guards should be used, Laravel by default takes the web guard.

How to define the GUARD?

To define a guard, you have to use : sign with auth middleware. Like auth:web or auth:api.

<?php
// routes/api.php
Route::group(['middleware' => 'auth:api'], function(){
// AUTHENTICATED users here,
// define routes here
});

I mentioned before that if you don’t define any guard, web is the default guard. So, auth and auth:web are same. For the web guard, Laravel uses session, cookie. For api guard, Laravel tries to look for api_token inside the header, form body, query string. If it fails to find the api_token then the user is not authenticated. If it finds the api_token then it tries to match the api token with database. If it can find any match then the user is authenticated otherwise he is NOT.

So, What’s the problem here?

Okay. Let’s check how Laravel stores the access token. By default, to use api guard with auth middleware, you will have to save the user’s access_token inside the users tables under a column name api_token. Something like,

public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
// the following column not available in my schema
$table->string('api_token'); // <===
$table->timestamps();
});
}

But, I want to save multiple access_tokens. Which is not possible in this database schema. To achieve this, we have to create another table. May be named as tokens. Something like this.

public function up () {
Schema::create('tokens', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id');
$table->string('access_token');
$table->string('refresh_token');
$table->dateTime('expires_in');
$table->timestamps();
});
}

So, whenever a user is registered or logged in, issue an access_token and save it to tokens table with the user_id. User can create as many as they want. Can login from multiple devices.

But, if we try to follow this, we can’t use Laravel’s auth:api middleware. It will throw an exception that the user is not logged in. Because for our schema, we don’t have the api_token field in our users table. So, how can we use the auth:api or something like this so that we can use the Auth facade from anywhere.

To overcome this issue, we have to define our own guard, so that we can pass it to auth middleware.

Define a GUARD

Laravel’s Documentation says, to add a custom guard you’ve to use Auth::extend method which will return an implementation of Illuminate\Contracts\Auth\Guard & can be placed in any ServiceProvider registered. As Laravel ships with AuthenticationServiceProvider it’s good to use this within that class.

The access_token will be the driver for the guard we are going to define next. It can be anything. You can name it anything. As of the documentation, AccessTokenGuard is the implementation of Guard interface.

Within the AccessTokenGuard class we need to implement the unimplemented methods of Guard interface. Laravel’s default TokenGuard & SessionGuard classes uses GuardHelpers trait. So, we can implement that. And the GuardHelpers doesn’t ship two methods user and validate. So, we need to declare those methods inside our Guard class.

As we implemented the GuardHelpers, if we look into it we will find that it requires an implementation of Illuminate\Contracts\Auth\UserProvider interface. So we need to create a new class to work with it. So the class is below.

What does the UserProvider contract do? It’s discussed in Laravel’s documentation.

The retrieveById function typically receives a key representing the user, such as an auto-incrementing ID from a MySQL database. The Authenticatable implementation matching the ID should be retrieved and returned by the method.

The retrieveByToken function retrieves a user by their unique $identifier and "remember me" $token, stored in a field remember_token. As with the previous method, the Authenticatableimplementation should be returned.

The updateRememberToken method updates the $user field remember_token with the new $token. The new token can be either a fresh token, assigned on a successful "remember me" login attempt, or when the user is logging out.

The retrieveByCredentials method receives the array of credentials passed to the Auth::attemptmethod when attempting to sign into an application. The method should then "query" the underlying persistent storage for the user matching those credentials. Typically, this method will run a query with a "where" condition on $credentials['username']. The method should then return an implementation of Authenticatable. This method should not attempt to do any password validation or authentication.

The validateCredentials method should compare the given $user with the $credentials to authenticate the user. For example, this method should probably use Hash::check to compare the value of $user->getAuthPassword() to the value of $credentials['password']. This method should return true or false indicating on whether the password is valid.

After implementing these things, we are almost done. Now we have to define this to our config/auth.php that you should use this guard to validate a user.

<?php
// config/auth.php
return [
// ...
'guards' => [
// ...
// ...
// this will be like auth:token instead of auth:api
// name the guard anything you want
'token' => [
// access_token is what we defined inside Auth::extend
// you can name this anything BUT should match with
// Auth::extend('HERE');
'driver' => 'access_token',
],
];

That’s it. Now you can define your routes like below and can use the Auth facade with the full functionality except the login & logout. Even the auth:api doesn’t provide login & logout. Routes can be like,

<?php
// routes/api.php
// token is the guard name, we defined above in config/auth.php
Route::group(['middleware' => 'auth:token'], function(){
// AUTHENTICATED users here,
// define routes here
});

That’s it. I have created a github repository for this. Please have a look as a whole. Laravel Custom Auth. There are some routes define. Postman collection added on public/postman-data with postman environment. Some test cases are defined too to check the accuracy with the auth:token and auth:api. You can clone it & play with it.

--

--

Syed Sirajul Islam Anik

software engineer with "Senior" tag | procrastinator | programmer | !polyglot | What else 🙄 — Open to Remote