Ketan Patel

Lumen REST API Authentication



In previous article we have seen how we can create the REST API's in Lumen. In this tutorial we will learn how to use the default authentication in Lumen API's.

Lumen does not provide any support for session state. So for the requests that we want to authenticate must be authenticated via mechanism which will be a stateless like API Tokens.

Basic flow:
  • We will have a field called "api_key" in users table.
  • In response of successful login, we will return the API_KEY also. Now this key needs to be passed with all the upcoming requests which requires authentication.
  • When any api request is made we will check in the header that is there any api_key value is passed for Authorization or not.
  • If the value for Authorization is empty or provided value is not correct then it will return the response as "Unauthorised."
  • When API_KEY is passed, we will check into the users table that is there any user in the users table having the passed key for field "api_key"
  • If record found then the authentication is passed and we can get the full user object also. If no such record found then it will return the response as "Unauthorised."

Sample  Code of Login Function:

/**
* Check user credentials
*
* @return \Illuminate\Http\Response
*/
public function login(Request $request)
{
  $this->validate($request, [
  'email'    => 'required',
  'password' => 'required'
 ]);

  $user = User::where('email', $request->input('email'))->first();

  if(Hash::check($request->input('password'), $user->password)){
   // you can use any logic to create the apikey. You can use md5 with other hash function, random shuffled string characters also
   $apikey = base64_encode(str_random(32));
   User::where('email', $request->input('email'))->update(['api_key' => $apikey]);

   return response()->json(['status' => 'success','api_key' => $apikey]);
  }else{

      return response()->json(['status' => 'fail'],401);
  }
}


Above code of login is just for understanding, it can be varied based on the application requirements. Our main focus is we should return the "api_key" value on successful response so it can be used to pass with next requests.

Consider the same previous article as an example and continue to add an Authentication in Lumen API's.

Authentication is already given in Lumen, we just need to enable it and add few lines of code to authenticate the user and then mentioning which routes we need to protect. In this tutorial we will add the protection for our ProductsController actions routes: index, storeProduct, getProduct, updateProduct and deleteProduct

Open app.php file inside bootstrap folder and uncomment some lines. First is our authentication middleware and then we need to register the auth service provider. Remove comments from below lines:

//$app->routeMiddleware([
//     'auth' => App\Http\Middleware\Authenticate::class,
// ]);

and

//$app->register(App\Providers\AuthServiceProvider::class);


Uncommenting these two statements are enough for using authentication in our application. Now open App\Http\Middleware\Authenticate.php  file which is our authentication middleware.
Inside of this class you can see that there is a method called "handle". This method will execute before our protected methods.

/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @param  string|null  $guard
 * @return mixed
 */
public function handle($request, Closure $next, $guard = null)
{
 if ($this->auth->guard($guard)->guest()) {
  return response('Unauthorized.', 401);
 }

 return $next($request);
}

So any time that we make a request for our index, storeProduct, getProduct, updateProduct and deleteProduct methods, the authentication middleware is going to be used, and this handle method is going to be called.

If the user is not authenticated, then it will return a 401. Otherwise, it will pass the request on to the next thing that will process that request.

So that is one thing that we needed to look at. The other is inside of the Providers folder, and it's AuthServiceProvider.php.

/**
 * Boot the authentication services for the application.
 *
 * @return void
 */
public function boot()
{
 // Here you may define how you wish users to be authenticated for your Lumen
 // application. The callback which receives the incoming request instance
 // should return either a User instance or null. You're free to obtain
 // the User instance via an API token or any other method necessary.
 $this->app['auth']->viaRequest('api', function ($request) {
  if ($request->input('api_token')) {
   return User::where('api_token', $request->input('api_token'))->first();
  }
 });
}

Now at the bottom of this file is a method called boot, and inside of boot is a call to this viaRequest method. And this is the method that is responsible for actually authenticating the user. So this is going to be dependent upon our implementation. And in this lesson, our implementation is going to be very simple.

We will check in header for the value set for Authentication and we check this value against the users table field "api_key", if it matches with any record then its authenticated and if not matched or value not provided then it means user is not authenticated.

As mentioned above that this function is based on our logic so we can apply it as per the application requirements. Here, I have added the general(basic) check. Instead of input we have used header check.

/**
 * Boot the authentication services for the application.
 *
 * @return void
 */
public function boot()
{
 // Here you may define how you wish users to be authenticated for your Lumen
 // application. The callback which receives the incoming request instance
 // should return either a User instance or null. You're free to obtain
 // the User instance via an API token or any other method necessary.
 $this->app['auth']->viaRequest('api', function ($request) {
  if ($request->header('Authorization')) {
   return User::where('api_key', $request->header('Authorization'))->first();
  }
 });
}

Now, the only other thing that we need to do is say where we want to use our authentication middleware. We can do that in a variety of places. We can define with routes or inside the constructor of the controller.

Add the below line in the group of product routes which will check the authentication for all the grouped actions.

'middleware' => 'auth'
// all the routes related to Product module 
$router->group(['middleware' => 'auth', 'prefix' => 'api/v1/product/'], function($router)
{
 $router->get('all','ProductController@index');
 $router->get('{id}','ProductController@getProduct');     
 $router->post('create','ProductController@storeProduct');
 $router->put('update/{id}','ProductController@updateProduct');
 $router->delete('delete/{id}','ProductController@deleteProduct');    
});

If you want to bypass the authentication for any specific action then you can define all the routes as separately and then define the auth middleware for the required action.

$router->get('api/v1/product/all','ProductController@index');
$router->get('api/v1/product/{id}','ProductController@getProduct');     
$router->post('api/v1/product/create',['middleware' => 'auth','ProductController@storeProduct']);
$router->put('api/v1/product/update/{id}','ProductController@updateProduct');
$router->delete('api/v1/product/delete/{id}','ProductController@deleteProduct');

or you can define inside constructor of controller as well:

public function __construct()
{
   $this->middleware('auth:api');
}

If you want to bypass the authentication for any specific action then you can define the action name like below:

public function __construct()
{
   $this->middleware('auth:api', ['except' => ['storeProduct']]);
}

So the authentication will not be checked for the storeProduct action. The same way you can define your other actions based on your application type, logic and requirements.

Now try to access the storeProduct API without passing the Authentication header value , it will throw and error of "Unauthorised." , If you pass the correct API_KEY for the Authentication header then only it will return the success response.

That's it!


If you have any issues/doubts in any lines, logic or the implementation then please feel free the mail me on: ketan32.patel@gmail.com

ketan patel

About Ketan Patel -

I have developed a wide range of websites using CorePHP, Opencart, CakePHP and CodeIgniter including sites for startup companies and small businesses. Apart from my blogging life, I like to read Novels, Listening music and Net surfing.

Subscribe to this Blog via Email :