Laravel sanctum multi authentication

Mosab Ibrahim • August 13, 2020

I guess you noticed that there is no way that you can implement multi authentication with sanctum using auth guards, but I kida figured out some easy and simple way to implement that.

Ok let's get started

After installing and setup sanctum, first you need to set up your auth model, suppose you have these two models (Customer, Driver)

Customer model

class Customer extends Authenticatable
{
    use HasApiTokens;
}

Driver Model

class Driver extends Authenticatable
{
    use HasApiTokens;
}

Then you need to make auth controller for each of them (you can do that in one controller if you want) .

php artisan make:controller API/Auth/CustomerAuthController
php artisan make:controller API/Auth/DriverAuthController

Customer auth controller

The only new thing that you need to add is the ability when you create new token on the createToken method you pass the ability on the second parameter, I call it role:customer

public function login(Request $request)
{
        $request->validate([
            'email' => 'required|email',
            'password' => 'required',
        ]);

        $customer = Customer::where('email', $request->email)->first();

        if (!$customer || !Hash::check($request->password, $customer->password)) {
            throw ValidationException::withMessages([
                'email' => ['The provided credentials are incorrect.'],
            ]);
        }

        return response()->json([
            'customer' => $customer,
            'token' => $customer->createToken('mobile', ['role:customer'])->plainTextToken
        ]);

}

Driver auth controller

Also, here you should add ability when creating the token I call it role:driver .

public function login(Request $request)
{
        $request->validate([
            'email' => 'required|email',
            'password' => 'required',
        ]);

        $driver = Driver::where('email', $request->email)->first();

        if (!$driver || !Hash::check($request->password, $driver->password)) {
            throw ValidationException::withMessages([
                'email' => ['The provided credentials are incorrect.'],
            ]);
        }

        return response()->json([
            'driver' => $driver,
            'token' => $driver->createToken('mobile', ['role:driver'])->plainTextToken
        ]);
}

Setting middleware

After setting driver and customer login methods, you need to create middleware for each of them to prevent each of them from accessing other routes.

php artisan make:middleware CustomerMiddleware

php artisan make:middleware DriverMiddleware

Then on the customer middleware you need to check if has the role:customer ability or not, if it's not you will return not authorized and the same thing for driver middleware.

Customer Middleware

class CustomerMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (auth()->user()->tokenCan('role:customer')) {
            return $next($request);
        }

        return response()->json('Not Authorized', 401);

    }
}

Driver Middleware

class DriverMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (auth()->user()->tokenCan('role:driver')) {
            return $next($request);
        }

        return response()->json('Not Authorized', 401);

    }
}

then you need to add these middlewares to your $routeMiddleware array at the end of Kernal.php file.

    'type.customer' => CustomerMiddleware::class,
    'type.driver' => DriverMiddleware::class,

At last you need to add these middleware to you route on api.php file.

For example

// Only for customers
Route::middleware(['auth:sanctum', 'type.customer'])->group(function () {
    Route::get('/customers/orders', 'API\Customers\OrderController@index');
});

// Only for drivers
Route::middleware(['auth:sanctum', 'type.driver'])->group(function () {
    Route::get('/drivers/orders', 'API\Drivers\OrderController@index');
});

That's all see ya next time :)