Skip to content

Commit 93f9293

Browse files
authored
fix(verification): add OTP dev skip (#1395)
1 parent b5570c1 commit 93f9293

File tree

7 files changed

+46
-27
lines changed

7 files changed

+46
-27
lines changed
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { env } from '@/lib/env'
1+
import { hasEmailService } from '@/lib/email/mailer'
22
import { isProd } from '@/lib/environment'
33
import { VerifyContent } from '@/app/(auth)/verify/verify-content'
44

55
export const dynamic = 'force-dynamic'
66

77
export default function VerifyPage() {
8-
const hasResendKey = Boolean(env.RESEND_API_KEY)
8+
const emailServiceConfigured = hasEmailService()
99

10-
return <VerifyContent hasResendKey={hasResendKey} isProduction={isProd} />
10+
return <VerifyContent hasEmailService={emailServiceConfigured} isProduction={isProd} />
1111
}

apps/sim/app/(auth)/verify/use-verification.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { createLogger } from '@/lib/logs/console/logger'
88
const logger = createLogger('useVerification')
99

1010
interface UseVerificationParams {
11-
hasResendKey: boolean
11+
hasEmailService: boolean
1212
isProduction: boolean
1313
}
1414

@@ -20,15 +20,15 @@ interface UseVerificationReturn {
2020
isInvalidOtp: boolean
2121
errorMessage: string
2222
isOtpComplete: boolean
23-
hasResendKey: boolean
23+
hasEmailService: boolean
2424
isProduction: boolean
2525
verifyCode: () => Promise<void>
2626
resendCode: () => void
2727
handleOtpChange: (value: string) => void
2828
}
2929

3030
export function useVerification({
31-
hasResendKey,
31+
hasEmailService,
3232
isProduction,
3333
}: UseVerificationParams): UseVerificationReturn {
3434
const router = useRouter()
@@ -74,10 +74,10 @@ export function useVerification({
7474
}, [searchParams])
7575

7676
useEffect(() => {
77-
if (email && !isSendingInitialOtp && hasResendKey) {
77+
if (email && !isSendingInitialOtp && hasEmailService) {
7878
setIsSendingInitialOtp(true)
7979
}
80-
}, [email, isSendingInitialOtp, hasResendKey])
80+
}, [email, isSendingInitialOtp, hasEmailService])
8181

8282
const isOtpComplete = otp.length === 6
8383

@@ -157,7 +157,7 @@ export function useVerification({
157157
}
158158

159159
function resendCode() {
160-
if (!email || !hasResendKey) return
160+
if (!email || !hasEmailService) return
161161

162162
setIsLoading(true)
163163
setErrorMessage('')
@@ -197,17 +197,27 @@ export function useVerification({
197197

198198
useEffect(() => {
199199
if (typeof window !== 'undefined') {
200-
if (!isProduction || !hasResendKey) {
200+
if (!isProduction && !hasEmailService) {
201201
setIsVerified(true)
202202

203-
const timeoutId = setTimeout(() => {
204-
window.location.href = '/workspace'
205-
}, 1000)
203+
const handleRedirect = async () => {
204+
try {
205+
await refetchSession()
206+
} catch (error) {
207+
logger.warn('Failed to refetch session during dev verification skip:', error)
208+
}
209+
210+
if (isInviteFlow && redirectUrl) {
211+
window.location.href = redirectUrl
212+
} else {
213+
router.push('/workspace')
214+
}
215+
}
206216

207-
return () => clearTimeout(timeoutId)
217+
handleRedirect()
208218
}
209219
}
210-
}, [isProduction, hasResendKey, router])
220+
}, [isProduction, hasEmailService, router, isInviteFlow, redirectUrl])
211221

212222
return {
213223
otp,
@@ -217,7 +227,7 @@ export function useVerification({
217227
isInvalidOtp,
218228
errorMessage,
219229
isOtpComplete,
220-
hasResendKey,
230+
hasEmailService,
221231
isProduction,
222232
verifyCode,
223233
resendCode,

apps/sim/app/(auth)/verify/verify-content.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ import { inter } from '@/app/fonts/inter'
1010
import { soehne } from '@/app/fonts/soehne/soehne'
1111

1212
interface VerifyContentProps {
13-
hasResendKey: boolean
13+
hasEmailService: boolean
1414
isProduction: boolean
1515
}
1616

1717
function VerificationForm({
18-
hasResendKey,
18+
hasEmailService,
1919
isProduction,
2020
}: {
21-
hasResendKey: boolean
21+
hasEmailService: boolean
2222
isProduction: boolean
2323
}) {
2424
const {
@@ -32,7 +32,7 @@ function VerificationForm({
3232
verifyCode,
3333
resendCode,
3434
handleOtpChange,
35-
} = useVerification({ hasResendKey, isProduction })
35+
} = useVerification({ hasEmailService, isProduction })
3636

3737
const [countdown, setCountdown] = useState(0)
3838
const [isResendDisabled, setIsResendDisabled] = useState(false)
@@ -93,7 +93,7 @@ function VerificationForm({
9393
<p className={`${inter.className} font-[380] text-[16px] text-muted-foreground`}>
9494
{isVerified
9595
? 'Your email has been verified. Redirecting to dashboard...'
96-
: hasResendKey
96+
: hasEmailService
9797
? `A verification code has been sent to ${email || 'your email'}`
9898
: !isProduction
9999
? 'Development mode: Check your console logs for the verification code'
@@ -106,7 +106,7 @@ function VerificationForm({
106106
<div className='space-y-6'>
107107
<p className='text-center text-muted-foreground text-sm'>
108108
Enter the 6-digit code to verify your account.
109-
{hasResendKey ? " If you don't see it in your inbox, check your spam folder." : ''}
109+
{hasEmailService ? " If you don't see it in your inbox, check your spam folder." : ''}
110110
</p>
111111

112112
<div className='flex justify-center'>
@@ -192,7 +192,7 @@ function VerificationForm({
192192
{isLoading ? 'Verifying...' : 'Verify Email'}
193193
</Button>
194194

195-
{hasResendKey && (
195+
{hasEmailService && (
196196
<div className='text-center'>
197197
<p className='text-muted-foreground text-sm'>
198198
Didn't receive a code?{' '}
@@ -245,10 +245,10 @@ function VerificationFormFallback() {
245245
)
246246
}
247247

248-
export function VerifyContent({ hasResendKey, isProduction }: VerifyContentProps) {
248+
export function VerifyContent({ hasEmailService, isProduction }: VerifyContentProps) {
249249
return (
250250
<Suspense fallback={<VerificationFormFallback />}>
251-
<VerificationForm hasResendKey={hasResendKey} isProduction={isProduction} />
251+
<VerificationForm hasEmailService={hasEmailService} isProduction={isProduction} />
252252
</Suspense>
253253
)
254254
}

apps/sim/lib/auth.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
handleInvoicePaymentFailed,
3333
handleInvoicePaymentSucceeded,
3434
} from '@/lib/billing/webhooks/invoices'
35-
import { sendEmail } from '@/lib/email/mailer'
35+
import { hasEmailService, sendEmail } from '@/lib/email/mailer'
3636
import { getFromEmailAddress } from '@/lib/email/utils'
3737
import { quickValidateEmail } from '@/lib/email/validation'
3838
import { env, isTruthy } from '@/lib/env'
@@ -147,7 +147,7 @@ export const auth = betterAuth({
147147
},
148148
emailAndPassword: {
149149
enabled: true,
150-
requireEmailVerification: isProd,
150+
requireEmailVerification: isProd && hasEmailService(),
151151
sendVerificationOnSignUp: false,
152152
throwOnMissingCredentials: true,
153153
throwOnInvalidCredentials: true,

apps/sim/lib/email/mailer.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ const azureEmailClient =
6969
? new EmailClient(azureConnectionString)
7070
: null
7171

72+
/**
73+
* Check if any email service is configured and available
74+
*/
75+
export function hasEmailService(): boolean {
76+
return !!(resend || azureEmailClient)
77+
}
78+
7279
export async function sendEmail(options: EmailOptions): Promise<SendEmailResult> {
7380
try {
7481
// Check if user has unsubscribed (skip for critical transactional emails)

docker-compose.local.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ services:
1010
limits:
1111
memory: 8G
1212
environment:
13+
- NODE_ENV=development
1314
- DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-simstudio}
1415
- BETTER_AUTH_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
1516
- NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}

docker-compose.prod.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ services:
99
limits:
1010
memory: 8G
1111
environment:
12+
- NODE_ENV=production
1213
- DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-simstudio}
1314
- BETTER_AUTH_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
1415
- NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}

0 commit comments

Comments
 (0)