2
votes

I have a React SPA in the same Laravel project. The login/signup/logout and all other js views are in the js folder and use axios api calls for all POST/GET requests. I want to use the default Laravel session based web authentication for the embedded SPA, since it's in the same project folder and it will be the only javascript client accessing it. This api does not need to be open to the public, just for this react app, and it's an SPA for the speed and good user experience instead of full page reloads.

I've tried using Passport before, and for over a month, I still can't get it to work as intended. I do not want to deal with tokens, access tokens, refresh tokens, revoking tokens, CSRF, etc. Just the out of the box simple Laravel session based auth that works so easily on web, but want it to work on my react app. The only blade file is the index.blade.php which includes the react app.js

Any idea how we can accomplish this?

UPDATE 1:

After implementing @ceejayoz's suggestion:

You have to add the various Session/Cookie middlewares in app/Http/Kernel.php (stuff like \Illuminate\Session\Middleware\StartSession::class) to the API routes.

I added to $middlewareGroups.api to match the web middleware in app/Http/Kernel.php:

'api' => [
    'throttle:60,1',
    'bindings',
    // Newly added middleware to match web middleware
    \App\Http\Middleware\EncryptCookies::class
    \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
    \Illuminate\Session\Middleware\StartSession::class,
    \Illuminate\View\Middleware\ShareErrorsFromSession::class,
    \App\Http\Middleware\VerifyCsrfToken::class,
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

I realized there are two issues that occurred:

  1. In the sessions table, even if not logged in, when loading app home page (or any page), multiple sessions are inserted into the sessions table. Shouldn't a new single session be inserted into this table only after user login?
  2. After user log in, when refreshing the page manually in the browser and a call is made to a protected route, I get a 401 Unauthenticated which points me to this method in Illuminate/Auth/GuardHelpers.php:

    public function authenticate() {
        if (! is_null($user = $this->user())) {
            return $user;
        }
    
        throw new AuthenticationException; // throws this 401 exception on logged in page refresh when fetching data from private route
    }
    

Some additional notes:

  • In config/auth.php I updated the guards.api.driver to session instead of token.
  • In routes/api.php I have the protected routes wrapped in auth middleware like this: Route::group(['middleware' => 'auth'], function() { PRIVATE ROUTES HERE }
  • In config/session.php I have 'domain' => '.mydomain.com'
  • I am sending back these headers with each axios api request like this:

    window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
    let token = document.head.querySelector('meta[name="csrf-token"]');
    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
    

Any idea how we can fix these 2 issues?

2
It's doable. You have to add the various Session/Cookie middlewares in app/Http/Kernel.php (stuff like \Illuminate\Session\Middleware\StartSession::class) to the API routes.ceejayoz
If you plan using your routes with /api prefix, you can just define them in routes/web.php and not in routes/api.php. Make sure that you include auth middleware. Then you can place them into a group Route::group(['prefix' => 'api', 'middleware' => ['auth'],], function () {...ljubadr
@ceejayoz That's actually pretty smart, thanks for sharing! This is SO much easier than implementing Passport which I think would've been an overkill for my use case. If you post an answer I will accept it as it required the least amount of refactoring. Thank you for your comment!Wonka
@ljubadr I was able to leave the api.php file as is and not move it to web.php using ceejayoz awesome technique. Just had to update 'auth:api' to 'auth' and it worked. Thank you for your comment!Wonka
I'm glad you found a solution!ljubadr

2 Answers

2
votes

Looks like your session was not persistent.

Check if you changed any values in config/session.php that might create problems.

You can check default sesion config values here

From the comments, @Wonka solved his problem by changing

'same_site' => 'strict'

to

'same_site' => null
1
votes

It's doable (and I've done the same myself for some apps).

By default, the routes in routes/api.php don't have sessions available, but you can add the various Session/Cookie middlewares in app/Http/Kernel.php (stuff like \Illuminate\Session\Middleware\StartSession::class) to the API routes.

You can, as @ljubadr suggested, also put the API routes right in routes/web.php instead, although that'd probably mean you'd need to make other changes (like removing CSRF protection from the web routes).