DEV Community

Sathish
Sathish

Posted on • Edited on

User Authentication using Laravel's passport

First, let's answer the basic question - What is User Authentication?

User authentication is a process that allows an application to verify the identity of someone. Each user is required to log in to the system to access an application. The user supplies the username of an account and a password if the account has one (in a secure system, all accounts must either have passwords or be invalidated). If the password is correct, the user is logged in to that account; the user acquires the access rights and privileges of the account.

Now, What is Laravel Passport?

APIs typically use tokens to authenticate users and do not maintain session state between requests. Laravel makes API authentication a breeze using Laravel Passport, which provides a full OAuth2 server implementation for your Laravel application in a matter of minutes. Passport is built on top of the League OAuth2 server that is maintained by Alex Bilbie.

If a particular user is authenticated, the token that was generated during login will be stored to seamlessly provide API access to the user until the token is explicitly revoked during the logout.

We'll now create a public API endpoint Login and a protected API endpoint Logout for logging in and out users in a Laravel application.

What is a public API endpoint?

A public API endpoint is available for any users of the web. Take Login as an example. A login should be available for everyone in order to login into the application.

What is a protected API endpoint?

A protected API endpoint will only be available for the authenticated users. Take Logout as an example. An account can be logged out only by a legitimate user.

Let's set up the application.

Before installing Laravel, make sure that you have Apache up and running with MySql and PHP V7.2.

We'll need Composer to install Laravel in our system. Composer is a tool for dependency management in PHP. It allows you to declare the libraries your project depends on and it will manage (install/update) them for you.
Composer can be either globally installed or locally installed based on your requirement. We'll install it locally now.

Open a suitable directory and run the following command in your terminal -

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"

php -r "if (hash_file('SHA384', 'composer-setup.php') === '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"

php composer-setup.php

php -r "unlink('composer-setup.php');"

This will create a composer.phar file in the directory you've chosen. Now, let's install our Laravel Application using the following command-

php composer.phar create-project --prefer-dist laravel/laravel laravel-passport

After installation, create a database and let's name it as passport and open the application in your favorite editor. I prefer Code ❤️. Don't forget to install the composer again inside your project if you have installed it locally before.

Environent Configuration

It is often helpful to have different configuration values based on the environment where the application is running. For example, you may wish to use a different cache driver locally than you do on your production server.

To make this a cinch, Laravel utilizes the DotEnv PHP library by Vance Lucas. In a fresh Laravel installation, the root directory of your application will contain a .env.example file. If you install Laravel via Composer, this file will automatically be renamed to .env. Otherwise, you should rename the file manually.

Now, open the .env file and update the following -

APP_URL=http://localhost -> APP_URL=http://localhost/laravel-passport/public

DB_DATABASE=homestead -> DB_DATABASE=your database name here i.e passport

DB_USERNAME=homestead -> DB_USERNAME=your db username

DB_PASSWORD=secret -> DB_PASSWORD=your db password

Let's install Passport

To get started, install Passport via the Composer package manager:

php composer.phar require laravel/passport

After install successfully Passport package in our application we need to set their Service Provider. so, open your config/app.php file and add following provider in it.

Imgur

Now, the Passport service provider registers its own database migration directory with the framework, so you should migrate your database after registering the provider. The Passport migrations will create the tables your application needs to store clients and access tokens.

Database: Migrations

Migrations are like version control for your database, allowing your team to easily modify and share the application's database schema. Migrations are typically paired with Laravel's schema builder to easily build your application's database schema. Laravel comes with a default users table migration. So, we need not write any migration for this application since we'll be using only the email and password for authentication.

To migrate the users and the other passport tables, run the following artisan command:

php artisan migrate

Imgur

Next, you should run the passport:install command. This command will create the encryption keys needed to generate secure access tokens. In addition, the command will create "personal access" and "password grant" clients which will be used to generate access tokens:

php artisan passport:install

After running this command, add the Laravel\Passport\HasApiTokens trait to your App\User(Location - app\User.php) model. This trait will provide a few helper methods to your model which allow you to inspect the authenticated user's token and scopes:

Imgur

Next, you should call the Passport::routes method within the boot method of your AuthServiceProvider(Location - app\Providers\AuthServiceProvider.php). This method will register the routes necessary to issue access tokens and revoke access tokens, clients, and personal access tokens:

Imgur

Finally, in your config/auth.php configuration file, you should set the driver option of the api authentication guard to passport. This will instruct your application to use Passport's TokenGuard when authenticating incoming API requests:

Imgur

Now, let's write a controller for login and logout.

Run php artisan make:controller AuthenticationController. This will create a AuthenticationController.php file in app\Http\Controllers

Login

The basic logic behind login will be to find and retrieve the record with the help of the email value that comes with the request. After retrieval, if the password that came in the request matches the password of the retrieved record - we will generate a token and send it as a response with the 200 status code. If the password mismatch then we'll send the appropriate error message with 422 status code.

If there is no user found with the request email, the same procedure is followed like the password mismatch scenario.

Imgur

Logout

The logic for logout is to retrieve the token from the request header. Then we will explicitly revoke the token.

Imgur

Our final controller will look like the following:

Imgur

Routes

All Laravel routes are defined in your route files, which are located in the routes directory. We'll use api.php to define our API routes. We'll define two routes, namely - login and logut. Remember, login is a public route and logout is a private route. The route file will now look like the following:

Imgur

Testing our API using Postman

Postman is a platform that supports and enhances API development.

