gioru
gioru•4mo ago

Need help with handling Convex Auth signIn errors in prod

I'm using Convex Auth and during dev I'm handling signin errors like this:
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
if (!validateForm()) return

setSubmitting(true)
try {
const formDataToSend = new FormData()
formDataToSend.append("email", formData.email)
formDataToSend.append("password", formData.password)
formDataToSend.append("flow", flow)

const { signingIn } = await signIn(provider ?? "password", formDataToSend)
if (!signingIn) {
handleSent?.(formData.email)
}
} catch (error: any) {
console.error({ error })
let title = "An error occurred"
let description = ""

if (error.message.includes('InvalidAccountId')) {
title = flow === "signIn" ? "Account not found" : "Email already registered"
description = flow === "signIn" ? "Maybe you meant to sign up?" : "Maybe you meant to log in?"
} else if (error.message.includes('InvalidSecret')) {
title = "Incorrect password"
description = "Check your password or reset it"
} else if (error.message.includes('TooManyFailedAttempts')) {
title = "Too many attempts"
description = "Try again later or reset your password"
} else if (error.message.includes(`Account ${formData.email} already exists`)) {
title = "Account already registered"
description = "Maybe you meant to log in?"
}

toast({ title, description, variant: "destructive" })
} finally {
setSubmitting(false)
}
}
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
if (!validateForm()) return

setSubmitting(true)
try {
const formDataToSend = new FormData()
formDataToSend.append("email", formData.email)
formDataToSend.append("password", formData.password)
formDataToSend.append("flow", flow)

const { signingIn } = await signIn(provider ?? "password", formDataToSend)
if (!signingIn) {
handleSent?.(formData.email)
}
} catch (error: any) {
console.error({ error })
let title = "An error occurred"
let description = ""

if (error.message.includes('InvalidAccountId')) {
title = flow === "signIn" ? "Account not found" : "Email already registered"
description = flow === "signIn" ? "Maybe you meant to sign up?" : "Maybe you meant to log in?"
} else if (error.message.includes('InvalidSecret')) {
title = "Incorrect password"
description = "Check your password or reset it"
} else if (error.message.includes('TooManyFailedAttempts')) {
title = "Too many attempts"
description = "Try again later or reset your password"
} else if (error.message.includes(`Account ${formData.email} already exists`)) {
title = "Account already registered"
description = "Maybe you meant to log in?"
}

toast({ title, description, variant: "destructive" })
} finally {
setSubmitting(false)
}
}
I noticed this doesn't work in production because the errors lose any kind of descriptions when they arrive to the client. Now, what's the correct way to handle auth errors (like user logins but it is not registered, the password is wrong, etc)? Thank you
20 Replies
ballingt
ballingt•4mo ago
As you've seen, error messages are stripped of this kind of debugging info in production. This is for security, to make sure just any error that throws in the backend doesn't reveal information that shouldn't be revealed.
ballingt
ballingt•4mo ago
GitHub
convex-auth/src/server/implementation.ts at main · get-convex/conve...
Library for built-in auth. Contribute to get-convex/convex-auth development by creating an account on GitHub.
ballingt
ballingt•4mo ago
One approach is to change these all into ConvexErrors, but we need to figure out in which situations that's safe to do.
ballingt
ballingt•4mo ago
Application Errors | Convex Developer Hub
If you have expected ways your functions might fail, you can either return
ballingt
ballingt•4mo ago
Another would be to catch these errors on the server and then return a member of a type union describing the issue Since this implementation.js file is part of the library this is a library change; this is work that needs to be done still. @gioru is there a format you'd want these in? Is catching errors how you'd like to do it, or are you just doing that because it was the only way?
cameronm
cameronm•4mo ago
@ballingt @gioru I ran into a similar issue. Even on the server, the errors returned are difficult to handle in try catch blocks as they are just the standard Error class. Sometimes the message is like a code, for instance 'InvalidSecret', but its inconsistent Would be nice even if these were custom named Error classes to check with instanceof to better handle some of the expected error types like @gioru is checking with string checks for example if (error instanceof ConvexAuthTooManyFailedAttemptsError) A little verbose, but you get what I mean. Or create a custom ConvexAuthError or ConvexServerError class extending Error with some additional details like a code that can be documented somewhere in the docs so we have a reference of expected errors to handle
ballingt
ballingt•4mo ago
instanceof doesn't work well with serializing errors to the browser so we tend to encourage typing the data associated with the ConvexError instead for anything that has to go over the wire But for errors that are dealt with within this code, yeah custom ConvexAuthBlahBLahBlah errors would work well This is (probably obviously) part of the codebase that isn't finished yet, we focused on getting the security stuff correct first but now this is high on the list for next things to do. Whatever it is ideally it'll be something that you can exhaustively check with TypeScript, so either a union of Error types or a single error with a field that has these messages on it Feel free to file an issue here if you want to be updated on progress https://github.com/get-convex/convex-auth/issues
cameronm
cameronm•4mo ago
@ballingt yes, agree with the instanceof. Should have added I am mainly doing this with an HTTP Api using HTTP Actions and not a Convex Client. So I am handling all errors on the server and just returning the appropriate responses. The client solution is definitely a trickier one with the reactivity and not exposing sensitive information.
gioru
gioruOP•4mo ago
Tried the api route way but had no success since useAuthActions can be called only inside react components. Tried the signIn convex server function too but wasn't enough to make the signin flow work. 🥲
http.route({
path: '/sign-in',
method: 'POST',
handler: httpAction(async (ctx, request) => {
const { provider, params } = await request.json();
const { signIn } = useAuthActions()

try {
const signinResult = await signIn(provider, params)

return new Response(JSON.stringify(signinResult), {
status: 200,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
}
})
} catch (error: any) {
return new Response(error, { status: 400 })
}
})
})

