Handling Session Expiring

I have two questions that I’m sure revolve around the same thing…

How do you handle the session timing out? In other words, someone’s working away, then leaves to run some errands, then comes back - with that first API call, assuming I’m following the great methodology in your book - how do you handle informing them they have to log back in?

Secondly, when visiting a URL directly, say from an email, or just by typing in a specific link, everything comes in BUT the logged in status. They might be logged in in another tab, but for some reason this new request isn’t picking it up. Where would I go about making that check?

This also comes up when users wants to alt-click open another tab - it doesn’t retain that logged in status.

It works great locally - but I think that’s because it’s running SPA-style, instead of the SSR - is this covered in the book?

Thanks ahead of time!

Hi @jhull,

To be honest, we will be getting to these type of scenarios with ROAST and don’t have them implemented quite yet. However, it’s very important (especially, as I can imagine with users entering a lot of content). It can even become an issue if you have the user authenticated and they spend 4 hours generating content without saving.

With the first question, there are two ways to approach this. If you want the user to remain logged in, you could follow an approach similar to how they implement it in WordPress admin. You’d create an interval that pings a an endpoint (such as /api/v1/user) that will re-fresh the session. This will do two things, first, it will maintain the session for the user. Second, and possibly most important, you can hook into this interval to see if the user is NOT authenticated, then prompt them with the login screen.

You can then allow the user to re-auth and not do any redirection so they save where they are at. Below is a little pseudo code for that:

// Import User API

export default {

    mounted(){
        setInterval( function(){ 
            this.checkAuth();
        }.bind(this), 30000);
    },

    methods: {
        checkAuth(){
            this.userAPI.get()
                .then(function(){
                    // Everything is cool, possibly even save some
                    // sort of local cache of data.
                })
                .catch(function(){
                    // Prompt Login but do not redirect after login.
                    // ROAST does this if you try to like a cafe without authentication.
                });
        }
    }
}

I’d probably put this in the App Layout so it’s present on every page and call it in the mounted() hook. If you have multiple layouts, I’d put this in each route file and call it on the mounted() hook, however, I’d destroy the call back on beforeRouteLeave() so you don’t compound 100s of checks. Hopefully that helps!

As for your second question, this is definitely an SSR issue. The reason SPA works is because it makes a call to the server to load the authenticated user instead of having the user returned via server. When we had this issue in the past, it was because the X-XSRF-TOKEN was not being proxied to the API. Does this workflow work:

  1. User authenticates
  2. Open a New Tab
  3. Navigate to app and the user is NOT authenticated
  4. In new tab, click a link within the app that would require auth. User now appears authenticated.

If that work flow works, it’s because of the proxying the node server headers to the API so the API has access to the Laravel Sanctum token. Let me know if that helps as well!

I will try out the first approach this week and let you know how it works out.

In regards to second–no, it won’t pick up the Auth when navigating like that. Pretty sure it’s just showing the SSR-ed page when I build the site out. No matter where I click through. End up having to close out the browser and log back in – which is less than ideal.

I know you had to set up your proxy for SSR before, I’d definitely make sure that is configured. That would be the main reason why this isn’t passing all the way through, the proper headers aren’t being proxied to the server. We had to do that by installing the dev auth module that contained Laravel Sanctum. You can do it though with the actual configuration as well. If you test a few things, I can set up some tests as well and see if we can share some code and figure it out.

But in production, there isn’t the proxy anymore, right? It actually works the reverse, where locally it functions as expected. It’s when its in Production that it tanks.

If you aren’t using the new Dev Auth module with Laravel Sanctum strategy, I do believe you need to have a proxy configured. Reason being is the node server that actually renders the page doesn’t have access to the headers that are in the request so they aren’t passed to your server initially. The Laravel Sanctum strategy should be passing these headers. If not, check out your axios module: https://axios.nuxtjs.org/setup#install (used by auth).

I’m probably completely dense here, but yes, I’m using the Dev Auth module and I can see that the SSR is “getting” the Auth - I have one computed property in a component based on this:

    computed: {
      ...mapState('auth', ['loggedIn', 'user'])
}

That will display properly on initial Load:

<div v-show="loggedIn">
  <UserProfile/>
</div>

That shows up OK in a header component. But when used within a page component like this:

      <div v-if="loggedIn">
        <vimeo-player ref="player" :video-id="page.video_id"
          :options="{'responsive':true}" class="video-embed"/>
      </div>

It shows the other v-else conditional instead.

Ok. I was able to figure it out - leaving it here in case someone else runs into the same problem. I’ve seen a lot of comments about trouble with Nuxt, hydration, static sites, SSR, and Authorization, so hopefully this will help someone out.

I have some pages that have both public content and private subscriber-related content. In order to benefit from the static site generation while being able to check if someone is logged in, I had to wrap the subscriber components in a <client-only> tag like this:

<client-only>
  <SubscriberComponent/>
</client-only>

Then, in the component, I do the check for whether the user is logged in or not, and if so, display the content:

<template>
  <div>
    <div v-if="$auth.loggedIn">
      ... all the secret stuff
    </div>
  </div>
</template>

Nuxt will generate the pages without the component, then when called up, will check to see if authorized and if so show the content.

It works GREAT now, which makes me–and everyone who subscribes–very happy. :smile:

Wow! Thank you so much for sharing, that’s EXTREMELY useful! I honestly haven’t ran into this scenario before and I really appreciate you posting the solution. Those SSR issues (especially with auth) are extremely difficult to diagnose.

You’re welcome :smile:

Don’t know if you want to split this out (since it’s two different topics), but the checking for logged in status is not quite working as expected…

In fact, what happens with the above is it keeps everyone logged in indefinitely LOL, I have it checking every 2 minutes, and for some reason then it just keeps them logged in beyond the session expiring.

I assume when you make a call, it resets the timer? Or I have it set to that?

I’m following what you have above. And my checkAuth() method is just hitting /api/user. Losing the authorization never happens…

Hi @jhull,

I was out of town, so I apologize for the late reply! Now when I think about it, when you hit the endpoint, it will reset the timer (which we don’t really want to do). It acts like another request since Sanctum refreshes the session. I think we might have to come up with an endpoint that is not protected by Laravel Sanctum so it’s a stateless check. I’ll have to do a little research on this.