Search code examples
angularlaravelinertiajslaravel-jetstream

Laravel 9 with Jetstream - problem with login redirect to an Angular / SPA front-end


We're running Laravel 9 with Jetstream with Inertia/Vue and Angular 14. At login, the user is redirected to /books/user/my-books, which is defined in the Laravel router only as:

Route::any('/{any}', [AngularController::class, 'index'])->where('any', '^(books).*$');

Any route beginning with /books is handed over for handling in Angular. At Angular compile, we copy its index.html into Laravel's /resources/views as angular.blade.php. This is essentially the setup described here: https://medium.com/swlh/how-to-setup-laravel-with-angular-d3de171afa03

This setup works when the user clicks links to Angular's pages from Vue's front end or cold loads them, but when we try to load the same destinations as a login redirect, these load in an iframe. The end result is a blank page that looks like an almost full screen modal. When the browser's reload button is clicked, the requested page loads without the iframe, and things work again. (When redirecting to an Angular address at login, the URL /user/login sticks in the address bar - when reload is clicked as described above, then the destination address specified for the HOME constant appears.)

The solution we're using right now is to add the following to the Angular index.html which is copied into /resources/views/angular.blade.php:

<script type="text/javascript">
  // break out of iframe hack-job
  if (top.location != self.location) {
    top.location = self.location;
  }
</script>

This works, but it still flashes the full screen modal and UX suffers. And of course, this feels like a total hack job.

Few notes: The destination for the redirect is defined in app/Providers/RouterServiceProvider.php in the HOME constant. This is the default Jetstream setup for defining a landing page after login.

Redirects to Vue paths work - these destinations are loading cleanly, without an iframe.

We've tried creating a Laravel controller at e.g. /user/redirect, setting the HOME const as the destination to let Laravel "load more fully" before having the controller redirect to the Angular side. Same results.

We've tried defining the HOME url in Laravel's router, with the angular.blade handling it. That also didn't work, and we've pretty much eliminated the possibility that doing a redirect at login to a wildcard route is somehow the problem.

Why are our Angular routes loading fine when accessed via links in Vue and by cold loading, but when redirected to at login, they get wrapped in an iframe, and how do we prevent this behaviour?


Solution

  • We solved the problem. After creating a /user/redirect route, we serviced it with a UserRedirectController and the following code:

    class UserRedirectController extends Controller
    {
        public function user_login_redirect() {
            return Inertia::location('/books/user/my-books');
        }
    }
    

    In short: using Inertia for the redirect solves the problem - everything works without a flashing modal, and no iframe breakout code needed. This is essentially the same solution as a redirect to Vue in an SO issue from a few months back: How to redirect to a blade dashboard page from Vue login Component page after successfully login in laravel JetStream?

    UPDATE:
    This method will generate a 409 error during redirect. This appears to be normal according to https://inertiajs.com/redirects:

    EXTERNAL REDIRECTS ... The Inertia::location() method will generate a 409 Conflict response and include the destination URL in the X-Inertia-Location header. When this response is received client-side, Inertia will automatically perform a window.location = url visit.