refactor(auth): replace 2FA string sentinel with typed error

Use a dedicated TwoFactorRequiredError and shared type guard to keep login flow checks type-safe and less brittle than matching magic strings.

Made-with: Cursor
This commit is contained in:
Ubuntu
2026-04-30 18:07:12 +00:00
parent 0b4d4f811e
commit 4f5521a6cf
4 changed files with 21 additions and 9 deletions
+6 -5
View File
@@ -1,6 +1,10 @@
import { api } from '@/lib/api'
import { resetHeicodeAuthenticatedSession } from '@/features/auth/heicode-authenticated-session'
import { saveUserId } from '@/features/auth/lib/storage'
import {
isTwoFactorRequiredError,
TwoFactorRequiredError,
} from '@/features/auth/errors'
import type {
LoginPayload,
LoginResponse,
@@ -72,7 +76,7 @@ async function establishManagerCookieSession(payload: LoginPayload) {
throw new Error(body?.message || 'Unable to establish Manager session')
}
if (body.data?.require_2fa) {
throw new Error('TWO_FACTOR_REQUIRED')
throw new TwoFactorRequiredError()
}
if (body.data?.id != null) {
saveUserId(body.data.id)
@@ -151,10 +155,7 @@ export async function login(payload: LoginPayload) {
try {
await establishManagerCookieSession(payload)
} catch (syncErr) {
if (
syncErr instanceof Error &&
syncErr.message === 'TWO_FACTOR_REQUIRED'
) {
if (isTwoFactorRequiredError(syncErr)) {
throw syncErr
}
clearHeicodeTokens()
+12
View File
@@ -0,0 +1,12 @@
export class TwoFactorRequiredError extends Error {
constructor() {
super('Two-factor authentication is required')
this.name = 'TwoFactorRequiredError'
}
}
export function isTwoFactorRequiredError(
error: unknown
): error is TwoFactorRequiredError {
return error instanceof TwoFactorRequiredError
}
+1
View File
@@ -71,6 +71,7 @@ export {
getAffiliateCode,
saveAffiliateCode,
} from './lib/storage'
export { TwoFactorRequiredError, isTwoFactorRequiredError } from './errors'
export {
isValidOTP,
@@ -22,6 +22,7 @@ import { loginFormSchema } from '@/features/auth/constants'
import { useAuthRedirect } from '@/features/auth/hooks/use-auth-redirect'
import { useTurnstile } from '@/features/auth/hooks/use-turnstile'
import { Turnstile } from '@/components/turnstile'
import { isTwoFactorRequiredError } from '@/features/auth/errors'
import type { AuthFormProps } from '@/features/auth/types'
export function UserAuthForm({
@@ -78,10 +79,7 @@ export function UserAuthForm({
toast.error(res.message)
}
} catch (error) {
if (
error instanceof Error &&
error.message === 'TWO_FACTOR_REQUIRED'
) {
if (isTwoFactorRequiredError(error)) {
redirectTo2FA()
return
}