Laravel Fortify doesn't respond with HTTP code but with actual routes, even if I send the registration request as an XHR one
Asked Answered
B

5

5

Context

With Postman, I send the following fields in order to register a user in the db, to the URL http://mywebsite/register:

  • email

  • password

  • password_confirmation

  • name

According to the documentation https://laravel.com/docs/8.x/fortify#registration (from which I found the above fields), Fortify already defines the route register so I don't need to define it myself.

According to the documentation, and to my needs, I don't need to create a registration form: I directly use Postman to send these registration data to Laravel Fortify's route /register as XHR POST request data. Moreover, I don't need Fortify to return views so I've disabled them (https://laravel.com/docs/8.x/fortify#disabling-views). Indeed, I just wait for HTTP code response that I will see in Postman's return data (see below).

What Fortify should return

Since I've disabled the views AND since I send a XHR POST Request (in Postman I'm sending this HTTP header along with the registration request: X-Requested-With = XMLHttpRequest):

If the registration attempt is successful, Fortify will redirect the user to the URI configured via the home configuration option within your application's fortify configuration file. If the login request was an XHR request, a 200 HTTP response will be returned.

If the request was not successful, the user will be redirected back to the registration screen and the validation errors will be available to you via the shared $errors Blade template variable. Or, in the case of an XHR request, the validation errors will be returned with a 422 HTTP response.

(https://laravel.com/docs/8.x/fortify#registration)

What Fortify actually returns

It correctly registers the user. But the results I see in Postman is a Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException. Indeed, Fortify is trying to reach the home view.

Question

Why does Fortify still seem to try to reach returned views routes (not defined because I don't need them), since my request is correctly an XHR POST one and since I've disabled the views in the Fortify config file?

Becoming answered 10/3, 2021 at 10:41 Comment(0)
B
4

I think I've found something "interesting" in the Fortify doc (https://laravel.com/docs/8.x/fortify#registration). Something is incomplete there. I think I've found what is missing in the docs!

I explain.

In my case, a phone app shows a registration form. It sends the Fortify's registration required fields like email, password, confirmation_password, ... to the URL <LaravelSite>/register which is defined by Fortify. Fortify successfully register the user and redirects him to the home route. However, I don't have defined any home route since that as you have understood, this Laravel site is an API. Results => I have this error: Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException.

I've followed the docs: I've disabled the views (in the Fortify config file). I've correctly specified that the request sent by the phone app (in reality, by Postman) is a XHR Request (indeed, I've sent the header X-Requested-With = XMLHttpRequest). So Fortify should return me a HTTP Code (200 here) instead of trying to return me the home URL.

What is missing in the doc? =====>>>> the middleware RedirectIfAuthenticated is executed. In its handle method there is a redirection: return redirect(RouteServiceProvider::HOME);. In order to make Fortify work correctly, this line must be disabled by commenting it.

The Fortify docs should contains something like the above sentence.

A PR or an issue should be created on their Github repository.

EDIT: we must also send this HTTP header: Accept: application/json (otherwise in case of registration success, it will still show a 404)

Becoming answered 10/3, 2021 at 13:24 Comment(1)
Oh my god, this just fixed the issue i've been battling for daysPeabody
T
2

setting the config variable views to false won't work as it only disable rendering the views file of fortify like login page, register page, reset password page etc.

Fortify route uses the middleware [web] by default you need to set this middleware value to [api] in the same config file where you have set views to false.

Thoughtful answered 10/3, 2021 at 11:30 Comment(3)
If I use api instead of web, I have this error: "Argument 1 passed to Laravel\\Fortify\\Http\\Controllers\\RegisteredUserController::__construct() must be an instance of Illuminate\\Contracts\\Auth\\StatefulGuard, instance of Illuminate\\Auth\\TokenGuard given"Becoming
Same here. Did you manage to solve it?Uzbek
You will get the error @Becoming when you have set the driver to 'token' in your auth file. The fix is: 1. set 'guard' => 'api' in fortify.php 2. set 'driver' => 'session' in the api section of auth.phpOgdan
B
2

Problem is with RedirectIfAuthenticated Middleware as @JarsOfJam-Scheduler stated. You can disable redirects for xhr request in RedirectIfAuthenticated Middleware by adding check.

if(!$request->wantsJson()){
    return redirect(RouteServiceProvider::HOME);
}

And dont forget to add header to axios

'Accept': 'application/json',
'Content-Type': 'application/json'
Batish answered 9/1, 2022 at 14:49 Comment(0)
M
1

Incidentally I was having the exact same issue. After 30min of online search, I ended up checking my trusted Laracast database, and for sure I found a video of 5min showing me how to (somehow) fix the issue.

https://laracasts.com/series/laravel-authentication-options/episodes/15

Simply put, all you need to do is add a register route to your routes/api.php file. The code for the route can be copied from the Fortify vendor directory: vendor/laravel/fortify/routes/routes.php. Copy the register route on lines 72/73. Paste it in api.php. POST your request now to api/register, and it will work like on the video.

Hope that helps

Merth answered 1/6, 2021 at 20:16 Comment(0)
P
1

Thanks a lot!

I'm also add \Illuminate\Http\JsonResponse as possible returned type. It's looks like it works fine.

public function handle(
    Request $request,
    Closure $next,
    string|null ...$guards,
): (
    \Illuminate\Http\Response |
    \Illuminate\Http\RedirectResponse |
    \Illuminate\Http\JsonResponse // <<<--- This!
)
{
    $guards = empty($guards) ? [null] : $guards;

    if (!$request->wantsJson()) { // <<<--- This!
       foreach ($guards as $guard) {
           if (Auth::guard($guard)->check()) {
               return redirect(RouteServiceProvider::HOME);
           }
       }
    }

    return $next($request);
}
Passel answered 26/6, 2023 at 8:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.