Setting the correct auth guard for your requests
James Bannister • February 9, 2019 • 3 minute read
api laravelWhen I'm writing controllers, I like to minimise the number of dependencies I need to use.
This means reusing dependencies I'm already pulling in where I can, and for the most part the $request
object contains a lot of what you will need.
A few months back I used the Laravel Analyzer shift, which combines analysis from over 11,000 projects, along with a collection of practices widely adopted by the Laravel Community.
One of the suggestions that was made was to use the $request
object to retrieve the currently authenticated user, rather than relying on auth()->user()
or Auth::user()
which involves resolving another dependency to retrieve something we already have access to.
I'd never really considered this before, but felt it was a decent suggestion and decided to implement it across my project.
Now, my project has both a web interface as well as an API. This means I have routes both in my web.php
file and api.php
file - each of which uses a different guard.
By default, web routes are configured to use the web
guard and API routes are configured to use the api
guard, and unless otherwise specified, Laravel will use the web
guard by default.
This is specified in your config/auth.php
file and you are free to change this as needed.
When using the $request
object to resolve a user through $request->user()
, as no guard has been passed into user()
, Laravel will use the default web
guard.
This works perfectly fine for any of your web routes, as this is what is being used by default:
1// Controller method for a web route 2 3public function store(Request $request) 4{ 5 // Your store logic... 6 7 // Old way I was using 8 $user = Auth::user(); // Returns currently authenticated user 9 10 // New way11 $user = $request->user(); // Returns currently authenticated user12}
However, when I was refactoring my API controllers, this approach wasn't working so well out of the box:
1// Controller method for an API route 2 3public function store(Request $request) 4{ 5 // Your store logic... 6 7 // Old way I was using 8 $user = Auth::guard('api')->user(); // Returns currently authenticated user 9 10 // New way11 $user = $request->user(); // Returns null12}
This is because my API routes used the api
guard to authenticate, and as I hadn't specified otherwise, $request->user()
was trying to resolve the user using the web
guard.
The fix is fairly simple, just specify the guard you need to use like so:
1// Controller method for an API route 2 3public function store(Request $request) 4{ 5 // Your store logic... 6 7 // Old way I was using 8 $user = Auth::guard('api')->user(); // Returns currently authenticated user 9 10 // New way11 $user = $request->user('api'); // Returns currently authenticated user12}
However, this is now the second time you've had to specify that you want to authenticate and retrieve the user using the api
guard, because if you are using the auth
middleware on your API routes then you may have a controller that looks a little like thi:
1// API controller 2public function __construct() 3{ 4 $this->middleware('auth:api'); 5} 6 7// ... 8 9public function store(Request $request)10{11 // Your store logic...12 13 $user = $request->user('api'); // Returns currently authenticated user14}
Wouldn't it be better if we didn't have to repeat this? Turns out there is and we can setup our API routes so that Laravel knows to use the api
guard by default instead.
This can be done by overriding the default driver for authentication.
The best way to do this, in my opinion, is to create a middleware that we can apply to our API routes.
- Create the middleware
php artisan make:middleware UseApiGuard
- Update the
handle
method to override the guard:
1<?php 2 3namespace App\Http\Middleware; 4 5use Closure; 6use Illuminate\Support\Facades\Auth; 7 8class UseApiGuard 9{10 /**11 * Handle an incoming request.12 *13 * @param \Illuminate\Http\Request $request14 * @param \Closure $next15 * @return mixed16 */17 public function handle($request, Closure $next)18 {19 Auth::shouldUse('api');20 21 // Or you can use auth()->setDefaultDriver('api')22 23 return $next($request);24 }25}
- Add the middleware to your
app/Http/Kernel.php
file
1protected $middlewareGroups = [ 2 'api' => [ 3 // Other middleware you may be applying... 4 'useapiguard', 5 ], 6]; 7 8protected $routeMiddleware = [ 9 // Other middleware...10 'useapiguard' => \App\Http\Middleware\UseApiGuard::class,11];
With this done, requests through your API routes will now be using the api
guard by default.
This means we can clean our API controllers up slightly:
1// API controller 2public function __construct() 3{ 4 $this->middleware('auth'); 5} 6 7// ... 8 9public function store(Request $request)10{11 // Your store logic...12 13 $user = $request->user(); // Returns currently authenticated user14}
As you can see, we no longer need to worry about specifying the guard we need to use and instead it is done for you and $request->user()
will now correctly return the user.
This has been my first post on my new site. If you have any feedback, thoughts, or different approaches to this then I'd love to hear from you; I'm @jryd_13 on Twitter.
Thanks!