fix: dup/modularisation findings — account gift-card 2FA gate, single-source 2FA predicate, login store delegation
Restart-loop dup/mod review findings: - Account page gift-card buy flow now passes the 2FA verification-code gate end-to-end: TwoFactorCodeInput + Request-a-new-code wired for saved-card/save-card charges, verification_code in the /api/user/giftcards/buy body, 403/429 gate-failure self-heal, buy button gated on missing code (backend BuyGiftCard gate at giftcards.go:1471 already required it — the frontend never sent it) - Removed dead requires2FACodeForSavedCard export from square.ts (zero consumers; all surfaces use authStore.savedCardChargeRequires2FACode) + its test; auth store getter documented as THE single source of truth - Login page now delegates token persistence to authStore.setToken instead of direct localStorage writes (drift-risk closed; setToken persists both tokens identically so the full-reload init still works) - Pence comment corrected (GBP minor unit) - account/+page.svelte:84+6; square.ts -16; square.test.ts -26; auth.svelte.ts comment; login/+page.svelte delegation Frontend 72/72 tests + build clean; backend builds.
This commit is contained in:
@@ -11,7 +11,6 @@ import {
|
||||
isSavedCardVerificationRequired,
|
||||
isTwoFactorVerificationGateFailure,
|
||||
requestNewTwoFactorCode,
|
||||
requires2FACodeForSavedCard,
|
||||
sanitizeDecimalInput,
|
||||
submitPaymentWithRetry
|
||||
} from './square';
|
||||
@@ -220,31 +219,6 @@ describe('payment failure classification', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('requires2FACodeForSavedCard', () => {
|
||||
it('is true when the session user has twoFactorRequired set', () => {
|
||||
expect(requires2FACodeForSavedCard({ twoFactorRequired: true })).toBe(true);
|
||||
});
|
||||
|
||||
it('is false when twoFactorRequired is unset or false', () => {
|
||||
expect(requires2FACodeForSavedCard({ twoFactorRequired: false })).toBe(false);
|
||||
expect(requires2FACodeForSavedCard({})).toBe(false);
|
||||
});
|
||||
|
||||
it('is false for a null/undefined session user', () => {
|
||||
expect(requires2FACodeForSavedCard(null)).toBe(false);
|
||||
expect(requires2FACodeForSavedCard(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it('does not depend on the 2FA setup flag (B6/B10: the code is required per charge)', () => {
|
||||
expect(requires2FACodeForSavedCard({ twoFactorRequired: true, twoFactorEnabled: true })).toBe(
|
||||
true
|
||||
);
|
||||
expect(requires2FACodeForSavedCard({ twoFactorRequired: true, twoFactorEnabled: false })).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isTwoFactorVerificationGateFailure', () => {
|
||||
it.each([
|
||||
[403, 'A two-factor verification code is required to use this saved card', true],
|
||||
|
||||
@@ -118,22 +118,6 @@ export function isSavedCardVerificationRequired(status: number, usedSavedCard: b
|
||||
export const SAVED_CARD_VERIFICATION_MESSAGE =
|
||||
'Your card issuer requires verification. Please pay with a new card or re-add your card.';
|
||||
|
||||
/**
|
||||
* B6/B10: whether charging a saved card requires the customer's current 2FA
|
||||
* verification code. True when the session user has `twoFactorRequired` set —
|
||||
* the profile exposes the backend's `twoFactorEnforced()` posture, so this is
|
||||
* true for every session user in an enforced environment. Charge surfaces show
|
||||
* the verification-code input whenever this is set; the backend additionally
|
||||
* 403s saved-card charges when the code is missing, so surfaces must also
|
||||
* reveal the input on that error. Single source of truth so the booking,
|
||||
* account, tip and admin surfaces can't drift on the gate condition.
|
||||
*/
|
||||
export function requires2FACodeForSavedCard(
|
||||
sessionUser: { twoFactorRequired?: boolean } | null | undefined
|
||||
): boolean {
|
||||
return !!sessionUser?.twoFactorRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* True when a saved-card charge error is a 2FA verification-gate rejection
|
||||
* (backend/handlers/payments/twofa.go): 403 when no code was supplied or the
|
||||
|
||||
@@ -78,7 +78,10 @@ class AuthStore {
|
||||
// yet enabled). The backend keys on the CARD OWNER — which the frontend
|
||||
// usually cannot know — so a 403 for a missing code must also surface the
|
||||
// input. Single source of truth so the predicate can't drift between the
|
||||
// booking, account, tip and admin payment surfaces.
|
||||
// booking, account, tip and admin payment surfaces. This getter is THE
|
||||
// single source of truth for the 2FA gate — the former square.ts helper
|
||||
// (requires2FACodeForSavedCard) was dead code and has been removed, so any
|
||||
// surface gating saved-card charges must read this getter.
|
||||
get savedCardChargeRequires2FACode() {
|
||||
return !!this.user?.twoFactorRequired;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user