Compare commits

..

5 Commits

Author SHA1 Message Date
gpt-engineer-app[bot]
813b15fad5 Fix Permissions-Policy header 2025-11-02 18:44:05 +00:00
gpt-engineer-app[bot]
7435aa8746 feat: Add analytics error handling 2025-11-02 18:40:23 +00:00
gpt-engineer-app[bot]
6d68427f5b Fix outdated Cloudflare Turnstile version 2025-11-02 18:36:20 +00:00
gpt-engineer-app[bot]
f05e31ce38 Fix remaining "Two-Factor" references 2025-11-02 18:32:38 +00:00
gpt-engineer-app[bot]
15b644e45a Refactor: Update MFA terminology 2025-11-02 18:27:11 +00:00
12 changed files with 74 additions and 35 deletions

View File

@@ -46,7 +46,6 @@
<!-- Cloudflare Turnstile - Preconnect for faster captcha loading --> <!-- Cloudflare Turnstile - Preconnect for faster captcha loading -->
<link rel="dns-prefetch" href="https://challenges.cloudflare.com"> <link rel="dns-prefetch" href="https://challenges.cloudflare.com">
<link rel="preconnect" href="https://challenges.cloudflare.com" crossorigin> <link rel="preconnect" href="https://challenges.cloudflare.com" crossorigin>
<link rel="modulepreload" href="https://challenges.cloudflare.com/turnstile/v0/api.js" as="script">
<link rel="dns-prefetch" href="https://cloudflare.com"> <link rel="dns-prefetch" href="https://cloudflare.com">
<link rel="preconnect" href="https://cloudflare.com" crossorigin> <link rel="preconnect" href="https://cloudflare.com" crossorigin>

View File

@@ -9,7 +9,7 @@ import { BrowserRouter, Routes, Route } from "react-router-dom";
import { AuthProvider } from "@/hooks/useAuth"; import { AuthProvider } from "@/hooks/useAuth";
import { AuthModalProvider } from "@/contexts/AuthModalContext"; import { AuthModalProvider } from "@/contexts/AuthModalContext";
import { LocationAutoDetectProvider } from "@/components/providers/LocationAutoDetectProvider"; import { LocationAutoDetectProvider } from "@/components/providers/LocationAutoDetectProvider";
import { Analytics } from "@vercel/analytics/react"; import { AnalyticsWrapper } from "@/components/analytics/AnalyticsWrapper";
import { Footer } from "@/components/layout/Footer"; import { Footer } from "@/components/layout/Footer";
import { PageLoader } from "@/components/loading/PageSkeletons"; import { PageLoader } from "@/components/loading/PageSkeletons";
@@ -162,7 +162,7 @@ const App = (): React.JSX.Element => (
</AuthModalProvider> </AuthModalProvider>
</AuthProvider> </AuthProvider>
{import.meta.env.DEV && <ReactQueryDevtools initialIsOpen={false} position="bottom" />} {import.meta.env.DEV && <ReactQueryDevtools initialIsOpen={false} position="bottom" />}
<Analytics /> <AnalyticsWrapper />
</QueryClientProvider> </QueryClientProvider>
); );

View File