http.route({
path: '/sign-in',
method: 'OPTIONS',
handler: httpAction(async (ctx, request) => {
return new Response(null, {
status: 204,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
}
})
})
})
http.route({
path: '/sign-in',
method: 'POST',
handler: httpAction(async (ctx, request) => {
const { provider, params } = await request.json();
const { signIn } = useAuthActions()

try {
const signinResult = await signIn(provider, params)

return new Response(JSON.stringify(signinResult), {
status: 200,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
}
})
} catch (error: any) {
return new Response(error, { status: 400 })
}
})
})

http.route({
path: '/sign-in',
method: 'OPTIONS',
handler: httpAction(async (ctx, request) => {
return new Response(null, {
status: 204,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
}
})
})
})
smaccoun
smaccoun•2mo ago
Has anyone found an example of this where someone cleanly handles auth errors? For example incorrect password. Handling application (expected) errors is quite important in signup
dowski
dowski•2mo ago
here's an example of handling invalid password using ConvexError: https://github.com/get-convex/convex-auth/commit/c95f02700207a7679189ad3fbb1765512e085615
GitHub
Handle password validation failure in the custom sign-up example (#...
Handle password validation failure in the custom sign-up example Uses a shared constant value between the backend auth config and the frontend and sends it in a ConvexError when password validat...
dowski
dowski•2mo ago
it's not a holistic solution to the error handling problem though
ballingt
ballingt•2mo ago
Oh nice! @smaccoun is this the kind of thing you're looking for, incorrect password during signup?
smaccoun
smaccoun•2mo ago
Yup this is on the right track! But as mentioned would be good to have a full enum list to match over, but this is a great start and at least ahndles the invalid password case which is what i was testing
ballingt
ballingt•2mo ago
@smaccoun I wrote a bit about this in https://github.com/get-convex/convex-auth/issues/124, the enum list of failures makes sense but is a higher level interface than anything Convex Auth provides today. It's a good idea but would be a bigger change, the more likely change in the short term is adding a callback to allow you to build your own enum.
Eva
Eva•2d ago
I'm trying to figure out how to display a simple "email does not exist" message for users who sign in with an invalid ID — I've read through this thread and a few issues on GitHub but it's still unclear. Am I correct in understanding that there's no way to display this message currently without creating a custom provider? I see there's a callback for invalid password handling, but there isn't one for an invalid email, and the errors get stripped from the app in production. Wondering what the most straightforward way is to implement this common error state @Michal Srb do you know if there's a workaround for this right now? Or does it require a library change
ballingt
ballingt•17h ago
That's right, you need a custom provider. An alternative could be for the existing provider to accept options for whether it's ok to reveal that an email does not exist, etc for all the errors that could be thrown.
ballingt
ballingt•17h ago
That discussion on https://github.com/get-convex/convex-auth/issues/124 doesn't list specific ones that would be useful to implement, would love to hear which others you want.
GitHub
Cleanly handling expected errors with Password provider · Issue #12...
There are a couple of posts on the discord about this but doesn't seem to be any answer. In short, it's really unclear how to properly handle expected (application) errors. These types of e...
ballingt
ballingt•17h ago
well it lists too many, everything from Firebase Generally configuration through code seems nice for complex cases, but if it's just a few errors you'd like thrown those could be added to the provider that comes with the library.
Eva
Eva•16h ago
These two basic ones would cover most cases, I imagine: - Account doesn't exist / email not found - Login rate limit exceeded For the password reset flow (these may exist already?, haven't checked): - Code expired - Invalid code