Lumen REST API Authentication
Lumen is a lightweight PHP framework that can be used to build RESTful APIs with authentication. Lumen supports various authentication mechanisms, including API keys, JWT tokens, and OAuth 2.0, which can be easily integrated into your API endpoints using middleware.
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 fieldapi_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."
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.