Laravel 5.3 Passport OAuth 2 Setup with Login using Email, Username or Phone

Laravel Passport makes it really easy to setup a OAuth 2 server for a application. If you are using Vue.js, then it also ships with components that you can use it in your project right away, which you can drop into your project. Laravel Passport is built on top of the thephpleague OAuth 2 package. The League OAuth 2 package supports the standard Grant Types and defines Interface for repositories that let’s us decide how the data is stored. But if we were to use Laravel Passport, it implements the repository interfaces for us, and we don’t have to worry about implementing it.

Note: Familiarity of the OAuth 2 flow and the terminology is required. Read this article to familiarise yourself with the various OAuth 2 Grants and Terms.

Laravel Passport

There are a lot of things that Passport provides us under the hood, it defines the routes and the Controllers that define the actions for those routes covering the various OAuth endpoints. It implements the storage repositories for the OAuth 2 library.

Let’s install the Laravel Passport package and see an overview of how it does things under the hood.

Installing Passport

First let’s require the Laravel Passport package using composer,

composer require laravel/passport

Once installed, the first thing we need to do is add the Passport Service Provider into the providers array of the config/app.php.

Laravel\Passport\PassportServiceProvider::class,

The reason to do this is because all of the Laravel Core components, Libraries and Packages are integrated and loaded using the Service Providers in Laravel. Service Provider and Container are the most important thing in the Laravel Framework through which it registers all of the Core components into the framework uses. To know more about the Laravel Service Provider read the official guide.

Let’s see what the Passport Service Provider is registering inside the Service Provider. Open up vendor/larave/passport/src/PassportServiceProvider.php file.

Our focus here is first on the register method. Inside this method we register various Implementation required for Laravel Passport and adding those Implementation into the laravel IoC container, which then can be Loaded any where just by Type hinting the Class or Interface.

Here Passport registers the various services related to AuthorizationServer, ResourceServer and the Guard.

/**
 * Register the service provider.
 *
 * @return void
 */
public function register()
{
    $this->registerAuthorizationServer();

    $this->registerResourceServer();

    $this->registerGuard();
}

AuthorizationServer Registration
/**
 * Register the authorization server.
 *
 * @return void
 */
protected function registerAuthorizationServer()
{
    $this->app->singleton(AuthorizationServer::class, function () {
        return tap($this->makeAuthorizationServer(), function ($server) {
            $server->enableGrantType(
                $this->makeAuthCodeGrant(), Passport::tokensExpireIn()
            );

            $server->enableGrantType(
                $this->makeRefreshTokenGrant(), Passport::tokensExpireIn()
            );

            $server->enableGrantType(
                $this->makePasswordGrant(), Passport::tokensExpireIn()
            );

            $server->enableGrantType(
                new PersonalAccessGrant, new DateInterval('P100Y')
            );

            $server->enableGrantType(
                new ClientCredentialsGrant, Passport::tokensExpireIn()
            );
        });
    });
}

Inside the registerAuthorizationServer, a singleton instance of the AuthorizationServer is created and registered in the Service Container. While creating the AuthorizationServer, all of the required Grant Types are registered as well, using other methods like makeAuthCodeGrant, makeRefreshTokenGrant, makePasswordGrant, PersonalAccessGrant and ClientCredentialGrant.

Note: When an instance is registered as a singleton, once the instance has been created for the first time, the same instance will be returned for the sub sequent request to that instance for a request cycle in the application. To know more about different ways to register your services read more on Service Container.

ResourceServer Registration

The ResourceServer is also registered as a singleton inside the Server Container.

/**
 * Register the resource server.
 *
 * @return void
 */
protected function registerResourceServer()
{
    $this->app->singleton(ResourceServer::class, function () {
        return new ResourceServer(
            $this->app->make(Bridge\AccessTokenRepository::class),
            'file://'.Passport::keyPath('oauth-public.key')
        );
    });
}

Finally, the Token Guard to prevent endpoint consumption without authentication is registered using the registerGuard.

Migrations

Next we need to run the migration to create the required database tables for using the OAuth service.

php artisan migrate

