Skip to content

Commit da0d8ca

Browse files
committed
fix(errors): fix error handling for signup/signin
1 parent c8b2c9e commit da0d8ca

File tree

3 files changed

+119
-88
lines changed

3 files changed

+119
-88
lines changed

apps/sim/app/(auth)/login/login-form.tsx

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { Input } from '@/components/ui/input'
1010
import { Label } from '@/components/ui/label'
1111
import { client } from '@/lib/auth-client'
1212
import { createLogger } from '@/lib/logs/console-logger'
13-
import { useNotificationStore } from '@/stores/notifications/store'
1413
import { SocialLoginButtons } from '@/app/(auth)/components/social-login-buttons'
1514

1615
const logger = createLogger('LoginForm')
@@ -49,9 +48,10 @@ export default function LoginPage({
4948
const searchParams = useSearchParams()
5049
const [isLoading, setIsLoading] = useState(false)
5150
const [mounted, setMounted] = useState(false)
52-
const { addNotification } = useNotificationStore()
5351
const [showPassword, setShowPassword] = useState(false)
5452
const [password, setPassword] = useState('')
53+
const [passwordErrors, setPasswordErrors] = useState<string[]>([])
54+
const [showValidationError, setShowValidationError] = useState(false)
5555

5656
// Initialize state for URL parameters
5757
const [callbackUrl, setCallbackUrl] = useState('/w')
@@ -121,45 +121,47 @@ export default function LoginPage({
121121
{
122122
onError: (ctx) => {
123123
console.error('Login error:', ctx.error)
124-
let errorMessage = 'Invalid email or password'
124+
let errorMessage: string[] = ['Invalid email or password']
125125

126-
// Handle all possible error cases from Better Auth
127-
if (ctx.error.message?.includes('EMAIL_NOT_VERIFIED')) {
126+
if (ctx.error.code?.includes('EMAIL_NOT_VERIFIED')) {
128127
return
129128
} else if (
130-
ctx.error.message?.includes('BAD_REQUEST') ||
129+
ctx.error.code?.includes('BAD_REQUEST') ||
131130
ctx.error.message?.includes('Email and password sign in is not enabled')
132131
) {
133-
errorMessage = 'Email sign in is currently disabled.'
132+
errorMessage.push('Email sign in is currently disabled.')
134133
} else if (
135-
ctx.error.message?.includes('INVALID_CREDENTIALS') ||
134+
ctx.error.code?.includes('INVALID_CREDENTIALS') ||
136135
ctx.error.message?.includes('invalid password')
137136
) {
138-
errorMessage = 'Invalid email or password. Please try again.'
137+
errorMessage.push('Invalid email or password. Please try again.')
139138
} else if (
140-
ctx.error.message?.includes('USER_NOT_FOUND') ||
139+
ctx.error.code?.includes('USER_NOT_FOUND') ||
141140
ctx.error.message?.includes('not found')
142141
) {
143-
errorMessage = 'No account found with this email. Please sign up first.'
144-
} else if (ctx.error.message?.includes('MISSING_CREDENTIALS')) {
145-
errorMessage = 'Please enter both email and password.'
146-
} else if (ctx.error.message?.includes('EMAIL_PASSWORD_DISABLED')) {
147-
errorMessage = 'Email and password login is disabled.'
148-
} else if (ctx.error.message?.includes('FAILED_TO_CREATE_SESSION')) {
149-
errorMessage = 'Failed to create session. Please try again later.'
150-
} else if (ctx.error.message?.includes('too many attempts')) {
151-
errorMessage =
142+
errorMessage.push('No account found with this email. Please sign up first.')
143+
} else if (ctx.error.code?.includes('MISSING_CREDENTIALS')) {
144+
errorMessage.push('Please enter both email and password.')
145+
} else if (ctx.error.code?.includes('EMAIL_PASSWORD_DISABLED')) {
146+
errorMessage.push('Email and password login is disabled.')
147+
} else if (ctx.error.code?.includes('FAILED_TO_CREATE_SESSION')) {
148+
errorMessage.push('Failed to create session. Please try again later.')
149+
} else if (ctx.error.code?.includes('too many attempts')) {
150+
errorMessage.push(
152151
'Too many login attempts. Please try again later or reset your password.'
153-
} else if (ctx.error.message?.includes('account locked')) {
154-
errorMessage =
152+
)
153+
} else if (ctx.error.code?.includes('account locked')) {
154+
errorMessage.push(
155155
'Your account has been locked for security. Please reset your password.'
156-
} else if (ctx.error.message?.includes('network')) {
157-
errorMessage = 'Network error. Please check your connection and try again.'
156+
)
157+
} else if (ctx.error.code?.includes('network')) {
158+
errorMessage.push('Network error. Please check your connection and try again.')
158159
} else if (ctx.error.message?.includes('rate limit')) {
159-
errorMessage = 'Too many requests. Please wait a moment before trying again.'
160+
errorMessage.push('Too many requests. Please wait a moment before trying again.')
160161
}
161162

162-
addNotification('error', errorMessage, null)
163+
setPasswordErrors(errorMessage)
164+
setShowValidationError(true)
163165
},
164166
}
165167
)
@@ -176,7 +178,7 @@ export default function LoginPage({
176178
}
177179
} catch (err: any) {
178180
// Handle only the special verification case that requires a redirect
179-
if (err.message?.includes('not verified') || err.message?.includes('EMAIL_NOT_VERIFIED')) {
181+
if (err.message?.includes('not verified') || err.code?.includes('EMAIL_NOT_VERIFIED')) {
180182
try {
181183
await client.emailOtp.sendVerificationOtp({
182184
email,
@@ -190,11 +192,8 @@ export default function LoginPage({
190192
router.push(`/verify`)
191193
return
192194
} catch (verifyErr) {
193-
addNotification(
194-
'error',
195-
'Failed to send verification code. Please try again later.',
196-
null
197-
)
195+
setPasswordErrors(['Failed to send verification code. Please try again later.'])
196+
setShowValidationError(true)
198197
setIsLoading(false)
199198
return
200199
}
@@ -308,7 +307,13 @@ export default function LoginPage({
308307
autoCorrect="off"
309308
placeholder="Enter your password"
310309
value={password}
311-
onChange={(e) => setPassword(e.target.value)}
310+
onChange={(e) => {
311+
setPassword(e.target.value)
312+
if (showValidationError) {
313+
setShowValidationError(false)
314+
setPasswordErrors([])
315+
}
316+
}}
312317
className="bg-neutral-900 border-neutral-700 text-white pr-10"
313318
/>
314319
<button
@@ -320,6 +325,13 @@ export default function LoginPage({
320325
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
321326
</button>
322327
</div>
328+
{showValidationError && passwordErrors.length > 0 && (
329+
<div className="text-xs text-red-400 mt-1 space-y-1">
330+
{passwordErrors.map((error, index) => (
331+
<p key={index}>{error}</p>
332+
))}
333+
</div>
334+
)}
323335
</div>
324336
</div>
325337

apps/sim/app/(auth)/signup/signup-form.tsx

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
import { Suspense, useEffect, useState } from 'react'
44
import Link from 'next/link'
55
import { useRouter, useSearchParams } from 'next/navigation'
6-
import { Command, CornerDownLeft, Eye, EyeOff } from 'lucide-react'
6+
import { Eye, EyeOff } from 'lucide-react'
77
import { Button } from '@/components/ui/button'
88
import { Input } from '@/components/ui/input'
99
import { Label } from '@/components/ui/label'
1010
import { client } from '@/lib/auth-client'
11-
import { useNotificationStore } from '@/stores/notifications/store'
11+
import { cn } from '@/lib/utils'
1212
import { SocialLoginButtons } from '@/app/(auth)/components/social-login-buttons'
1313

1414
const PASSWORD_VALIDATIONS = {
@@ -41,12 +41,12 @@ function SignupFormContent({
4141
const searchParams = useSearchParams()
4242
const [isLoading, setIsLoading] = useState(false)
4343
const [, setMounted] = useState(false)
44-
const { addNotification } = useNotificationStore()
4544
const [showPassword, setShowPassword] = useState(false)
4645
const [password, setPassword] = useState('')
4746
const [passwordErrors, setPasswordErrors] = useState<string[]>([])
4847
const [showValidationError, setShowValidationError] = useState(false)
4948
const [email, setEmail] = useState('')
49+
const [emailError, setEmailError] = useState('')
5050
const [waitlistToken, setWaitlistToken] = useState('')
5151
const [redirectUrl, setRedirectUrl] = useState('')
5252
const [isInviteFlow, setIsInviteFlow] = useState(false)
@@ -144,6 +144,14 @@ function SignupFormContent({
144144
setShowValidationError(false)
145145
}
146146

147+
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
148+
setEmail(e.target.value)
149+
// Clear any previous email errors when the user starts typing
150+
if (emailError) {
151+
setEmailError('')
152+
}
153+
}
154+
147155
async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
148156
e.preventDefault()
149157
setIsLoading(true)
@@ -163,7 +171,8 @@ function SignupFormContent({
163171
try {
164172
if (errors.length > 0) {
165173
// Show first error as notification
166-
addNotification('error', errors[0], null)
174+
setPasswordErrors([errors[0]])
175+
setShowValidationError(true)
167176
setIsLoading(false)
168177
return
169178
}
@@ -177,33 +186,42 @@ function SignupFormContent({
177186
{
178187
onError: (ctx) => {
179188
console.error('Signup error:', ctx.error)
180-
let errorMessage = 'Failed to create account'
189+
let errorMessage: string[] = ['Failed to create account']
181190

182-
// Handle all possible error cases from Better Auth
183-
if (ctx.error.status === 422 || ctx.error.message?.includes('already exists')) {
184-
errorMessage = 'An account with this email already exists. Please sign in instead.'
191+
if (ctx.error.code?.includes('USER_ALREADY_EXISTS')) {
192+
errorMessage.push(
193+
'An account with this email already exists. Please sign in instead.'
194+
)
195+
setEmailError(errorMessage[0])
185196
} else if (
186-
ctx.error.message?.includes('BAD_REQUEST') ||
197+
ctx.error.code?.includes('BAD_REQUEST') ||
187198
ctx.error.message?.includes('Email and password sign up is not enabled')
188199
) {
189-
errorMessage = 'Email signup is currently disabled.'
190-
} else if (ctx.error.message?.includes('INVALID_EMAIL')) {
191-
errorMessage = 'Please enter a valid email address.'
192-
} else if (ctx.error.message?.includes('PASSWORD_TOO_SHORT')) {
193-
errorMessage = 'Password must be at least 8 characters long.'
194-
} else if (ctx.error.message?.includes('MISSING_CREDENTIALS')) {
195-
errorMessage = 'Please enter all required fields.'
196-
} else if (ctx.error.message?.includes('EMAIL_PASSWORD_DISABLED')) {
197-
errorMessage = 'Email and password signup is disabled.'
198-
} else if (ctx.error.message?.includes('FAILED_TO_CREATE_USER')) {
199-
errorMessage = 'Failed to create account. Please try again later.'
200-
} else if (ctx.error.message?.includes('network')) {
201-
errorMessage = 'Network error. Please check your connection and try again.'
202-
} else if (ctx.error.message?.includes('rate limit')) {
203-
errorMessage = 'Too many requests. Please wait a moment before trying again.'
200+
errorMessage.push('Email signup is currently disabled.')
201+
setEmailError(errorMessage[0])
202+
} else if (ctx.error.code?.includes('INVALID_EMAIL')) {
203+
errorMessage.push('Please enter a valid email address.')
204+
setEmailError(errorMessage[0])
205+
} else if (ctx.error.code?.includes('PASSWORD_TOO_SHORT')) {
206+
errorMessage.push('Password must be at least 8 characters long.')
207+
setPasswordErrors(errorMessage)
208+
setShowValidationError(true)
209+
} else if (ctx.error.code?.includes('PASSWORD_TOO_LONG')) {
210+
errorMessage.push('Password must be less than 128 characters long.')
211+
setPasswordErrors(errorMessage)
212+
setShowValidationError(true)
213+
} else if (ctx.error.code?.includes('network')) {
214+
errorMessage.push('Network error. Please check your connection and try again.')
215+
setPasswordErrors(errorMessage)
216+
setShowValidationError(true)
217+
} else if (ctx.error.code?.includes('rate limit')) {
218+
errorMessage.push('Too many requests. Please wait a moment before trying again.')
219+
setPasswordErrors(errorMessage)
220+
setShowValidationError(true)
221+
} else {
222+
setPasswordErrors(errorMessage)
223+
setShowValidationError(true)
204224
}
205-
206-
addNotification('error', errorMessage, null)
207225
},
208226
}
209227
)
@@ -255,7 +273,8 @@ function SignupFormContent({
255273
router.push('/verify')
256274
} catch (error) {
257275
console.error('Failed to send verification code:', error)
258-
addNotification('error', 'Account created but failed to send verification code.', null)
276+
setPasswordErrors(['Account created but failed to send verification code.'])
277+
setShowValidationError(true)
259278
router.push('/login')
260279
}
261280
} catch (error) {
@@ -304,9 +323,17 @@ function SignupFormContent({
304323
autoComplete="email"
305324
autoCorrect="off"
306325
value={email}
307-
onChange={(e) => setEmail(e.target.value)}
308-
className="bg-neutral-900 border-neutral-700 text-white"
326+
onChange={handleEmailChange}
327+
className={cn(
328+
'bg-neutral-900 border-neutral-700 text-white',
329+
emailError && 'border-red-500 focus-visible:ring-red-500'
330+
)}
309331
/>
332+
{emailError && (
333+
<div className="text-xs text-red-400 mt-1">
334+
<p>{emailError}</p>
335+
</div>
336+
)}
310337
</div>
311338
<div className="space-y-2">
312339
<Label htmlFor="password" className="text-neutral-300">

apps/sim/app/w/components/sidebar/sidebar.tsx

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { HelpCircle, ScrollText, Send, Settings } from 'lucide-react'
77
import { Skeleton } from '@/components/ui/skeleton'
88
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
99
import { useSession } from '@/lib/auth-client'
10-
import { isProd } from '@/lib/environment'
1110
import { useSidebarStore } from '@/stores/sidebar/store'
1211
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
1312
import { WorkflowMetadata } from '@/stores/workflows/registry/types'
@@ -157,9 +156,6 @@ export function Sidebar() {
157156
mode === 'hover' &&
158157
((isHovered && !isAnyModalOpen && explicitMouseEnter) || workspaceDropdownOpen)
159158

160-
// Invite members is only shown in production
161-
const shouldShowInviteMembers = isProd
162-
163159
return (
164160
<aside
165161
className={clsx(
@@ -276,19 +272,17 @@ export function Sidebar() {
276272
<div className="flex-shrink-0 px-3 pb-3 pt-1">
277273
<div className="flex flex-col space-y-[1px]">
278274
{/* Invite members button */}
279-
{shouldShowInviteMembers && (
280-
<Tooltip>
281-
<TooltipTrigger asChild>
282-
<div
283-
onClick={() => setShowInviteMembers(true)}
284-
className="flex items-center justify-center rounded-md text-sm font-medium text-muted-foreground hover:bg-accent/50 cursor-pointer w-8 h-8 mx-auto"
285-
>
286-
<Send className="h-[18px] w-[18px]" />
287-
</div>
288-
</TooltipTrigger>
289-
<TooltipContent side="right">Invite Members</TooltipContent>
290-
</Tooltip>
291-
)}
275+
<Tooltip>
276+
<TooltipTrigger asChild>
277+
<div
278+
onClick={() => setShowInviteMembers(true)}
279+
className="flex items-center justify-center rounded-md text-sm font-medium text-muted-foreground hover:bg-accent/50 cursor-pointer w-8 h-8 mx-auto"
280+
>
281+
<Send className="h-[18px] w-[18px]" />
282+
</div>
283+
</TooltipTrigger>
284+
<TooltipContent side="right">Invite Members</TooltipContent>
285+
</Tooltip>
292286

293287
{/* Help button */}
294288
<Tooltip>
@@ -315,17 +309,15 @@ export function Sidebar() {
315309
) : (
316310
<>
317311
{/* Invite members bar */}
318-
{shouldShowInviteMembers && (
319-
<div className="flex-shrink-0 px-3 pt-1">
320-
<div
321-
onClick={() => setShowInviteMembers(true)}
322-
className="flex items-center rounded-md px-2 py-1.5 text-sm font-medium text-muted-foreground hover:bg-accent/50 cursor-pointer"
323-
>
324-
<Send className="h-[18px] w-[18px]" />
325-
<span className="ml-2">Invite members</span>
326-
</div>
312+
<div className="flex-shrink-0 px-3 pt-1">
313+
<div
314+
onClick={() => setShowInviteMembers(true)}
315+
className="flex items-center rounded-md px-2 py-1.5 text-sm font-medium text-muted-foreground hover:bg-accent/50 cursor-pointer"
316+
>
317+
<Send className="h-[18px] w-[18px]" />
318+
<span className="ml-2">Invite members</span>
327319
</div>
328-
)}
320+
</div>
329321

330322
{/* Bottom buttons container */}
331323
<div className="flex-shrink-0 px-3 pb-3 pt-1">

0 commit comments

Comments
 (0)