@@ -235,7 +235,7 @@ export function AdminUserDeletionDialog({
<DialogHeader> <DialogHeader>
<div className="flex items-center gap-2 justify-center mb-2"> <div className="flex items-center gap-2 justify-center mb-2">
<Shield className="h-6 w-6 text-primary" /> <Shield className="h-6 w-6 text-primary" />
<DialogTitle>MFA Verification Required</DialogTitle> <DialogTitle>Multi-Factor Authentication Verification Required</DialogTitle>
</div> </div>
<DialogDescription className="text-center"> <DialogDescription className="text-center">
This is a critical action that requires additional verification This is a critical action that requires additional verification

View File

@@ -0,0 +1,36 @@
import { Analytics } from "@vercel/analytics/react";
import { Component, ReactNode } from "react";
class AnalyticsErrorBoundary extends Component<
{ children: ReactNode },
{ hasError: boolean }
> {
constructor(props: { children: ReactNode }) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error: Error) {
// Silently fail - analytics should never break the app
console.info('[Analytics] Failed to load, continuing without analytics');
}
render() {
if (this.state.hasError) {
return null;
}
return this.props.children;
}
}
export function AnalyticsWrapper() {
return (
<AnalyticsErrorBoundary>
<Analytics />
</AnalyticsErrorBoundary>
);
}

View File

@@ -61,7 +61,7 @@ export function MFAChallenge({ factorId, onSuccess, onCancel }: MFAChallengeProp
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center gap-2 text-primary"> <div className="flex items-center gap-2 text-primary">
<Shield className="w-5 h-5" /> <Shield className="w-5 h-5" />
<h3 className="font-semibold">Two-Factor Authentication</h3> <h3 className="font-semibold">Multi-Factor Authentication</h3>
</div> </div>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">

View File

@@ -45,8 +45,8 @@ export function MFARemovalDialog({ open, onOpenChange, factorId, onSuccess }: MF
if (currentAal !== 'aal2') { if (currentAal !== 'aal2') {
toast({ toast({
title: 'MFA Required', title: 'Multi-Factor Authentication Required',
description: 'Please verify your identity with MFA before making security changes', description: 'Please verify your identity with Multi-Factor Authentication before making security changes',
variant: 'destructive' variant: 'destructive'
}); });
onOpenChange(false); onOpenChange(false);
@@ -161,8 +161,8 @@ export function MFARemovalDialog({ open, onOpenChange, factorId, onSuccess }: MF
// Phase 2: Check if user's role requires MFA // Phase 2: Check if user's role requires MFA
if (requiresMFA) { if (requiresMFA) {
toast({ toast({
title: 'MFA Required', title: 'Multi-Factor Authentication Required',
description: 'Your role requires two-factor authentication and it cannot be disabled', description: 'Your role requires Multi-Factor Authentication and it cannot be disabled',
variant: 'destructive' variant: 'destructive'
}); });
handleClose(); handleClose();
@@ -182,8 +182,8 @@ export function MFARemovalDialog({ open, onOpenChange, factorId, onSuccess }: MF
if (data?.error) throw new Error(data.error); if (data?.error) throw new Error(data.error);
toast({ toast({
title: 'MFA Disabled', title: 'Multi-Factor Authentication Disabled',
description: 'Two-factor authentication has been disabled' description: 'Multi-Factor Authentication has been disabled'
}); });
handleClose(); handleClose();
onSuccess(); onSuccess();
@@ -204,14 +204,14 @@ export function MFARemovalDialog({ open, onOpenChange, factorId, onSuccess }: MF
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle className="flex items-center gap-2"> <AlertDialogTitle className="flex items-center gap-2">
<Shield className="h-5 w-5 text-destructive" /> <Shield className="h-5 w-5 text-destructive" />
Disable Two-Factor Authentication Disable Multi-Factor Authentication
</AlertDialogTitle> </AlertDialogTitle>
<AlertDialogDescription asChild> <AlertDialogDescription asChild>
<div className="space-y-4"> <div className="space-y-4">
<Alert variant="destructive"> <Alert variant="destructive">
<AlertTriangle className="h-4 w-4" /> <AlertTriangle className="h-4 w-4" />
<AlertDescription> <AlertDescription>
Disabling MFA will make your account less secure. You'll need to verify your identity first. Disabling Multi-Factor Authentication will make your account less secure. You'll need to verify your identity first.
</AlertDescription> </AlertDescription>
</Alert> </Alert>
@@ -235,9 +235,9 @@ export function MFARemovalDialog({ open, onOpenChange, factorId, onSuccess }: MF
{step === 'totp' && ( {step === 'totp' && (
<div className="space-y-3"> <div className="space-y-3">
<p className="text-sm">Step 2 of 3: Enter your current TOTP code</p> <p className="text-sm">Step 2 of 3: Enter your current code</p>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="totp">Verification Code</Label> <Label htmlFor="totp">Code from Authenticator App</Label>
<Input <Input
id="totp" id="totp"
type="text" type="text"
@@ -257,10 +257,10 @@ export function MFARemovalDialog({ open, onOpenChange, factorId, onSuccess }: MF
<div className="space-y-3"> <div className="space-y-3">
<p className="text-sm font-semibold">Step 3 of 3: Final confirmation</p> <p className="text-sm font-semibold">Step 3 of 3: Final confirmation</p>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
Are you sure you want to disable two-factor authentication? This will: Are you sure you want to disable Multi-Factor Authentication? This will:
</p> </p>
<ul className="text-sm text-muted-foreground list-disc list-inside space-y-1"> <ul className="text-sm text-muted-foreground list-disc list-inside space-y-1">
<li>Remove TOTP protection from your account</li> <li>Remove Multi-Factor Authentication protection from your account</li>
<li>Send a security notification email</li> <li>Send a security notification email</li>
<li>Log this action in your security history</li> <li>Log this action in your security history</li>
</ul> </ul>
@@ -285,7 +285,7 @@ export function MFARemovalDialog({ open, onOpenChange, factorId, onSuccess }: MF
)} )}
{step === 'confirm' && ( {step === 'confirm' && (
<AlertDialogAction onClick={handleMFARemoval} disabled={loading} className="bg-destructive text-destructive-foreground hover:bg-destructive/90"> <AlertDialogAction onClick={handleMFARemoval} disabled={loading} className="bg-destructive text-destructive-foreground hover:bg-destructive/90">
{loading ? 'Disabling...' : 'Disable MFA'} {loading ? 'Disabling...' : 'Disable Multi-Factor Authentication'}
</AlertDialogAction> </AlertDialogAction>
)} )}
</AlertDialogFooter> </AlertDialogFooter>

View File

@@ -30,18 +30,18 @@ export function MFARequiredAlert() {
return ( return (
<Alert variant="destructive" className="my-4"> <Alert variant="destructive" className="my-4">
<Shield className="h-4 w-4" /> <Shield className="h-4 w-4" />
<AlertTitle>Two-Factor Authentication Required</AlertTitle> <AlertTitle>Multi-Factor Authentication Required</AlertTitle>
<AlertDescription className="mt-2 space-y-3"> <AlertDescription className="mt-2 space-y-3">
<p> <p>
{needsVerification {needsVerification
? 'Please verify your identity with two-factor authentication to access this area.' ? 'Please verify your identity with Multi-Factor Authentication to access this area.'
: 'Your role requires two-factor authentication to access this area.'} : 'Your role requires Multi-Factor Authentication to access this area.'}
</p> </p>
<Button <Button
onClick={handleAction} onClick={handleAction}
size="sm" size="sm"
> >
{needsVerification ? 'Verify Now' : 'Set up MFA'} {needsVerification ? 'Verify Now' : 'Set up Multi-Factor Authentication'}
</Button> </Button>
</AlertDescription> </AlertDescription>
</Alert> </Alert>

View File

@@ -19,7 +19,7 @@ export function MFAStepUpModal({ open, factorId, onSuccess, onCancel }: MFAStepU
<DialogTitle>Additional Verification Required</DialogTitle> <DialogTitle>Additional Verification Required</DialogTitle>
</div> </div>
<DialogDescription className="text-center"> <DialogDescription className="text-center">
Your role requires two-factor authentication. Please verify your identity to continue. Your role requires Multi-Factor Authentication. Please verify your identity to continue.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>

View File

@@ -131,10 +131,10 @@ export function TOTPSetup() {
} }
handleSuccess( handleSuccess(
'TOTP Enabled', 'Multi-Factor Authentication Enabled',
isOAuthUser isOAuthUser
? 'Please verify with your authenticator code to continue.' ? 'Please verify with your authenticator app to continue.'
: 'Please sign in again to activate MFA protection.' : 'Please sign in again to activate Multi-Factor Authentication protection.'
); );
if (isOAuthUser) { if (isOAuthUser) {
@@ -249,7 +249,7 @@ export function TOTPSetup() {
Cancel Cancel
</Button> </Button>
<Button onClick={verifyAndEnable} disabled={loading || !verificationCode.trim()}> <Button onClick={verifyAndEnable} disabled={loading || !verificationCode.trim()}>
{loading ? 'Verifying...' : 'Enable TOTP'} {loading ? 'Verifying...' : 'Enable Multi-Factor Authentication'}
</Button> </Button>
</div> </div>
</div> </div>
@@ -262,7 +262,7 @@ export function TOTPSetup() {
<Card> <Card>
<CardHeader> <CardHeader>
<CardDescription> <CardDescription>
Add an extra layer of security to your account with two-factor authentication. Add an extra layer of security to your account with Multi-Factor Authentication.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
@@ -271,7 +271,7 @@ export function TOTPSetup() {
<Alert> <Alert>
<Shield className="w-4 h-4" /> <Shield className="w-4 h-4" />
<AlertDescription> <AlertDescription>
Two-factor authentication is enabled for your account. You'll be prompted for a verification code when signing in. Multi-Factor Authentication is enabled for your account. You'll be prompted for a code from your authenticator app when signing in.
</AlertDescription> </AlertDescription>
</Alert> </Alert>
@@ -280,7 +280,7 @@ export function TOTPSetup() {
<Alert variant="default" className="border-amber-200 dark:border-amber-800 bg-amber-50 dark:bg-amber-950"> <Alert variant="default" className="border-amber-200 dark:border-amber-800 bg-amber-50 dark:bg-amber-950">
<AlertTriangle className="w-4 h-4 text-amber-600 dark:text-amber-400" /> <AlertTriangle className="w-4 h-4 text-amber-600 dark:text-amber-400" />
<AlertDescription className="text-amber-800 dark:text-amber-200"> <AlertDescription className="text-amber-800 dark:text-amber-200">
Your role requires MFA. You cannot disable two-factor authentication. Your role requires Multi-Factor Authentication. You cannot disable it.
</AlertDescription> </AlertDescription>
</Alert> </Alert>
)} )}

View File

@@ -463,7 +463,7 @@ export function PasswordUpdateDialog({ open, onOpenChange, onSuccess }: Password
<div className="space-y-4 py-4"> <div className="space-y-4 py-4">
<div className="flex flex-col items-center gap-4"> <div className="flex flex-col items-center gap-4">
<Label htmlFor="totp-code">Authentication Code</Label> <Label htmlFor="totp-code">Code from Authenticator App</Label>
<InputOTP <InputOTP
maxLength={6} maxLength={6}
value={totpCode} value={totpCode}

View File

@@ -372,16 +372,16 @@ export function SecurityTab() {
</Card> </Card>
</div> </div>
{/* Two-Factor Authentication - Full Width (Moderators+ Only) */} {/* Multi-Factor Authentication - Full Width (Moderators+ Only) */}
{isModerator() && ( {isModerator() && (
<Card> <Card>
<CardHeader> <CardHeader>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Smartphone className="w-5 h-5" /> <Smartphone className="w-5 h-5" />
<CardTitle>Two-Factor Authentication</CardTitle> <CardTitle>Multi-Factor Authentication</CardTitle>
</div> </div>
<CardDescription> <CardDescription>
Add an extra layer of security to your account with TOTP authentication Add an extra layer of security to your account with Multi-Factor Authentication
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
@@ -434,7 +434,7 @@ export function SecurityTab() {
</span> </span>
)} )}
Last active: {format(new Date(session.refreshed_at || session.created_at), 'PPpp')} Last active: {format(new Date(session.refreshed_at || session.created_at), 'PPpp')}
{session.aal === 'aal2' && ' • MFA'} {session.aal === 'aal2' && ' • Multi-Factor'}
</p> </p>
{session.not_after && ( {session.not_after && (
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">

View File

@@ -26,6 +26,10 @@
{ {
"key": "X-XSS-Protection", "key": "X-XSS-Protection",
"value": "1; mode=block" "value": "1; mode=block"
},
{
"key": "Permissions-Policy",
"value": "browsing-topics=()"
} }
] ]
}, },