The migration files are registered in the boot method of the PassportServiceProvider using the registerMigrations method. After the migrations are run, the following tables are created.

  • oauth_auth_code - Used to store the Authorization Codes.
  • oauth_access_tokens - Stores all the issued Access Tokens.
  • oauth_refresh_tokens - Stores all the Refresh Tokens for the Issued Access Tokens
  • oauth_clients - Stores Clients.
  • oauth_personal_access_clients - Stores the Personal Access Clients for users to primary use for their own use, like testing.

Note: In addition to this, the users and password_resets tables are also created, which are default when creating auth using laravel.

Passport Install

Once the migrations have been run and the tables have been created successfully. Laravel Passport defined 3 Console Commands, located in the vendor/laravel/passport/src/Console/ directory.

  • ClientCommand - Used to Create OAuth Clients.
  • KeysCommand - Generates Encryption Keys.
  • InstallCommand - Runs the above Client and Keys Commands to create the default Clients and the Keys are generated.

php artisan passport:install

This command will create personal access and password grant clients also generates the encryption keys by using the Client and Keys Command. The created Clients can be seen in the oauth_clients table.

Once all the above is done Passport is all setup, now we need to modify our Laravel Application to use Passport.

Modifying User Model

Laravel Passport provides a HasApiTokens trait which provides various helper methods including methods that define eloquent relationships like clients and tokens which represents that a user can have many clients and tokens. This must be included in the User.php i.e the User model class of your application.

In addition to the methods provided by the trait, we should defined a method called findForPassport which Passport looks for in your User model when it needs to identify a user. It takes as a argument the identifier value the user has provided.

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use Notifiable, HasApiTokens;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'phone', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * Find the user identified by the given $identifier.
     *
     * @param $identifier email|phone
     * @return mixed
     */
    public function findForPassport($identifier) {
        return User::orWhere('email', $identifier)->orWhere('phone', $identifier)->first();
    }
}

Info: The findForPassport is used by the getUserEntityByUserCredentials method in the vendor/laravel/passport/src/Bridge/UserRepository.php Class. This methods checks if the User model defines the findForPassport method, if defined, it uses that method to get the user. If not, it uses the email columns to retrieve the user.

Registering the routes

Now that Passport is setup, migrations are created, User model has been configured, we need to define the routes for accessing the Passport endpoints.

Note: The Controllers for the routes are defined inside the laravel/passport/src/Http/Controllers/. Check out those controllers, it might give an idea of how each actions handle the requests.

The routes can be registered inside the app/Providers/AuthServiceProvider.php boot method. Inside the boot method call the Passport::routes() method to register the routes.

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Laravel\Passport\Passport;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        Passport::routes();
    }
}

After installing the package to view the list of routes provided by Passport run the route:list artisan command.

php artisan route:list

Finally, set passport as the guard in the config/auth.php file driver option for the api authentication guard as passport.

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

    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

Laravel Passport is setup and you can access endpoints now. To have a quick start of the application you can use the views and resources provided by passport, know more.

Password Grant Usage

Now that all the things have been setup and registered,

The default /user route in the routes/api.php is protected using the auth:api middleware. Try hitting the endpoint without the Authorization header which will respond with an Unauthenticated response.

We can request a new Access Token by calling the /oauth/token endpoint with the following input JSON,

{
    "username": "demo@demo.com",
    "password": "demo",
    "grant_type": "password",
    "client_id": "2",
    "client_secret": "nzy3Px88DopPmyVUVmpcjOoIYrpPq47hW8T2A6jm",
    "scope": ""
}

In the above JSON input, user can also input their registered phone number which will also be authenticated since we have provided the findForPassport method to get the user based on the identifier.

which will respond with the following JSON response from the OAuth server,

{
  "token_type": "Bearer",
  "expires_in": 3155673600,
  "access_token": "eyJ0eXAiOiJKV1QiLCJh......",
  "refresh_token": "R9BhZnpu1EO+ptQeJwU......"
}

Note: When calling some endpoints as an api, attach a Accepts: application/json header.

After this, we can use the access_token to request the /user and other endpoints in your application which is protected using the auth:api guard. If token is valid, the request passes to the Controller, else an Unauthenticated error is sent back.

By default there is a /user route defined, which you can try hitting using the access_token and passing it in the Authorization header with type Bearer.