Dan Mercer
Dan Mercer3y ago

Auth0 token expired

I'm using Convex + Auth0 (with a custom provider, but it's pretty similar to ConvexProviderWithAuth0). When I leave a page open for a while and then try to do something, I get a "token expired" error from Convex. Presumably I need to update the ID token periodically. Has anyone else run into similar problems?
32 Replies
jamwt
jamwt3y ago
@gautamg may know
nipunn
nipunn3y ago
Usually the ID token lasts a while (like a day) - and then typically the auth provider makes you log in again. What's the exact error you are getting - and in what scenario? I can look into it a bit. If I am understanding correctly, I believe the useAuth0 hook handles this for you - by unsetting the isAuthenticated prior to expiry and thus causing your react app to show the login page again.
nipunn
nipunn3y ago
I (think) I was able to repro by lowering my auth0 ID token expiration on my app to 60 seconds. Typically it's 10 hours (36000s). The behavior I observed was 1) useAuth0 requested login on every refresh (presumably because it is always close to expiring). 2) Convex would write "Token expired" to the console after 60 seconds after login if I tried to call an action or mutation. Does that match the behavior you're seeing?
No description
No description
ballingt
ballingt3y ago
@Dan Mercer For a solution for now, you could do something like what @nipunn came up with here: call getAuthTokenSilently to refresh auth and set the token again.
const { isAuthenticated, isLoading, getIdTokenClaims, loginWithRedirect, getAccessTokenSilently } =
useAuth0();
useEffect(() => {
async function setAuth() {
// Has side effect of updating ID token
await getAccessTokenSilently();
// Get the new ID token
const claims = await getIdTokenClaims();
const token = claims!.__raw;
console.log("Setting auth to");
console.log(token);
client.setAuth(token);
setClientAuthed(true);
}
const setAuthIntervalHandle = setInterval(setAuth, 3600 * 1000); // get a new token once an hour

if (isAuthenticated) {
void setAuth();
setClientAuthed(true);
return () => client.clearAuth();
}
}, [isAuthenticated, getIdTokenClaims, isLoading, client]);
const { isAuthenticated, isLoading, getIdTokenClaims, loginWithRedirect, getAccessTokenSilently } =
useAuth0();
useEffect(() => {
async function setAuth() {
// Has side effect of updating ID token
await getAccessTokenSilently();
// Get the new ID token
const claims = await getIdTokenClaims();
const token = claims!.__raw;
console.log("Setting auth to");
console.log(token);
client.setAuth(token);
setClientAuthed(true);
}
const setAuthIntervalHandle = setInterval(setAuth, 3600 * 1000); // get a new token once an hour

if (isAuthenticated) {
void setAuth();
setClientAuthed(true);
return () => client.clearAuth();
}
}, [isAuthenticated, getIdTokenClaims, isLoading, client]);
nipunn
nipunn3y ago
I had it refresh every 30 seconds for sake of testing, but you can pick a frequency that makes sense for you given your id token expiration time.
ballingt
ballingt3y ago
updated the snippet to an hour case people come copy-pasting
RJ
RJ3y ago
I’ve encountered a similar problem in my app (using Clerk instead of Auth0), but hadn’t investigated it at all yet. So this is also helpful for me, thank you!
Dan Mercer
Dan MercerOP3y ago
Awesome, thanks! I didn't know that about getAccessTokenSilently, I'll give that a try. I'll post here with more details if I'm still seeing problems. I'm still seeing problems. It happens a few times a day on my various devices. I leave my web app open on my device, then when I come back to it after being away for a while (sometimes less than an hour), Convex throws a Token expired error. Then I refresh the page, and Next.js fails to start with a message saying a client-side error occurred - and when I check the browser console, it's a "not authenticated" error thrown from inside one of my queries. I've tried to make sure all my queries are gated behind auth being present, though, so that's super frustrating Hmmm I actually think the second error might be because my React skills are bad. 😅 But the first error I'm still stumped by
RJ
RJ3y ago
I continue to have similar problems. Per previous discussion in this thread, when I went to add code to refresh the token at a sufficiently frequent interval, I saw that I already was doing so (every 50 seconds). I hadn't posted more about it because I have had trouble isolating the problem and wanted to spend more time investigating it to ensure it's a Convex-related issue.
ballingt
ballingt3y ago
We're hoping to make some client changes that will help here, but to we should be able to get this working now with the workaround described. Could you share your auth-related code, or would you be up to look at this on a screenshare call @Dan Mercer?
RJ
RJ3y ago
I'm pretty sure I've only seen this issue when a tab is open for long enough (for the browser to suspend its internet connection? for my computer to sleep?), like you describe @Dan Mercer Refreshing the page does resolve the problem for me, though
ballingt
ballingt3y ago
also curious about Auth0 settings
RJ
RJ3y ago
My session duration, using Clerk, is 7 days. In my dev environment I set it to 5 mins (the minimum Clerk permits) and still observed the problem (it did not seem to correspond at all with session expiration)—if you're interested @ballingt.
ballingt
ballingt3y ago
@nipunn and I repro'd what sounds just like this and were able to fix it with the code above, so I'd want to look at how the code differs so we can either get that fix working for you or find a new repro @RJ and you see this in prod without waiting a week it sounds like?
RJ
RJ3y ago
Correct @ballingt
ballingt
ballingt3y ago
thanks @RJ for walking me through this on a call, there's an issue to fix here. @Dan Mercer I'd still love to see your code or screenshare sometime today if you're available, and to hear how you have token expiration configured in the Auth0 dashboard.
nipunn
nipunn3y ago
Thanks Tom and RJ and freeze!
Dan Mercer
Dan MercerOP3y ago
Here's my code for the Auth0 and Convex providers. It's pretty similar to the snippet above, except the ID token is stored in a state hook so that I only change the Convex client when the token actually changes.
Dan Mercer
Dan MercerOP3y ago
And here are my token expiration settings
No description
ballingt
ballingt3y ago
thanks @Dan Mercer, we're looking at this today
nb
nb3y ago
I'm getting something similar. When I inspect the ID token returned by react Auth0 in jwt.io, I see that it's expired, even though isAuthenticated() returns true. My theory (this is my first time using Auth0!) is that this is normal: https://community.auth0.com/t/id-token-expiration-check/76453/3, but the convex server for some reason demands a non-expired token. Changing the call to getAccessTokenSilently() in ConvexProviderUsingAuth0 to add cacheMode: 'off' forces Auth0 to get a new token each time. It seems these default to 36000 seconds or 10 hours, so this only appears 10 hours after authenticating. This seems like a maybe a bug with Convex? I think these tokens are supposed to be used once at login to verify the user is who they are, and then that session is supposed to persist on the client/server without requiring revalidation of the token. There might be a different Auth0 API needed for server side checks?
nipunn
nipunn3y ago
Hi - just to make sure I'm looking at the right thing, what version of Convex are you using? IE what version is in your package.json
nb
nb3y ago
0.9.0
nipunn
nipunn3y ago
Using ConvexProviderUsingAuth0 on v0.9.0 should automatically refresh the token periodically
nipunn
nipunn3y ago
UNPKG - convex
The CDN for convex
nb
nb3y ago
Ah OK, there might be something weird with my setup. I'm using @auth0/auth0-react @ 2.0.0 and had to change ConvexProviderWithAuth0 to pass redirect_uri like this: { domain, clientId: authInfo.applicationID, authorizationParams: { redirect_uri: typeof window === "undefined" ? void 0 : window.location.origin, }, cacheLocation: "localstorage" }, instead of at the top level of the dictionary to get auth/redirect to work. That might be a sign that something's wrong with how I set up my repo / I'm using an old version / etc. Let me look into this and follow up. RE: that line, I think Auth0 returns a cached and expired id token there.
nipunn
nipunn3y ago
interesting let me see if I can find some docs on getAccessTokenSilently to understand what's going on when we tested it originally, it appeared to check if the existing token was valid and return it if so. It sounds like you're observing an expired token getting returned here?
nipunn
nipunn3y ago
It says in the docs that it should always be unexpired: If there's a valid token stored, return it. Otherwise, opens an iframe with the /authorize URL using the parameters provided as arguments. Random and secure state and nonce parameters will be auto-generated. If the response is successful, results will be valid according to their expiration times. So either the doc is wrong or something is up with the setup. Let's figure it out!
nb
nb3y ago
Yeah, that's right. The comment I linked to above by the Auth0 employee seems to think expired ID tokens are totally fine and it's ok to trust local cache. I wonder if local cache for Auth0 has some sort of a different expiration for session lifetime stored, and just relies on that for when to refetch the token.
nipunn
nipunn3y ago
Thanks for investigating!! We will have to look into this more
nb
nb3y ago
Oh you know, this is probably because auth0-react 2.0.0 just came out last month. That at least explains the redirect_uri thing, not sure if it explains the token expiration. https://github.com/auth0/auth0-react/blob/master/MIGRATION_GUIDE.md
GitHub
auth0-react/MIGRATION_GUIDE.md at master · auth0/auth0-react
Auth0 SDK for React Single Page Applications (SPA) - auth0-react/MIGRATION_GUIDE.md at master · auth0/auth0-react

Did you find this page helpful?