feat(auth): added better error handling for login/signup flow

This commit is contained in:
Waleed Latif
2025-02-16 21:31:43 -08:00
parent eeaf8b0d32
commit 71a66c6a13
3 changed files with 97 additions and 23 deletions

View File

@@ -16,15 +16,16 @@ import {
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { client } from '@/lib/auth-client'
import { useNotificationStore } from '@/stores/notifications/store'
import { NotificationList } from '@/app/w/components/notifications/notifications'
export default function LoginPage() {
const router = useRouter()
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState('')
const { addNotification } = useNotificationStore()
async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
setError('')
setIsLoading(true)
const formData = new FormData(e.currentTarget)
@@ -34,8 +35,27 @@ export default function LoginPage() {
try {
await client.signIn.email({ email, password })
router.push('/w/1')
} catch (err) {
setError('Invalid email or password')
} catch (err: any) {
let errorMessage = 'Invalid email or password'
if (err.message?.includes('not verified')) {
errorMessage =
'Please verify your email before signing in. Check your inbox for the verification link.'
} else if (err.message?.includes('not found')) {
errorMessage = 'No account found with this email. Please sign up first.'
} else if (err.message?.includes('invalid password')) {
errorMessage = 'Invalid password. Please try again or use the forgot password link.'
} else if (err.message?.includes('too many attempts')) {
errorMessage = 'Too many login attempts. Please try again later or reset your password.'
} else if (err.message?.includes('account locked')) {
errorMessage = 'Your account has been locked for security. Please reset your password.'
} else if (err.message?.includes('network')) {
errorMessage = 'Network error. Please check your connection and try again.'
} else if (err.message?.includes('rate limit')) {
errorMessage = 'Too many requests. Please wait a moment before trying again.'
}
addNotification('error', errorMessage, null)
} finally {
setIsLoading(false)
}
@@ -44,21 +64,44 @@ export default function LoginPage() {
async function signInWithGithub() {
try {
await client.signIn.social({ provider: 'github' })
} catch (err) {
setError('Failed to sign in with GitHub')
} catch (err: any) {
let errorMessage = 'Failed to sign in with GitHub'
if (err.message?.includes('account exists')) {
errorMessage =
'An account with this email already exists. Please sign in with email instead.'
} else if (err.message?.includes('cancelled')) {
errorMessage = 'GitHub sign in was cancelled. Please try again.'
} else if (err.message?.includes('network')) {
errorMessage = 'Network error. Please check your connection and try again.'
}
addNotification('error', errorMessage, null)
}
}
async function signInWithGoogle() {
try {
await client.signIn.social({ provider: 'google' })
} catch (err) {
setError('Failed to sign in with Google')
} catch (err: any) {
let errorMessage = 'Failed to sign in with Google'
if (err.message?.includes('account exists')) {
errorMessage =
'An account with this email already exists. Please sign in with email instead.'
} else if (err.message?.includes('cancelled')) {
errorMessage = 'Google sign in was cancelled. Please try again.'
} else if (err.message?.includes('network')) {
errorMessage = 'Network error. Please check your connection and try again.'
}
addNotification('error', errorMessage, null)
}
}
return (
<main className="flex min-h-screen flex-col items-center justify-center bg-gray-50">
<NotificationList />
<div className="sm:mx-auto sm:w-full sm:max-w-md">
<h1 className="text-2xl font-bold text-center mb-8">Sim Studio</h1>
<Card className="w-full">
@@ -102,7 +145,6 @@ export default function LoginPage() {
<Label htmlFor="password">Password</Label>
<Input id="password" name="password" type="password" required />
</div>
{error && <p className="text-sm text-red-500">{error}</p>}
<Button type="submit" className="w-full" disabled={isLoading}>
{isLoading ? 'Signing in...' : 'Sign in'}
</Button>

View File

@@ -37,13 +37,22 @@ export default function SignupPage() {
await client.signUp.email({ email, password, name })
router.push('/verify-request')
} catch (err: any) {
// Handle specific error messages
let errorMessage = 'Something went wrong. Please try again.'
let errorMessage = 'Failed to create account'
if (err.message?.includes('Password is too short')) {
errorMessage = 'Password must be at least 8 characters long'
} else if (err.message?.includes('existing email')) {
errorMessage = 'An account with this email already exists'
errorMessage = 'An account with this email already exists. Please sign in instead.'
} else if (err.message?.includes('invalid email')) {
errorMessage = 'Please enter a valid email address'
} else if (err.message?.includes('password too long')) {
errorMessage = 'Password must be less than 128 characters'
} else if (err.message?.includes('rate limit')) {
errorMessage = 'Too many signup attempts. Please try again later.'
} else if (err.message?.includes('network')) {
errorMessage = 'Network error. Please check your connection and try again.'
} else if (err.message?.includes('invalid name')) {
errorMessage = 'Please enter a valid name'
}
addNotification('error', errorMessage, null)
@@ -55,16 +64,40 @@ export default function SignupPage() {
async function signUpWithGithub() {
try {
await client.signIn.social({ provider: 'github' })
} catch (err) {
addNotification('error', 'Failed to sign up with GitHub', null)
} catch (err: any) {
let errorMessage = 'Failed to sign up with GitHub'
if (err.message?.includes('account exists')) {
errorMessage = 'An account with this email already exists. Please sign in instead.'
} else if (err.message?.includes('cancelled')) {
errorMessage = 'GitHub sign up was cancelled. Please try again.'
} else if (err.message?.includes('network')) {
errorMessage = 'Network error. Please check your connection and try again.'
} else if (err.message?.includes('rate limit')) {
errorMessage = 'Too many attempts. Please try again later.'
}
addNotification('error', errorMessage, null)
}
}
async function signUpWithGoogle() {
try {
await client.signIn.social({ provider: 'google' })
} catch (err) {
addNotification('error', 'Failed to sign up with Google', null)
} catch (err: any) {
let errorMessage = 'Failed to sign up with Google'
if (err.message?.includes('account exists')) {
errorMessage = 'An account with this email already exists. Please sign in instead.'
} else if (err.message?.includes('cancelled')) {
errorMessage = 'Google sign up was cancelled. Please try again.'
} else if (err.message?.includes('network')) {
errorMessage = 'Network error. Please check your connection and try again.'
} else if (err.message?.includes('rate limit')) {
errorMessage = 'Too many attempts. Please try again later.'
}
addNotification('error', errorMessage, null)
}
}

View File

@@ -6,11 +6,6 @@ import { Resend } from 'resend'
import { db } from '@/db'
import * as schema from '@/db/schema'
type EmailHandler = {
user: { email: string }
url: string
}
// If there is no resend key, it might be a local dev environment
// In that case, we don't want to send emails and just log them
const resend = process.env.RESEND_API_KEY
@@ -35,8 +30,8 @@ export const auth = betterAuth({
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
sendResetPassword: async ({ user, url }: EmailHandler) => {
await resend.emails.send({
sendResetPassword: async ({ user, url, token }, request) => {
const result = await resend.emails.send({
from: 'Sim Studio <team@simstudio.ai>',
to: user.email,
subject: 'Reset your password',
@@ -47,6 +42,10 @@ export const auth = betterAuth({
<p>If you didn't request this, you can safely ignore this email.</p>
`,
})
if (!result) {
throw new Error('Failed to send reset password email')
}
},
},
emailVerification: {