17

I'm configuring a Laravel project to use Passport token authentication. Everything seems to be working, but when the auth:api middleware fails, it responds to the client with a status of 200 and a bunch of HTML in the response body. Instead, I want it to respond with a status of 401.

I can't find anything in the Laravel Passport source or documentation about doing something like this. I can't even find the source for the middleware.

My test route:

Route::get('/protected', function () {
    return response()->json([
        'success' => true
    ]);
})->middleware('auth:api');

config/auth.php

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'passport',
        'provider' => 'appUsers',
    ],
],

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],

    'appUsers' => [
        'driver' => 'eloquent',
        'model' => App\Models\AppUser::class
    ],
],
SimpleJ
  • 13,812
  • 13
  • 53
  • 93

3 Answers3

31

You can solve it by sending this header with your request.

Accept : application/json

This will send this message with 401 status code.

{
    "message": "Unauthenticated."
}
Ariful Haque
  • 3,662
  • 5
  • 37
  • 59
  • I want to add custom code to this JSON response like({ "error" : 401 "message": "Unauthenticated." }). How Can I do that? – Sachin Kumar May 13 '18 at 10:02
  • I don't understand this answer, can you please complement it showing us how to send that header? – Gjaa Mar 06 '19 at 23:58
  • @Gjaa you can check this answer to learn how to add Header in Laravel Response https://stackoverflow.com/questions/17548569/where-can-i-set-headers-in-laravel – Ariful Haque Mar 07 '19 at 02:14
  • If you are working with `Laravel 5.5` and none of that is working and if you remember deleting all type of caches, try running `php artisan config:cache` in all of your servers. Was stuck on it for hours together and only that made it work. Hope it helps someone! – Hari Harker Jul 05 '19 at 08:09
20

TLDR

You need to make sure the requests include the Accept: application/json header.

Details

If a user does not authenticate, Laravel will throw an AuthenticationException.

This exception is handled by the render method in Illuminate/Foundation/Exceptions/Handler.php, and will in turn call the the unauthenticated() method which is defined in your app/Exceptions/Handler.php:

protected function unauthenticated($request, AuthenticationException $exception)
{
    if ($request->expectsJson()) {
        return response()->json(['error' => 'Unauthenticated.'], 401);
    }

    return redirect()->guest(route('login'));
}

As you can see, by default, if the request expects a JSON response, you'll get a 401 with a JSON error body. If the request does not expect JSON, the request is redirected to the login page.

The expectsJson() method will return true if your request has either the Accept: application/json header, or the X-Requested-With: XMLHttpRequest. The Accept: application/json header is more appropriate for api calls, whereas the X-Requested-With: XMLHttpRequest header is used for ajax calls.

So, without changing any of your application code, just make sure the requests include the Accept: application/json header.

However, if you need to have a different action happen when a user is not authenticated, you can modify this unauthenticated() method in app/Exceptions/Handler.php:

protected function unauthenticated($request, AuthenticationException $exception)
{
    if ($request->expectsJson()) {
        return response()->json(['error' => 'Unauthenticated.'], 401);
    }

    // return a plain 401 response even when not a json call
    return response('Unauthenticated.', 401);
}
patricus
  • 59,488
  • 15
  • 143
  • 145
  • God bless you bro – fkabeer Jan 07 '19 at 10:39
  • If you are working with `Laravel 5.5` and none of that is working and if you remember deleting all type of caches, try running `php artisan config:cache` in all of your servers. Was stuck on it for hours together and only that made it work. Hope it helps someone! – Hari Harker Jul 05 '19 at 08:09
9

In middleware you can return like this:

return abort(401);
Serdar Saygılı
  • 461
  • 3
  • 13