Laravel 5.3 Passport OAuth 2 Setup with Login using Email, Username or Phone
Reading Time:
Reading Time:
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.
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
.
LaravelPassportPassportServiceProvider::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();
}
/**
* 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.
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(BridgeAccessTokenRepository::class),
'file://'.Passport::keyPath('oauth-public.key')
);
});
}
Finally, the Token Guard
to prevent endpoint consumption without authentication is registered using the registerGuard
.
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 Tokensoauth_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.
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.
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 IlluminateNotificationsNotifiable;
use IlluminateFoundationAuthUser as Authenticatable;
use LaravelPassportHasApiTokens;
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.
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 AppProviders;
use IlluminateSupportFacadesGate;
use IlluminateFoundationSupportProvidersAuthServiceProvider as ServiceProvider;
use LaravelPassportPassport;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
'AppModel' => 'AppPoliciesModelPolicy',
];
/**
* 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.
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.