Is there an expiration of the email verification token?
I need to determine whether the email verification token expires. If it does, I plan to create a dedicated "Account Confirmation" page that indicates when the token has expired and displays a "Resend activation link" button. Additionally, I want to know the duration (in hours) for which the token is valid and how to capture an error if the token has expired.
26 Replies
hello sir @bekacru can you help me?
yes. it does expire in 1 hour. you can configure it under
emailVerification.expiresIn
how about the error, how can I display the error, where can I get the error?
I want to display the error if the token is invalid or it's expired
better auth will add error query param with
token_expired
is this already added?
yes
sir @bekacru this is a very confusing to me, there's a error query param on the forgot password to handle the invalid token, but I can access also the
onError
inside of authClient.resetPassword
so the error query param is useless now?the token_expired is jsut for when the users tries to verify their email
hello sir @bekacru
this is how I handle the email verification manually
but whenever the email verification is successfull it's not redirecting to the
callbackURL
? also I can't get the error
all I can see is the ?error=token_expired
and it is 200
status on my network tab and it's not adding that query params
on the url
.Is this being called in on the client or server (eg server actions)?
Also, is the
verifyEmail
coming from auth.api?I called it on the client side this is from
authClient.verifyEmail
, I don't understand the callbackURL
is this will only trigger if there's something wrong on the validation? what I mean the only purpose of the callbackURL
is to pass the query params
on that route?If the user clicks the provided verification URL, their email is automatically verified, and they are redirected to the callbackURL.
Could it be session token expired? 🤔
Could you try re-signing in, then verify email, and try again? Just to test
I tried to click the verification URL that has been already expired, and I see this on my network tab, as you can see on the picture I got a successfully message even the token has already expired, also on my network tab there's a request
invetory?error=tokent_expired
this is my another route but I'm not redirecting on that route? it's just pass only the query params

that's why I thought the
callbackURL
or redirectTo
is not really going to the route it's just only passing the query params on that callbackURL
or redirectTo
here's how I setup the sendVerificationEmail
function in the auth server instance
@Ping @bekacru
This is what I did to see where the problem is.
First, I tried visiting the default URL of sendVerificationEmail
in the auth.ts
, which is this:
http://localhost:3000/api/auth/verify-email?token=[TOKEN]&callbackURL=/verify-email
.
When I verify my email directly through this URL, the callbackURL
works; it redirects me to that route whether or not the query parameters are present.
But when I use the manual authClient.verifyEmail
on the client side, the redirect for callbackURL
does not work. It sends a request in the network tab, but it does not redirect.In your onSuccess, just redirect them using code.
how about the error? it's not passing the query params at all, or it will pass now if I use router.push on the
onSuccess
@Ping @bekacru
You should just make your email verification flow similar to your reset password flow.
In reset password, the error is caught in onError
when you manually call authClient.resetPassword
.
Unlike the manual call to authClient.verifyEmail
, the HTTP status is still success even if there’s an error in token validation.
For me, I find it a bit challenging to handle the error regarding the query params in manual verify email; it also doesn’t redirect even if there’s a callbackURL
, so the query params never get passed.
That’s why I prefer handling the error in reset password — it doesn’t need a redirect or query params. The error is immediately caught in onError
and it makes it easier to customize the page.But when I use the manual authClient.verifyEmail on the client side, the redirect for callbackURL does not work. It sends a request in the network tab, but it does not redirect.you sohuld redirect them yourself since you're calling it directly you're in contorl people call
verifyEmail
from native mobile and desktop apps. we don't want to return 302 or "redirect" them since that wouldn't make senseI see, I didn't think about that this is also for multiplatform 😅
@bekacru so it's okay that I will use
router.push
or router.refresh
so the error query params will pass on the url?
And why does resetPassword
return an error in onError
when verifyEmail
has the same function to handle token validation? Why is it that for verifyEmail
you don’t return the error in onError
—if you are considering mobile apps and desktop apps.
Also, does the flow for callbackURL
or redirectTo
only work when you directly call api/auth/reset-password
or api/auth/verify-email
? Because that’s what I noticed: when I use the URL from the auth instance itself, the callbackURL
and redirectTo
work; but when I create a custom URL to handle token validation on a custom page and then manually call authClient.resetPassword
or authClient.verifyEmail
, the callbackURL
or redirectTo
don’t work.
@Ping @bekacru I can't get the error on authClient.verifyEmail
even I use router.refresh()
the url
is not changing, is not passing the query params of error.
I'm using a custom url on sendEmailVerification
like this:
Instead the user click the email verification direct to http://localhost:3000/api/auth/verify-email?token
, on this custom url the user will redirect to the custom page to verify manually using authClient.verifyEmail
button.
I think this is a bad DX 🤔 it's better to return an error instead of status 200 even if there's a query params for errorif you call
verifyEmail
manually, it's not going to be added as a query paramSolution
if the token is invalid, it just returns 401 instead
it's not returning as 401, it's returning as 200 that's why I can't handle the error
when there is an error?
I tried to access the error from onError but I didn't get the error even the toke has expired
as you can see here I always got the onSuccess

but in the network tab it said 302? or because of the callbackURL?
oh I see 😂 it's coming from the callbackURL, so when calling the manual verifyEmail I don't need to pass the callbackURL anymore
I just confused by the documentation on
Email
the verifyEmail
has callbackURL
when triggering the manual verify email