nhost icon indicating copy to clipboard operation
nhost copied to clipboard

Next JWT token expired when using SSR

Open AlexanderMoroz opened this issue 2 years ago • 18 comments

So after discussion with @dbarrosop in discord help channel I've decided to open an Issue here

Describe the bug I'm investigation integration next + nhost with SSR enabled (pages router). I've setup our app the same way as in examples - <NhostProvider> + <NhostApolloProvider> + getNhostSession from nhost/nextjs

In general it works really well! Thanks guys for all your work.

But sometimes (and it's even not clear when and why) I recieve errors about JWT token being expired, both on client and server. Previously we were using next but without getNhostSessions so all auth part was working only on client and it was working flawlessly

error: ApolloError: Could not verify JWT: JWTExpired To Reproduce Steps to reproduce the behavior:

  1. Go to start page.
  2. Wait till refresh token expired

Expected behavior Token updates automatically

Screenshots

Desktop (please complete the following information):

  • OS: macOS 14.0 (23A344)
  • Browser: Chrome: 118.0.5993.117
  • Versions:
    • "next": "13.3.0",
    • "@nhost/apollo": "^5.2.19",
    • "@nhost/nextjs": "^1.13.33",
    • "@nhost/nhost-js": "^2.2.12",
    • "@nhost/react": "^2.0.27",
    • "@nhost/react-apollo": "^5.0.36",

Code

ApptProvider in _app.tsx

export const Provider: FC<ProviderProps> = ({
  children,
  nhostSession,
  transitionConfig = {
    type: 'spring',
    bounce: 0.01,
  },
}) => {
  return (
    <NhostProvider nhost={nhost} initial={nhostSession}>
      <ApolloProvider>
        <MotionConfig transition={transitionConfig}>{children}</MotionConfig>
      </ApolloProvider>
    </NhostProvider>
  );
};

ApolloProvider used above:

import { FC, PropsWithChildren } from 'react';
import { NhostApolloProvider } from '@nhost/react-apollo';
import { nhost } from '@htch/shared-services/nhost';
import { NhostApolloClientOptions } from '@nhost/apollo';

export const ApolloProvider: FC<
  PropsWithChildren<Omit<NhostApolloClientOptions, 'nhost'>>
> = ({ children, ...props }) => {
  return (
    <NhostApolloProvider nhost={nhost} {...props}>
      {children}
    </NhostApolloProvider>
  );
};

getNhostSession:

import { getNhostSession as fetchNhostSession } from '@nhost/nextjs';
import { REGION, SUBDOMAIN } from '@htch/shared-services/nhost';
import { GetServerSidePropsContext } from 'next';

export const getNhostSession = (context: GetServerSidePropsContext) =>
  fetchNhostSession(
    {
      subdomain: SUBDOMAIN,
      region: REGION,
    },
    context
  );

Screeshoots

image

this is console.log from getNhostSession image

So session is here, but JWT is expired

Nhost refresh token expired at looks correct image So it expires in a future at the moment of screenshot

Also noticed that If I delete nhostSession from cookies and just refersh page - it started to work

Would be glad to get any help with it. Thanks!

AlexanderMoroz avatar Oct 31 '23 12:10 AlexanderMoroz

thanks for the report, we will try to look into this as soon as possible

dbarrosop avatar Nov 03 '23 07:11 dbarrosop

Hi. Thanks a lot.

Sorry for maybe a late response, but we pushed all new SSR-related changes to dev environment and looks like this issue is not reproducible in production mode. I do remember that I've tried production build on my local machine and saw error related to JWT token expiration, but at least on dev env we can't see it.

So I assume that this might be related to hot-reloading.

AlexanderMoroz avatar Nov 03 '23 10:11 AlexanderMoroz

Any updates on this?

mmlngl avatar Nov 14 '23 16:11 mmlngl

Update from my side:

This definitely happens in production mode

AlexanderMoroz avatar Nov 20 '23 17:11 AlexanderMoroz

Our users are reporting issues in production too.

Is there anything I can do to help? Repo? Screenshots?

mmlngl avatar Nov 20 '23 18:11 mmlngl

The issue is that we haven't been able to reproduce consistently so any information you can share on how to reproduce will help debug this. For instance, when does it happen? Is this an issue with users having the refresh token expire? Do you have subscriptions?

dbarrosop avatar Nov 21 '23 08:11 dbarrosop

Yep, it's quite hard to reproduce. So I've noticed that this happens in dev env (next 13.3 + ssr + page routes) when I have two users simultaneously accessing application. Looks like it's same issue on our dev env with production build.

Yes, we do have subscriptions and we use them quite a lot. For me it looks like token expired and client on server doesn't know how to refresh it and after this things are messed up. The only thing that helps - is to do signOut and then sign in back. Just refresh page and trigger new SSR run does not help.

Maybe I can take a look at some specific logs when we have this error and provide it to you?

AlexanderMoroz avatar Nov 21 '23 10:11 AlexanderMoroz

ok, finally!

We've managed to reproduce this bug in a fresh repo with all new versions of npm packages (not nhost-cli yet). I've made a loom-video with explanation of what is happening and looking forward to continue discussion.

https://www.loom.com/share/4620beb23f964c7d9cf7e5192d550aea

AlexanderMoroz avatar Nov 22 '23 16:11 AlexanderMoroz

Thanks @AlexanderMoroz

mmlngl avatar Nov 22 '23 17:11 mmlngl

https://github.com/HTCH-app/nhost-jwt-token-test

That's a repo. Unfortunately we don't include our nhost instance in this repo.

AlexanderMoroz avatar Nov 22 '23 17:11 AlexanderMoroz

@dbarrosop @onehassan

Hello again! Could you please take a look at this bug once more? Also would be great to check video + repo to reproduce.

We add temporary fix which is a bit smelly, so would be great to have this thing properly fixed. What we do is to catch error in apollo link and if it's JWT is expired error we just call nhost.auth.refreshToken() method. This helps, but not always. Graphql-ws which apollo uses throws an error which we can't catch with apollo link, and websockets are failing as a result.

Here is an example of temporary fix:

import { nhost } from '@htch/shared-services/nhost';
import { onError } from '@apollo/client/link/error';

const JWT_ERROR_MESSAGE = 'Could not verify JWT: JWTExpired';

// Log any GraphQL errors or network error that occurred
export const createNhostJwtErrorMiddleware = () => {
  let isJwtExpiredError = false;

  return onError(({ graphQLErrors, networkError, ...rest }) => {
    if (graphQLErrors)
      graphQLErrors.forEach(({ message }) => {
        if (message.includes(JWT_ERROR_MESSAGE)) isJwtExpiredError = true;
      });

    if (networkError) {
      if (networkError.message.includes(JWT_ERROR_MESSAGE))
        isJwtExpiredError = true;
    }

    if (isJwtExpiredError) {
      nhost.auth
        .refreshSession()
        .then(() => {
          isJwtExpiredError = false;
        })
    }
  });
};

Thanks in advance!

AlexanderMoroz avatar Nov 29 '23 16:11 AlexanderMoroz

Unfortunately we are a bit caught up with other stuff right now but it's on our shortlist of things to look at so as soon as those are out of the way this comes next (should be at some point during December 🤞). Apologies for the delay.

dbarrosop avatar Nov 30 '23 13:11 dbarrosop

Thanks for response!

AlexanderMoroz avatar Nov 30 '23 14:11 AlexanderMoroz

HNY folks!

Curious: did you get to look at this in Dec?

We see the often over and over again in production.

mmlngl avatar Jan 03 '24 14:01 mmlngl

Not yet, unfortunately December turned out to be way too optimistic, specially given our AI week and the holidays. We will most likely start looking into this before the end of the month.

dbarrosop avatar Jan 04 '24 08:01 dbarrosop

thanks for the update!

mmlngl avatar Jan 04 '24 18:01 mmlngl

Don't suppose this has started yet?

mmlngl avatar Jan 22 '24 15:01 mmlngl

We just started looking into this so we will hopefully have news soon.

dbarrosop avatar Feb 01 '24 18:02 dbarrosop

As you probably noticed we have a fix already. We will be releasing a new version of the SDKs with it tomorrow. If you upgrade, we'd appreciate if you could confirm the issue is gone. Thanks for your patience.

dbarrosop avatar Feb 22 '24 18:02 dbarrosop

Whoop whoop!

Thanks for the hard work of all your team.

We'll report back with confirmation.

M

On Thu, 22 Feb 2024, 18:25 David Barroso, @.***> wrote:

As you probably noticed we have a fix already. We will be releasing a new version of the SDKs with it tomorrow. If you upgrade, we'd appreciate if you could confirm the issue is gone. Thanks for your patience.

— Reply to this email directly, view it on GitHub https://github.com/nhost/nhost/issues/2348#issuecomment-1960018696, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAALNV5G5CJPVT5I3PWWC4TYU6EQBAVCNFSM6AAAAAA6XTCIA6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNRQGAYTQNRZGY . You are receiving this because you commented.Message ID: @.***>

mmlngl avatar Feb 22 '24 19:02 mmlngl

Initial tests are in.

We upgraded all nhost deps in our monorepo to:

"@nhost/apollo": "^5.2.22",
"@nhost/nextjs": "^1.13.40",
"@nhost/nhost-js": "^2.2.18",
"@nhost/react": "^2.1.1",
"@nhost/react-apollo": "^5.0.38",

Question: Do these versions have contain the fix?

Result: No change.

Screenshot 2024-02-26 at 12 15 30

mmlngl avatar Feb 26 '24 12:02 mmlngl

No, those are old packages. For instance, latest @nhost/apollo is 6.0.7.

dbarrosop avatar Feb 26 '24 12:02 dbarrosop

Hi @mmmoli, can you please try again with the latest versions:

@nhost/nhost-js: 3.0.7
@nhost/apollo: 6.0.7
@nhost/nextjs: 2.1.4
@nhost/react: 3.2.2
@nhost/react-apollo: 9.0.2

onehassan avatar Feb 26 '24 15:02 onehassan

@dbarrosop @onehassan does nhost support next@14 now?. Looks like we start having this issue after upgrading to [email protected], downgrading back to 13.5.6 fixed the problem. Could you please check?

AlexanderMoroz avatar Apr 03 '24 15:04 AlexanderMoroz