Why does a request to an undefined route result in 401 unauthenticated in Laravel

Viewed 820

A Laravel 8 application with Sanctum.

When I send a request to a defined route using Postman with an access token the response is correct. I'm happy with this.

When I send a request to a defined route using Postman without an access token the response is 401 Access denied. I'm happy with this.

When I send a request to an undefined route using Postman with an access token the response is 401 Unauthenticated. I'm NOT happy with this. I want a more appropriate response, such as 404.

I don't know how to choose what code from my application to include this question. Please ask me for whatever might be useful and I'll add it.

Update:

Thanks to the answer by @MartinZeitler I worked out that the problem is caused by me making Nova accessible at / instead of /nova.

I did this in /config/nova.php by changing

'path' => '/nova', to 'path' => '/',.

If I revert this to '/nova' then everything works as I expect it to.

But I need Nova at /, so what can I do?

6 Answers

It's difficult to answer a routing question without, eg. the output of php artisan route:list.
The 401 likely comes from Sanctum or from missing a catch-all route Route::any('', [ ... ]).
Since Laravel 8.26.0, there is a new missing() method available.

Beside that, my best bet would be Illuminate\Foundation\Http\Kernel ...

/**
 * The priority-sorted list of middleware.
 *
 * This forces non-global middleware to always be in the given order.
 *
 * @var array
 */
protected $middlewarePriority = [];

401 & 404 are exceptions that exit the application flow from normal behavior. in your situation it seems The 401 exception get executed sooner, and doesn't let the 404 to get executed.

if these exception controls has been placed in the middlewares, you could change the execution order of middlewares from The App\Http\Kernel

for route middleware from $middlewarePriority

for global middleware from $middleware

but The url not match error (The 404) occurs here :

Symfony\Component\Routing\Matcher The method: match(), and it is out of middlewares scope.

but how to prevent the 401 from running first? this exception is thrown from the authentication middleware,

The Middlewares can get called in 3 places in the laravel:

  1. as Global Middleware
  2. as Route Parameter Middleware
  3. only get called in the Controller's Constructor

I have tested, that if The middleware is Global it gets executed, before the Url not match 404.

In the laravel docs, it is mentioned that:

The withoutMiddleware method can only remove route middleware and does not apply to global middleware.

I would translated it to:

The Global Middleware has executed before we get to the routes files

TL;DR

so I guess, You have added the authentication middleware in the $middleware. and I think you have to move it ('auth' => \App\Http\Middleware\Authenticate::class) to the $routeMiddleware array.

then use the middleware(auth:sanctum) in The routes files or the RouteServiceProvider

The issue in way that token comes from server and stored to local storage. example

{success{token:xfdhgkhkhewrh}}

Token like

'Authorization': 'Bearer' + " " + this.token.success.token

from local storage. **TOKEN UNDEFINED** was the issue of returning **401**.

For detailed explanation

Maybe need to check a FormRequest class which handle your method in the controller, and then check method authorize he is should return when true for example

public function authorize()
{
   return auth('sanctum')->check(); // or for test just return true
}

try to add this to the Handler.php file in the register function in Exceptions directory.

  $this->renderable(function (NotFoundHttpException $e, $request) {
      //json response
      return $this->error('Not found', 404);

      // return 404 as html view
      // abort(404);
   });

You can create custom exception handling to handle all exceptions.

public function register()
{
    $this->renderable(function (\Exception $e, $request) {
        if($request->is('api/*'))
            return $this->handleException($request, $e);
    });
}

public function handleException($request, \Exception $exception)
{
    if ($exception instanceof ValidationException) {
       return $this->sendValidationError($exception->errors());
    }

    if ($exception instanceof NotFoundHttpException) {
        return $this->sendError([], 404, __('message.page_not_found'));
    }

    if ($exception instanceof AuthenticationException) {
        return $this->sendError([], 401, __('auth.unauthorized'));
    }

    if ($exception instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
        return $this->sendError([], 404, __('message.not_found'));
    }

    if($request->is('api/*')) {
       return $this->sendError([], 501, __('message.error_message'));
    }
}
Related