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:
+6
-5
@@ -1,6 +1,10 @@
|
|||||||
import { api } from '@/lib/api'
|
import { api } from '@/lib/api'
|
||||||
import { resetHeicodeAuthenticatedSession } from '@/features/auth/heicode-authenticated-session'
|
import { resetHeicodeAuthenticatedSession } from '@/features/auth/heicode-authenticated-session'
|
||||||
import { saveUserId } from '@/features/auth/lib/storage'
|
import { saveUserId } from '@/features/auth/lib/storage'
|
||||||
|
import {
|
||||||
|
isTwoFactorRequiredError,
|
||||||
|
TwoFactorRequiredError,
|
||||||
|
} from '@/features/auth/errors'
|
||||||
import type {
|
import type {
|
||||||
LoginPayload,
|
LoginPayload,
|
||||||
LoginResponse,
|
LoginResponse,
|
||||||
@@ -72,7 +76,7 @@ async function establishManagerCookieSession(payload: LoginPayload) {
|
|||||||
throw new Error(body?.message || 'Unable to establish Manager session')
|
throw new Error(body?.message || 'Unable to establish Manager session')
|
||||||
}
|
}
|
||||||
if (body.data?.require_2fa) {
|
if (body.data?.require_2fa) {
|
||||||
throw new Error('TWO_FACTOR_REQUIRED')
|
throw new TwoFactorRequiredError()
|
||||||
}
|
}
|
||||||
if (body.data?.id != null) {
|
if (body.data?.id != null) {
|
||||||
saveUserId(body.data.id)
|
saveUserId(body.data.id)
|
||||||
@@ -151,10 +155,7 @@ export async function login(payload: LoginPayload) {
|
|||||||
try {
|
try {
|
||||||
await establishManagerCookieSession(payload)
|
await establishManagerCookieSession(payload)
|
||||||
} catch (syncErr) {
|
} catch (syncErr) {
|
||||||
if (
|
if (isTwoFactorRequiredError(syncErr)) {
|
||||||
syncErr instanceof Error &&
|
|
||||||
syncErr.message === 'TWO_FACTOR_REQUIRED'
|
|
||||||
) {
|
|
||||||
throw syncErr
|
throw syncErr
|
||||||
}
|
}
|
||||||
clearHeicodeTokens()
|
clearHeicodeTokens()
|
||||||
|
|||||||
+12
@@ -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
|
||||||
|
}
|
||||||
@@ -71,6 +71,7 @@ export {
|
|||||||
getAffiliateCode,
|
getAffiliateCode,
|
||||||
saveAffiliateCode,
|
saveAffiliateCode,
|
||||||
} from './lib/storage'
|
} from './lib/storage'
|
||||||
|
export { TwoFactorRequiredError, isTwoFactorRequiredError } from './errors'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
isValidOTP,
|
isValidOTP,
|
||||||
|
|||||||
+2
-4
@@ -22,6 +22,7 @@ import { loginFormSchema } from '@/features/auth/constants'
|
|||||||
import { useAuthRedirect } from '@/features/auth/hooks/use-auth-redirect'
|
import { useAuthRedirect } from '@/features/auth/hooks/use-auth-redirect'
|
||||||
import { useTurnstile } from '@/features/auth/hooks/use-turnstile'
|
import { useTurnstile } from '@/features/auth/hooks/use-turnstile'
|
||||||
import { Turnstile } from '@/components/turnstile'
|
import { Turnstile } from '@/components/turnstile'
|
||||||
|
import { isTwoFactorRequiredError } from '@/features/auth/errors'
|
||||||
import type { AuthFormProps } from '@/features/auth/types'
|
import type { AuthFormProps } from '@/features/auth/types'
|
||||||
|
|
||||||
export function UserAuthForm({
|
export function UserAuthForm({
|
||||||
@@ -78,10 +79,7 @@ export function UserAuthForm({
|
|||||||
toast.error(res.message)
|
toast.error(res.message)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (
|
if (isTwoFactorRequiredError(error)) {
|
||||||
error instanceof Error &&
|
|
||||||
error.message === 'TWO_FACTOR_REQUIRED'
|
|
||||||
) {
|
|
||||||
redirectTo2FA()
|
redirectTo2FA()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user