While this is good enough for most of the apps, sometimes we need to tweak it a little bit if there is a new need arises.
I created this middleware because I need a few user groups that would access the app and in every user group there are roles.
- In your terminal, run
composer require jsdecena/laravel-passport-multiauth
or add this in yourcomposer.json
"require": { ... "jsdecena/laravel-passport-multiauth": "^0.2", ... },
- Add this line in your
config/app.php
'providers' => [ ... Jsdecena\LPM\LaravelPassportMultiAuthServiceProvider::class, ... ]
- Add this in your
app\Http\Kernel.php
/** * The application's route middleware. * * These middleware may be assigned to groups or used individually. * * @var array */ protected $routeMiddleware = [ ... 'mmda' => \Jsdecena\LPM\Middleware\ProviderDetectorMiddleware::class, ];
- Also in your
routes/api.php
Route::post('oauth/token/', 'CustomerTokenAuthController@issueToken') ->middleware(['mmda', 'throttle']) ->name('issue.token');
Trivia: Why mmda? This is because in the Philippines, they are the one that handles the traffic 😅
- And in the
config/auth.php
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], 'customers' => [ 'driver' => 'passport', 'provider' => 'customers' ], ], 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => 'App\User', ], /** * This is the important part. You can create as many providers as you like but right now, * we just need the customer */ 'customers' => [ 'driver' => 'eloquent', 'model' => 'App\Customer', ], ],
In your controller, you can access the user logged in via
auth()->guard('customer')->user()
- Your
Customer
model should extend withAuthenticatable
and use theNotifiable
andHasApiTokens
traits
<?php namespace App; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Passport\HasApiTokens; class Customer extends Authenticatable { use Notifiable, HasApiTokens; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', ]; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = [ 'password', 'remember_token', ]; }
Note that you need the
Customer
model or any model that you need to authenticate with.
-
Migrate the customer table
php artisan vendor:publish --tag=migrations
-
And in your controller:
App\Http\Controllers\Auth\CustomerTokenAuthController.php
<?php namespace App\Http\Controllers\Auth; use App\Customers\Customer; use App\Customers\Exceptions\CustomerNotFoundException; use Illuminate\Database\ModelNotFoundException; use Laravel\Passport\Http\Controllers\AccessTokenController; use Laravel\Passport\TokenRepository; use League\OAuth2\Server\AuthorizationServer; use Psr\Http\Message\ServerRequestInterface; use Lcobucci\JWT\Parser as JwtParser; class CustomerTokenAuthController extends AccessTokenController { /** * The authorization server. * * @var \League\OAuth2\Server\AuthorizationServer */ protected $server; /** * The token repository instance. * * @var \Laravel\Passport\TokenRepository */ protected $tokens; /** * The JWT parser instance. * * @var \Lcobucci\JWT\Parser */ protected $jwt; /** * Create a new controller instance. * * @param \League\OAuth2\Server\AuthorizationServer $server * @param \Laravel\Passport\TokenRepository $tokens * @param \Lcobucci\JWT\Parser $jwt */ public function __construct(AuthorizationServer $server, TokenRepository $tokens, JwtParser $jwt) { parent::__construct($server, $tokens, $jwt); } /** * Override the default Laravel Passport token generation * * @param ServerRequestInterface $request * @return array * @throws UserNotFoundException */ public function issueToken(ServerRequestInterface $request) { $body = (parent::issueToken($request)); $token = json_decode($body, true); if (array_key_exists('error', $token)) { return response()->json([ 'error' => $token['error'], 'status_code' => 401 ], 401); } $data = $request->getParsedBody(); $email = $data['username']; switch ($data['provider']) { case 'customers'; try { $user = Customer::where('email', $email)->firstOrFail(); } catch (ModelNotFoundException $e) { return response()->json([ 'error' => $e->getMessage(), 'status_code' => 401 ], 401); } break; default : try { $user = User::where('email', $email)->firstOrFail(); } catch (ModelNotFoundException $e) { return response()->json([ 'error' => $e->getMessage(), 'status_code' => 401 ], 401); } } return compact('token', 'user'); } }
- The request to authenticate must have the
provider
key so the system will know which user is to authenticate with
eg.
POST /api/oauth/token HTTP/1.1 Host: localhost Content-Type: application/x-www-form-urlencoded Cache-Control: no-cache grant_type=password&username=test%40email.com&password=secret&provider=customers
If the provider parameter is not passed, it will default looking into the
users
table as usual.