Fresh Laravel Sanctum API redirecting to '/dashboard' route
Asked Answered
V

5

5

I have a fresh installation of Laravel Sanctum API. When I try to log a user in after registration or when submitting the registration form twice, I get an exception with the message "The route dashboard could not be found". I don't understand why it's trying to redirect the user to the 'dashboard' route. The only place that I can see 'dashboard' in my project is in RouteServiceProvider.This seems like a bug too me with Sanctum.

enter image description here

Vallee answered 3/1, 2023 at 1:59 Comment(7)
the guest middleware will try to redirect to HOME that is defined in the RouteServiceProvider by default ... is the constant for HOME set to dashboard?Dumuzi
Yes, the constant for HOME is set to dashboard. That's what I don't understand, why it's trying to redirect, it's supposed to return an API response.Vallee
are you sending the accept header for JSON?Dumuzi
Yeah, I am, in Postman and also Axios in my SPA.Vallee
clear the route cache and try again. php artisan route:clear to clear route cache. php artisan optimize to clear all cache.Perineurium
@HamidHosseini what is in php artisan route:list?Clareclarence
These comments are right. Unless you have a route already pointing to /dashboard view, you will get the 404 error. But, what if I don't have such endpoint defined or my App doesn't need it? So, forcing to redirect the user to /dashboard after authenticating is a mistake. It should be / only.Rector
R
5

this error shows when you try to register while you are already logged, so make sure to log out before register

Rubenrubens answered 19/2, 2023 at 11:55 Comment(2)
Thanks, I was trying to fix a problem that didn't exist.Vallee
It also happens after you log in, so that's not exactly a solution.Ammerman
C
8

You are right, App\Providers\RouteServiceProvider\RouteServiceProvider::HOME const is set to "/dashboard". If you follow the usage of this const you can see that is used by App\Http\Middleware\RedirectIfAuthenticated middleware

class RedirectIfAuthenticated {
/**
 * Handle an incoming request.
 *
 * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
 */
public function handle(Request $request, Closure $next, string ...$guards): Response
{
    $guards = empty($guards) ? [null] : $guards;

    foreach ($guards as $guard) {
        if (Auth::guard($guard)->check()) {
            return redirect(RouteServiceProvider::HOME);
        }
    }

    return $next($request);
}
}

This middleware is aliased as "guest" in the App\Http\Kernel:

protected $middlewareAliases = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
    'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
    'signed' => \App\Http\Middleware\ValidateSignature::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    'verified' => \App\Http\Middleware\EnsureEmailIsVerified::class,
];

And this middleware is used in unauthenticated routes defined by routes/auth.php

Route::post('/register', [RegisteredUserController::class, 'store'])
            ->middleware('guest')
            ->name('register');

Route::post('/login', [AuthenticatedSessionController::class, 'store'])
            ->middleware('guest')
            ->name('login');

Route::post('/forgot-password', [PasswordResetLinkController::class, 'store'])
            ->middleware('guest')
            ->name('password.email');

Route::post('/reset-password', [NewPasswordController::class, 'store'])
            ->middleware('guest')
            ->name('password.store');

This built-in middleware middleware will try and redirect you to the HOME if you are already authenticated. I think that there is an issue in Laravel breeze scaffolding (php artisan breeze:install api) that not provide a check on the response type in the App\Http\Middleware\RedirectIfAuthenticated middleware. If you see instead the App\Http\Middleware\Authenticate middleware here is automatically provided a redirectTo method that check for the expected response ($request->expectsJson()):

class Authenticate extends Middleware
{
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     */
    protected function redirectTo(Request $request): ?string
    {
        return $request->expectsJson() ? null : route('login');
    }
}

TLDR
In short to work correctly with the App\Http\Middleware\RedirectIfAuthenticated middleware in API based authentication you can add manually the following check in the middleware:

foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
  if ($request->expectsJson()) {
    return response()->json(['error' => 'Already authenticated.'], 200);
  }
  return redirect(RouteServiceProvider::HOME);
}
}
Christianechristiania answered 8/3, 2023 at 14:29 Comment(3)
I opened an issue on Github scaffolding project: github.com/platformsh-templates/laravel/issues/50Christianechristiania
Good effort. It is weird that your bug is still open despite it is a valid one. Just a tiny improvement to the response: return response()->json(['message' => 'authenticated.'], 200); (because it is not an error, it is the expected behavior)Mehta
Now this is an answer, explaining where that /dashboard came from was really helpful.Ammerman
R
5

this error shows when you try to register while you are already logged, so make sure to log out before register

Rubenrubens answered 19/2, 2023 at 11:55 Comment(2)
Thanks, I was trying to fix a problem that didn't exist.Vallee
It also happens after you log in, so that's not exactly a solution.Ammerman
F
1

To add to highest voted answer:

Even in Laravel 11, the RedirectIfAuthenticated middleware is still not supporting the API setup (it will always try to redirect you instead of returning a JSON response).

This is strange, because the other scenario, where you are not authenticated and trying to reach an authenticated resource, you are getting the correct 'Unauthenticated.' message for a long time now. No idea why they don't add support for this.

Solution:

Override the 'guest' alias with your own middleware in bootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {
        $middleware->statefulApi();
        $middleware->api(prepend: [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
        ]);

        // Overriding the 'guest' alias because Laravel's RedirectIfAuthenticated 
        // middleware does not have API support
        // https://github.com/laravel/laravel/pull/6229
        $middleware->alias([
            'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        ]);

        //
    })

The middleware:

use Closure;
use Illuminate\Http\Request;
use App\Responses\StandardJsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Auth\Middleware\RedirectIfAuthenticated as BaseRedirectIfAuthenticated;    
    
    class RedirectIfAuthenticated extends BaseRedirectIfAuthenticated
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(Request): (Response)  $next
     */
    public function handle(Request $request, Closure $next, string ...$guards): Response
    {
        $guards = empty($guards) ? [null] : $guards;

        foreach ($guards as $guard) {
            if (auth()->guard($guard)->check()) {
                if ($request->expectsJson()) {
                    return StandardJsonResponse::error('Unauthorized.', 403);
                } else {
                    return redirect($this->redirectTo($request));
                }
            }
        }

        return $next($request);
    }
}
Fro answered 29/8, 2024 at 11:32 Comment(0)
S
0

Make sure the dashboard route is added to the routes/web.php or routes/api.php file.

ex: Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
Sextain answered 3/1, 2023 at 9:48 Comment(0)
A
0

Add the "Accept" header to your API request and set the value to "application/json" and it will work.

Apparition answered 7/8, 2024 at 15:34 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.