Closed ysemennikov closed 2 months ago
Hi @wangsijie, thanks for assigning yourself :) If you need, I can create an account for you, so you can test it yourself on my website
Small update: I tried to debug this and localize the issue.
With this app.vue
the website loads properly (so I removed the logic of receiving tokens, but, of course, this means that auth is disabled):
<template>
<NuxtLoadingIndicator color="#409EFF" />
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
And this app.vue
causes the issue:
<script setup lang="ts">
const client = useLogtoClient();
const idToken = useState<string | null>('id_token', () => null);
const currentOrgID = useCookie<string | undefined>('organization_id');
await callOnce(async () => {
if (!client) {
throw new Error('Logto client is not available');
}
const [idTokenRes, orgTokenRes] = await Promise.allSettled([
client.getIdToken(),
currentOrgID.value ? client.getOrganizationToken(currentOrgID.value) : Promise.resolve(null),
]);
if (idTokenRes.status === 'fulfilled') {
idToken.value = idTokenRes.value;
} else {
idToken.value = null;
}
});
</script>
<template>
<NuxtLoadingIndicator color="#409EFF" />
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
My hypothesis is that maybe the client.getOrganizationToken
method requires client.getIdToken
to be called before?
So I'm using the Promise.allSettled
to avoid sequential tokens fetching (I wanted them to be fetched in parallel). Maybe I should do something like this instead of Promise.allSettled
:
idToken.value = await client.getIdToken();
currentOrgToken.value = await client.getOrganizationToken(currentOrgID.value)
Will test this soon.
I'll take a look.
Hey! Some new updates are here. Try to tested my hypothesis
My hypothesis is that maybe the client.getOrganizationToken method requires client.getIdToken to be called before?
With this app.vue
, and the issue has appeared again (infinite loading of the page). So it seems like the client.getOrganizationToken
causes the error.
<script setup lang="ts">
const client = useLogtoClient();
const user = useLogtoUser();
const idToken = useState<string | null>('id_token', () => null);
const currentOrgID = useCookie<string | undefined>('organization_id');
const currentOrgToken = useState<string | null>('organization_token', () => null);
if (user) {
await callOnce(async () => {
if (!client) {
throw new Error('Logto client is not available. Failed to fetch id_token and access_token.');
}
// Get organizations list of current user
const organizationData: { id: string; name: string; description: string | null }[] = user.organization_data;
const organizationIDs = organizationData.map(org => org.id);
// if currentOrgID is not in the list of organizations, reset it
if (!organizationIDs.includes(currentOrgID.value!)) {
currentOrgID.value = undefined;
}
// if currentOrgID is not set, set it to the first organization
if (!currentOrgID.value && organizationData.length) {
currentOrgID.value = organizationData[0].id;
}
// fetch ID token
idToken.value = await client.getIdToken();
// fetch organization access token
if (currentOrgID.value) {
currentOrgToken.value = await client.getOrganizationToken(currentOrgID.value);
}
});
}
</script>
<template>
<NuxtLoadingIndicator color="#409EFF" />
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
I will try to remove the currentOrgToken.value = await client.getOrganizationToken(currentOrgID.value);
string and test again.
I will try to remove the
currentOrgToken.value = await client.getOrganizationToken(currentOrgID.value);
string and test again.
Hey @wangsijie, I have tried this and it works. So the client.getOrganizationToken
causes the endless loading
Hi @wangsijie, do you have any updates regarding this? I tried to take a look myself, but haven't figured out what the problem might be yet. As I understand, something is wrong in @logto/client
, maybe you could point me so I could find a solution? Unfortunately it's kind of a blocker for us now, because it's impossible to release our app if the page simply doesn't load :(
Thanks for your patient, I am looking into this today.
hi @ysemennikov, I tried but I am unable to reproduct the "endless loading", after the local tokens expired, trying to "getOrganizationToken" will throw a 500 error in nuxt app, and it won't redirect to sign in page automaticlly. I guess you have some error handlers that will catch the error and do the redirection?
btw, the SDK itself is unable to detect the "expiration", even if the idToken is invalid, isAuthticated()
still returns true
, it only check if idToken exists.
https://github.com/logto-io/js/blob/master/packages/client/src/client.ts#L163
Hi @wangsijie, thank you very much for the response! Unfortunately I can't catch the error, Sentry can't do it too. It simply shows that navigation takes a lot of time (I closed the browser tab after 43 seconds):
I will try to find a workaround for this, maybe try with a fresh Nuxt project.
By the way, is it possible to refresh an ID token in Nuxt? It seems like I have to reload the page if it expires, because the Logto Client is only available only on the server side.
hi @ysemennikov
client.signIn()
Hi @wangsijie
- Could you please show how you did the redirection? A simple example code in a repo or something like codesandbox would be great.
We redirect to sign-in page using the usual NuxtLink. The user has to click this link in order to be redirected.
Here is the codesandbox: https://codesandbox.io/p/devbox/logto-nuxt-bug-pdsww3. There is a SyntaxError in the preview (don't know why), but you should be able to open it locally.
Well, there is only one Nuxt middleware that can influence redirect: middleware/auth.global.ts
. But actually it seems to be configured properly, don't think it causes the error.
Hi @ysemennikov
I've successfully run the project on my end.
However, I was unable to reproduce the "endless loading" problem you reported. When the token expires, I'm encountering a 500 error with the message "Response.clone: Body has already been consumed." instead of the endless loading behavior.
Upon investigation, I've identified that this 500 error is occurring because the "fetchUserInfo" function is failing. To address this, I'll be preparing a patch to fix the issue. The update will modify the failed request to return null instead of throwing an error, which should resolve the problem. Also, this will make the user
in the middleware to be null
, and then you can get the authentication state correctly.
hi @ysemennikov, a new version of SDK is just released, resolving the above 500 error, could you please try again?
Hi @wangsijie, thank you for the update! I have just updated the package on our side and am testing it right now. Will get back soon!
Hi @wangsijie, sorry for so a long response. I have tested the new version now and unfortunately using organization tokens still resulted in endless loading. However, a couple of days ago we've decided to use only access tokens (with client.getAccessToken
method) because of our business logic's requirements. Everything works well now, I haven't noticed any bugs like endless loading.
As far as I know, client.getOrganizationToken
uses client.getAccessToken
under the hood, so I assume the error has been resolved and we can close the issue for now.
Thanks @ysemennikov, I'll do futher invesitigation to find any other possible cause. Feel free to reopen!
Describe the bug
After I sign in and the given ID token expires, it seems like it's not being refreshed. This results in endless loading of the page. However, when I go to the sign-out route (domain.com/sign-out), I successfully log out and the page then loads successfully again.
Expected behavior
The page loading is not blocked. The token has to be refreshed.
How to reproduce?
app.vue:
Context
Nuxt 3 app with Logto module installed, deployed on Vercel. We also use Organizations feature of Logto.
Screenshots