'use client' import { useState, useEffect } from 'react' import { useRouter } from 'next/navigation' import { createClient } from '@/lib/supabase/client' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { useToast } from '@/components/ui/use-toast' import { Loader2, ShieldCheck, ShieldOff, KeyRound } from 'lucide-react' import { isMfaRequired } from '@/lib/auth/mfa' import { isBankIdEnabled } from '@/lib/auth/bankid' import { BankIdSettings } from '@/components/settings/BankIdSettings' import { userHasPassword } from '@/lib/auth/has-password' const isSelfHosted = process.env.NEXT_PUBLIC_SELF_HOSTED === 'true' const mfaRequired = isMfaRequired() const bankIdEnabled = isBankIdEnabled() export function SecuritySettings() { const [newPassword, setNewPassword] = useState('') const [confirmPassword, setConfirmPassword] = useState('') const [isChangingPassword, setIsChangingPassword] = useState(false) const [hasMfa, setHasMfa] = useState(false) const [isLoadingMfa, setIsLoadingMfa] = useState(true) const [isUnenrolling, setIsUnenrolling] = useState(false) const [mfaFactorId, setMfaFactorId] = useState(null) const [hasPassword, setHasPassword] = useState(null) const { toast } = useToast() const router = useRouter() const supabase = createClient() useEffect(() => { async function loadStatus() { const [{ data: factors }, { data: userData }] = await Promise.all([ supabase.auth.mfa.listFactors(), supabase.auth.getUser(), ]) const verifiedFactor = factors?.totp?.find(f => f.status === 'verified') setHasMfa(!!verifiedFactor) setMfaFactorId(verifiedFactor?.id ?? null) setHasPassword(userData?.user ? userHasPassword(userData.user) : null) setIsLoadingMfa(false) } loadStatus() // eslint-disable-next-line react-hooks/exhaustive-deps }, []) const handleChangePassword = async (e: React.FormEvent) => { e.preventDefault() setIsChangingPassword(true) const strong = newPassword.length >= 8 && /[a-z]/.test(newPassword) && /[A-Z]/.test(newPassword) && /[0-9]/.test(newPassword) && /[^a-zA-Z0-9]/.test(newPassword) if (!strong) { toast({ title: 'Lösenordet är för svagt', description: 'Lösenordet måste vara minst 8 tecken och innehålla versaler, gemener, siffror och specialtecken.', variant: 'destructive', }) setIsChangingPassword(false) return } if (newPassword !== confirmPassword) { toast({ title: 'Lösenorden matchar inte', description: 'Kontrollera att du skrev samma lösenord i båda fälten.', variant: 'destructive', }) setIsChangingPassword(false) return } try { const res = await fetch('/api/account/password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: newPassword }), }) if (!res.ok) { const body = (await res.json().catch(() => ({}))) as { error?: string } toast({ title: 'Kunde inte uppdatera lösenord', description: body.error || 'Försök igen senare.', variant: 'destructive', }) return } toast({ title: 'Lösenord uppdaterat', description: 'Ditt lösenord har ändrats.', }) setNewPassword('') setConfirmPassword('') setHasPassword(true) } catch { toast({ title: 'Något gick fel', description: 'Försök igen senare.', variant: 'destructive', }) } finally { setIsChangingPassword(false) } } const handleUnenrollMfa = async () => { if (!mfaFactorId) return setIsUnenrolling(true) try { const { error } = await supabase.auth.mfa.unenroll({ factorId: mfaFactorId }) if (error) { toast({ title: 'Kunde inte inaktivera 2FA', description: error.message, variant: 'destructive', }) return } toast({ title: 'Tvåfaktorsautentisering inaktiverad', description: '2FA har tagits bort från ditt konto.', }) setHasMfa(false) setMfaFactorId(null) } catch { toast({ title: 'Något gick fel', description: 'Försök igen senare.', variant: 'destructive', }) } finally { setIsUnenrolling(false) } } return (
{bankIdEnabled && } {/* BankID-only users with no password — banner above everything else */} {hasPassword === false && ( Sätt ett lösenord Du loggade in med BankID och har inget lösenord ännu. Sätt ett lösenord för att kunna aktivera 2FA eller logga in när BankID inte är tillgängligt. )} {/* Change password — hidden when the user has no password (the banner above handles the set-initial-password flow). */} {hasPassword !== false && ( Ändra lösenord Uppdatera ditt lösenord. Om du loggar in med e-postlänk kan du sätta ett lösenord här.
setNewPassword(e.target.value)} required minLength={8} disabled={isChangingPassword} />
setConfirmPassword(e.target.value)} required minLength={8} disabled={isChangingPassword} />
)} {/* MFA — hidden for self-hosted */} {!isSelfHosted && ( Tvåfaktorsautentisering (2FA) Skydda ditt konto med en autentiseringsapp. Vid varje inloggning behöver du ange en kod utöver ditt lösenord. {isLoadingMfa ? (
Laddar...
) : hasMfa ? (

2FA är aktiverad

Ditt konto skyddas med tvåfaktorsautentisering.

{!mfaRequired && ( )} {mfaRequired && (

Tvåfaktorsautentisering är obligatorisk och kan inte inaktiveras.

)}
) : (

2FA är inte aktiverad

Vi rekommenderar att du aktiverar tvåfaktorsautentisering.

{hasPassword === false ? ( ) : ( )}
)}
)}
) }