import { useState } from 'react' import { useSearchParams } from 'react-router-dom' import { Button, Input } from '@gouvfr-lasuite/cunningham-react' import { useFlow } from '../../api/flows' import { useSessionStore } from '../../stores/session' import FlowForm from '../../components/FlowNodes/FlowForm' export default function SecurityPage() { const [params] = useSearchParams() const flowId = params.get('flow') const { needs2faSetup } = useSessionStore() // New users arriving from recovery flow: redirect to onboarding wizard if (needs2faSetup) { window.location.href = '/onboarding' return
Redirecting to account setup...
} // If redirected back from Kratos with a flow param, show the result if (flowId) { return (

Security

) } return (

Security

{needs2faSetup && (
You must set up two-factor authentication before you can use this app. Configure an authenticator app or security key below.
)}

Password

Two-Factor Authentication

Add a second layer of security to your account using an authenticator app, security key, or backup codes.

) } function PasswordSection() { const [expanded, setExpanded] = useState(false) const [password, setPassword] = useState('') const [confirm, setConfirm] = useState('') const [saving, setSaving] = useState(false) const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null) const mismatch = confirm.length > 0 && password !== confirm const handleSubmit = async () => { if (!password || password !== confirm) return setSaving(true) setMessage(null) try { const flowResp = await fetch('/kratos/self-service/settings/browser', { credentials: 'include', headers: { Accept: 'application/json' }, }) if (!flowResp.ok) throw new Error('Failed to create settings flow') const flow = await flowResp.json() const csrfToken = flow.ui.nodes.find( (n: { attributes: { name: string } }) => n.attributes.name === 'csrf_token' )?.attributes?.value ?? '' const submitResp = await fetch(flow.ui.action, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, credentials: 'include', body: new URLSearchParams({ csrf_token: csrfToken, method: 'password', password, }), }) if (submitResp.ok || submitResp.status === 422) { const result = await submitResp.json() const errorMsg = result.ui?.messages?.find((m: { type: string }) => m.type === 'error') if (errorMsg) { setMessage({ type: 'error', text: errorMsg.text }) } else { setMessage({ type: 'success', text: 'Password changed successfully.' }) setPassword('') setConfirm('') setExpanded(false) } } else { throw new Error('Failed to change password') } } catch (err) { setMessage({ type: 'error', text: String(err) }) } finally { setSaving(false) } } if (!expanded) { return ( <> {message && (
{message.text}
)} ) } return (
{message && (
{message.text}
)} ) => setPassword(e.target.value)} fullWidth /> ) => setConfirm(e.target.value)} state={mismatch ? 'error' : undefined} text={mismatch ? 'Passwords do not match' : undefined} fullWidth />
) } function MfaSection({ method, title, description }: { method: string; title: string; description: string }) { const [flowId, setFlowId] = useState(null) const [loading, setLoading] = useState(false) const [expanded, setExpanded] = useState(false) const startFlow = async () => { setLoading(true) try { const resp = await fetch('/kratos/self-service/settings/browser', { credentials: 'include', headers: { Accept: 'application/json' }, }) if (resp.ok) { const flow = await resp.json() setFlowId(flow.id) setExpanded(true) } } catch { // ignore } finally { setLoading(false) } } return (
{title}
{description}
{!expanded && ( )}
{expanded && flowId && (
)}
) } function SettingsFlow({ flowId, only, exclude }: { flowId: string; only?: string; exclude?: string[] }) { const { data: flow, isLoading, error } = useFlow('settings', flowId) if (isLoading) return
Loading...
if (error) return
Error: {String(error)}
if (!flow) return null return }