Before testing, add a record to your users table to test our API. Also, make sure that the storage and bootstrap/cache directory of the application is writable.

Testing login - POST http://localhost/laravel-passport/public/api/login

Imgur

Testing logout - GET http://localhost/laravel-passport/public/api/logout

Now, copy the token and set it as header.

Imgur

Feel free to check out the final codebase

Top comments (18)

Collapse
 
kabircse profile image
Kabir Hossain

I am trying to implement laravel passport on laravel 6. But I am getting an error.
Login and registration are ok. But I am getting an error when trying to fetch(authentic) pages.

The error is here pastebin.com/1M4iC3u5

My api.php



Route::group(['prefix'=>'v1'],function(){
        Route::post('register', 'API\Auth\RegisterController@register');
        Route::post('login', 'API\Auth\RegisterController@login');
        Route::get('gas_stations', 'API\GasStationController@index');
        //it works
        //Route::get('brands', 'API\BrandController@index');


        Route::group(['middleware'=>['auth:api']],function() {
            //it is not working
            Route::get('brands', 'API\BrandController@index');
        });    
    });
Collapse
 
malwarebo profile image
Irfan

I don't know how you have set up the flow but here is something that should probably work.

Inside the BrandController add this to the constructor.

$this->middleware(['auth'']);

Next, the index function inside the controller should look like this:

public function index()
    {
        $brands = Brands::all();
        return view('brands.index');
    }

Then use the Route directly as:

Route:get(/brands, BrandController@index);
Collapse
 
kabircse profile image
Kabir Hossain

Thanks a lot

Collapse
 
olivedev profile image
olivedev

Laravel Passport is definitely the best option for api token authentication in Laravel based apps. It automatically generates api token in Laravel apps. This make it easy to configure and efficient to use in your apps.

Collapse
 
sathish profile image
Sathish

That's why I wrote the tutorial. 😉

Collapse
 
hariwac profile image
Harinarayanan T

Hi,

Suppose I want to validate this condition while login a user using API

$aCredential = ['username' => $userName, 'password' => $password, 'user_type' => 1, 'status' => 1];

in this case do we need to separately validate each data rather than

if(Auth::attemp(aCredential))

In laravel 5.5, if you use passport attempt() will trigger error - function does not exits. So what will be the solution?

Collapse
 
jacobhenning profile image
Jacob Henning

Awesome article! I found it super helpful.

I did have a question for you though. In your logout function, you go through the following steps:

1) Get token from the header
2) Parse token to get the id
3) Retrieve token from user tokens using said id
4) Take this retrieved token, and revoke it.

I wrote my logout function differently as follows

$request->user()->token()->revoke();

1) Grab the user from the request
2) Grab the current toke from user
3) Revoke the token taken from the user

I chose not to use the id at all. My question is should I be using the id? Is there ever a time where the token I'm logging out is not going to be the token assigned to the current user?

Collapse
 
lexiebkm profile image
Alexander B.K.

Your authentication controller looks more readable/clearer than others I find in other articles. But maybe because I don't have prior knowledge of OAuth. Therefore I should first read the fundamental concepts explained in oauth2.thephpleague.com/ and even in tools.ietf.org/html/rfc6749, before I can proceed to code using Laravel Passport. Now, I know why Laravel documentation suggest that I should get familiar with OAuth before continuing. :)
Without good knowledge in OAuth, I feel I will only be able to copy paste code from others.

Collapse
 
lexiebkm profile image
Alexander B.K.

I can see you use Password Grant in this example from your AuthenticationController. But when I compare with what I read in Laravel documentation : laravel.com/docs/7.x/passport#requ..., I wonder how you get to use your code for issuing access token with Password Grant flow.
I know the User model uses HasApiTokens trait that has createToken method. But there is no clue, either in that HasApiTokens trait or in your code which specifies sufficient parameters usually required for Password Grant, i.e client_id and client_secret.
Maybe I missed something, but where in your code those parameters for Password grant are supplied for requesting access token ?

As as comparison, this is code snippet I find in other article :

public function login(Request $request) {
    $input = $this->validate($request, [
        'email' => 'required|email|exists:users,email',
        'password' => 'required|min:6',
    ], [
        'email.exists' => 'The user credentials were incorrect.',
    ]);

    request()->request->add([
        'grant_type' => 'password',
        'client_id' => env('PASSWORD_CLIENT_ID'),
        'client_secret' => env('PASSWORD_CLIENT_SECRET'),
        'username' => $input['email'],
        'password' => $input['password'],
    ]);

    $response = Route::dispatch(Request::create('/oauth/token', 'POST'));
    $data = json_decode($response->getContent(), true);
    if (!$response->isOk()) {
        return response()->json($data, 401);
    }
    return $data;
}

We see it also includes /oauth/token route for requesting access token as always mentioned in Laravel documentation.

Collapse
 
chrismuga profile image
ChrisMuga

Thank you, This helped me so much!

Collapse
 
sathish profile image
Sathish

Happy about that.

Collapse
 
pixelspy profile image
pixelSpy

hey thks for your great tutorial!
how do you then link this to your front end in vue.js for example?
i'm quite new to APIs
cheers

Collapse
 
sathish profile image
Sathish

No linking required. Call the appropriate API calls.

Collapse
 
webfacer profile image
Ilic Davor

if someone has issue to velidate password, try this:
try replacing the if statment with password by
'if(Hash::check($request->password, $user->password)) {//...}'

Collapse
 
hari03 profile image
Hariharan

How would you approach if you have the get the user details from a lumen endpoint?