Add multi-authentication support to Laravel Passport
The instructions below are from the developing version (Branch master
). Please prefer stable versions! Stable docs HERE
Laravel Passport |
---|
^3.0 |
^4.0 |
^5.0 |
^6.0 |
- Install using composer:
$ composer require smartins/passport-multiauth
- If you are using a Laravel version less than 5.5 you need to add the provider on
config/app.php
:
'providers' => [
// ...
SMartins\PassportMultiauth\Providers\MultiauthServiceProvider::class,
],
- Migrate database to create
oauth_access_token_providers
table:
$ php artisan migrate
- Add new provider in
config/auth.php
using a model that extends ofAuthenticatable
class and useHasApiTokens
trait.
Example:
- Configure your model:
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Admin extends Authenticatable
{
use Notifiable, HasApiTokens;
}
- And your
config/auth.php
providers:
// ...
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
// ** New provider**
'admins' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
],
],
// ...
- Add a new
guard
inconfig/auth.php
guards array using driverpassport
and the provider added above:
// ...
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
// ** New guard **
'admin' => [
'driver' => 'passport',
'provider' => 'admins',
],
],
// ...
- Register the middleware
AddCustomProvider
to$routeMiddleware
attributes onapp/Http/Kernel.php
file.
class Kernel extends HttpKernel
{
// ...
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
// ...
'oauth.providers' => \SMartins\PassportMultiauth\Http\Middleware\AddCustomProvider::class,
];
// ...
}
- Replace the middleware
Authenticate
onapp/Http/Kernel
$routeMiddleware
attribute.
class Kernel extends HttpKernel
{
// ...
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
// 'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth' => \SMartins\PassportMultiauth\Http\Middleware\MultiAuthenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'oauth.providers' => \SMartins\PassportMultiauth\Http\Middleware\AddCustomProvider::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
// ...
}
- Encapsulate the passport routes for access token with the registered middleware in
AuthServiceProvider
. This middleware will add the capability toPassport
routeoauth/token
use the value ofprovider
param on request:
namespace App\Providers;
use Route;
use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
// ...
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes();
// Middleware `oauth.providers` middleware defined on $routeMiddleware above
Route::group(['middleware' => 'oauth.providers'], function () {
Passport::routes(function ($router) {
return $router->forAccessTokens();
});
});
}
// ...
}
Optional: Publish migrations:
Just run the vendor:publish
artisan command with package provider as parameter:
$ php artisan vendor:publish --provider="SMartins\PassportMultiauth\Providers\MultiauthServiceProvider"
- Add the
provider
parameter in your request at/oauth/token
:
POST /oauth/token HTTP/1.1
Host: localhost
Accept: application/json, text/plain, */*
Content-Type: application/json;charset=UTF-8
Cache-Control: no-cache
{
"username":"[email protected]",
"password":"password",
"grant_type" : "password",
"client_id": "client-id",
"client_secret" : "client-secret",
"provider" : "admins"
}
- You can pass your guards on
auth
middleware as you wish. Example:
Route::group(['middleware' => ['api', 'auth:admin']], function () {
Route::get('/admin', function ($request) {
// Get the logged admin instance
return $request->user(); // You can use too `$request->user('admin')` passing the guard.
});
});
The api
guard use is equals the example with admin
.
- You can pass many guards to
auth
middleware.
Route::group(['middleware' => ['api', 'auth:admin,api']], function () {
Route::get('/admin', function ($request) {
// The instance of user authenticated (Admin or User in this case) will be returned
return $request->user();
});
});
You can use too the Auth
facade:
Auth::check();
Auth::user();
- Add the
provider
parameter in your request at/oauth/token
:
POST /oauth/token HTTP/1.1
Host: localhost
Accept: application/json, text/plain, */*
Content-Type: application/json;charset=UTF-8
Cache-Control: no-cache
{
"grant_type" : "refresh_token",
"client_id": "client-id",
"client_secret" : "client-secret",
"refresh_token" : "refresh-token",
"provider" : "admins"
}
- Just use the
scope
andscopes
middlewares fromLaravel\Passport
.
protected $routeMiddleware = [
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
];
Instead to use the Laravel\Passport\Passport::actingAs()
method, use SMartins\PassportMultiauth\PassportMultiauth::actingAs()
.
The difference is that the actingAs
from this package get the guard based on Authenticatable
instance passed on first parameter and authenticate this user using your guard. On authenticated request (Using auth
middleware from package - SMartins\PassportMultiauth\Http\Middleware\MultiAuthenticate)
the guard is checked on Request
to return the user or throws a Unauthenticated
exception. E.g.:
use App\User;
use Tests\TestCase;
use SMartins\PassportMultiauth\PassportMultiauth;
class AuthTest extends TestCase
{
public function fooTest()
{
$user = factory(User::class)->create();
PassportMultiauth::actingAs($user);
$this->json('GET', 'api/user');
}
public function withScopesTest()
{
$user = factory(User::class)->create();
PassportMultiauth::actingAs($user, ['see-balance']);
$this->json('GET', 'api/balance');
}
}
Based on renanwilian responses to Passport Multi-Auth issue.