Setting the correct auth guard for your requests

James Bannister • February 9, 2019 • 3 minute read

api laravel

When 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 way
11 $user = $request->user(); // Returns currently authenticated user
12}

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 way
11 $user = $request->user(); // Returns null
12}

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 way
11 $user = $request->user('api'); // Returns currently authenticated user
12}

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 user
14}

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.

  1. Create the middleware php artisan make:middleware UseApiGuard
  2. 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 $request
14 * @param \Closure $next
15 * @return mixed
16 */
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}
  1. 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 user
14}

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!