0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-06 20:40:08 -05:00

refactor(experience): polish handle mfa redirection codes and add comments (#4782)

This commit is contained in:
Xiao Yijun 2023-10-30 10:53:56 +08:00 committed by GitHub
parent 4080a2599b
commit 7029255b91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,5 +1,6 @@
import { MfaFactor, type RequestErrorBody } from '@logto/schemas';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { validate } from 'superstruct';
@ -22,45 +23,67 @@ export type Options = {
const useMfaErrorHandler = ({ replace }: Options = {}) => {
const navigate = useNavigate();
const { t } = useTranslation();
const { setToast } = useToast();
const startTotpBinding = useStartTotpBinding({ replace });
/**
* Redirect the user to the corresponding MFA page.
*
* Binding pages are hosted on following routes:
* - /{@link UserMfaFlow.MfaBinding} List of available MFA factors for binding.
* - /{@link UserMfaFlow.MfaBinding}/{@link MfaFactor} Binding page for the specific factor.
*
* Verification pages are hosted on following routes:
* - /{@link UserMfaFlow.MfaVerification} List of available MFA factors for verification.
* - /{@link UserMfaFlow.MfaVerification}/{@link MfaFactor} Verification page for the specific factor.
*
* Redirection rules:
* - If there is only one available factor, redirect to the specific MFA factor page.
* - If there are multiple available factors:
* - Binding: redirect to the available factors list page.
* - Verification: redirect to the last used specific factor page.
*/
const handleMfaRedirect = useCallback(
(flow: UserMfaFlow, state: MfaFlowState) => {
const { availableFactors } = state;
if (flow === UserMfaFlow.MfaVerification) {
// In MFA verification flow, the user will be redirected to the last used factor which is the first one in the array.
const factor = availableFactors[0];
if (!factor) {
return;
}
navigate({ pathname: `/${flow}/${factor}` }, { replace, state });
return;
}
// Binding flow
if (availableFactors.length > 1) {
if (availableFactors.length > 1 && flow === UserMfaFlow.MfaBinding) {
/**
* Redirect to the MFA binding page if there are multiple available factors.
*/
navigate({ pathname: `/${flow}` }, { replace, state });
return;
}
/**
* For verification: the first available factor is the last used factor which is guaranteed by the backend.
* For binding: the first available factor is the only available factor since we handle the multiple factors case above.
*/
const factor = availableFactors[0];
if (!factor) {
/**
* This should never happen since we check the available factors' length before handling the redirection.
*/
setToast(t('error.unknown'));
return;
}
if (factor === MfaFactor.TOTP) {
if (factor === MfaFactor.TOTP && flow === UserMfaFlow.MfaBinding) {
/**
* Start TOTP binding process if only TOTP is available.
*/
void startTotpBinding(state);
return;
}
/**
* Redirect to the specific MFA factor page.
*/
navigate({ pathname: `/${flow}/${factor}` }, { replace, state });
},
[navigate, replace, startTotpBinding]
[navigate, replace, setToast, startTotpBinding, t]
);
const handleMfaError = useCallback(