Lever la barrière d'enrôlement une fois les codes notés
Le layout applicatif calcule l'écran d'enrôlement à son propre rendu, et un layout n'est pas rejoué par la navigation client : une fois le second facteur activé, l'écran restait posé sur toutes les routes, quel que soit le menu choisi. L'action d'enrôlement ne peut pas réactualiser la route elle-même sans emporter les codes de secours, qui ne sont affichés qu'une fois. Le rafraîchissement est donc déclenché par l'écran des codes, sur un bouton : c'est leur destinataire qui décide du moment où il les a notés. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dd60dc33b8
commit
61a273548b
1 file changed
+33
-3
@@ -1,5 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
import { useActionState, useState, useTransition } from 'react';
|
import { useActionState, useState, useTransition } from 'react';
|
||||||
|
|
||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
@@ -28,8 +29,12 @@ export function MfaSettings({
|
|||||||
enrolled: boolean;
|
enrolled: boolean;
|
||||||
required: boolean;
|
required: boolean;
|
||||||
}) {
|
}) {
|
||||||
|
const router = useRouter();
|
||||||
const [offer, setOffer] = useState<OfferState | null>(null);
|
const [offer, setOffer] = useState<OfferState | null>(null);
|
||||||
const [starting, startTransition] = useTransition();
|
const [starting, startTransition] = useTransition();
|
||||||
|
// Les codes restent à l'écran tant que personne n'a dit les avoir notés : ce
|
||||||
|
// sont eux, et non l'horloge, qui décident du moment où l'on passe à la suite.
|
||||||
|
const [codesNoted, setCodesNoted] = useState(false);
|
||||||
|
|
||||||
const [confirmState, confirm, confirming] = useActionState(
|
const [confirmState, confirm, confirming] = useActionState(
|
||||||
confirmEnrolmentAction,
|
confirmEnrolmentAction,
|
||||||
@@ -43,8 +48,22 @@ export function MfaSettings({
|
|||||||
const codes = confirmState.recoveryCodes;
|
const codes = confirmState.recoveryCodes;
|
||||||
const active = (enrolled || confirmState.ok) && !disableState.ok;
|
const active = (enrolled || confirmState.ok) && !disableState.ok;
|
||||||
|
|
||||||
if (codes && codes.length > 0) {
|
if (codes && codes.length > 0 && !codesNoted) {
|
||||||
return <RecoveryCodes codes={codes} />;
|
return (
|
||||||
|
<RecoveryCodes
|
||||||
|
codes={codes}
|
||||||
|
onNoted={() => {
|
||||||
|
setCodesNoted(true);
|
||||||
|
// Le layout applicatif décide de l'écran d'enrôlement au rendu, et un
|
||||||
|
// layout n'est pas rejoué par la navigation client : sans ce
|
||||||
|
// rafraîchissement, l'écran resterait posé sur toutes les pages, quel
|
||||||
|
// que soit le menu choisi. Il est déclenché ici, et non par l'action
|
||||||
|
// d'enrôlement, pour que les codes ne s'effacent pas avant d'avoir été
|
||||||
|
// notés.
|
||||||
|
router.refresh();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (active) {
|
if (active) {
|
||||||
@@ -193,7 +212,13 @@ export function MfaSettings({
|
|||||||
* Ils sont conservés hachés : ni le support ni un administrateur ne peuvent les
|
* Ils sont conservés hachés : ni le support ni un administrateur ne peuvent les
|
||||||
* relire. La seule issue, ensuite, est d'en régénérer une série.
|
* relire. La seule issue, ensuite, est d'en régénérer une série.
|
||||||
*/
|
*/
|
||||||
function RecoveryCodes({ codes }: { codes: string[] }) {
|
function RecoveryCodes({
|
||||||
|
codes,
|
||||||
|
onNoted,
|
||||||
|
}: {
|
||||||
|
codes: string[];
|
||||||
|
onNoted: () => void;
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-3 p-4">
|
<div className="flex flex-col gap-3 p-4">
|
||||||
<p className="text-sm text-ink-1">
|
<p className="text-sm text-ink-1">
|
||||||
@@ -214,6 +239,11 @@ function RecoveryCodes({ codes }: { codes: string[] }) {
|
|||||||
Affichés une seule fois : ils ne sont pas conservés en clair. Chacun ne
|
Affichés une seule fois : ils ne sont pas conservés en clair. Chacun ne
|
||||||
sert qu’une fois.
|
sert qu’une fois.
|
||||||
</p>
|
</p>
|
||||||
|
<div>
|
||||||
|
<Button variant="primary" onClick={onNoted}>
|
||||||
|
J’ai noté mes codes, continuer
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user