mirror of
https://github.com/logto-io/logto.git
synced 2025-01-20 21:32:31 -05:00
refactor(experience): polish handle mfa redirection codes and add comments (#4782)
This commit is contained in:
parent
4080a2599b
commit
7029255b91
1 changed files with 39 additions and 16 deletions
|
@ -1,5 +1,6 @@
|
||||||
import { MfaFactor, type RequestErrorBody } from '@logto/schemas';
|
import { MfaFactor, type RequestErrorBody } from '@logto/schemas';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { validate } from 'superstruct';
|
import { validate } from 'superstruct';
|
||||||
|
|
||||||
|
@ -22,45 +23,67 @@ export type Options = {
|
||||||
|
|
||||||
const useMfaErrorHandler = ({ replace }: Options = {}) => {
|
const useMfaErrorHandler = ({ replace }: Options = {}) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { t } = useTranslation();
|
||||||
const { setToast } = useToast();
|
const { setToast } = useToast();
|
||||||
const startTotpBinding = useStartTotpBinding({ replace });
|
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(
|
const handleMfaRedirect = useCallback(
|
||||||
(flow: UserMfaFlow, state: MfaFlowState) => {
|
(flow: UserMfaFlow, state: MfaFlowState) => {
|
||||||
const { availableFactors } = state;
|
const { availableFactors } = state;
|
||||||
|
|
||||||
if (flow === UserMfaFlow.MfaVerification) {
|
if (availableFactors.length > 1 && flow === UserMfaFlow.MfaBinding) {
|
||||||
// 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];
|
* Redirect to the MFA binding page if there are multiple available factors.
|
||||||
|
*/
|
||||||
if (!factor) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
navigate({ pathname: `/${flow}/${factor}` }, { replace, state });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Binding flow
|
|
||||||
if (availableFactors.length > 1) {
|
|
||||||
navigate({ pathname: `/${flow}` }, { replace, state });
|
navigate({ pathname: `/${flow}` }, { replace, state });
|
||||||
return;
|
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];
|
const factor = availableFactors[0];
|
||||||
|
|
||||||
if (!factor) {
|
if (!factor) {
|
||||||
|
/**
|
||||||
|
* This should never happen since we check the available factors' length before handling the redirection.
|
||||||
|
*/
|
||||||
|
setToast(t('error.unknown'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (factor === MfaFactor.TOTP) {
|
if (factor === MfaFactor.TOTP && flow === UserMfaFlow.MfaBinding) {
|
||||||
|
/**
|
||||||
|
* Start TOTP binding process if only TOTP is available.
|
||||||
|
*/
|
||||||
void startTotpBinding(state);
|
void startTotpBinding(state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect to the specific MFA factor page.
|
||||||
|
*/
|
||||||
navigate({ pathname: `/${flow}/${factor}` }, { replace, state });
|
navigate({ pathname: `/${flow}/${factor}` }, { replace, state });
|
||||||
},
|
},
|
||||||
[navigate, replace, startTotpBinding]
|
[navigate, replace, setToast, startTotpBinding, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleMfaError = useCallback(
|
const handleMfaError = useCallback(
|
||||||
|
|
Loading…
Add table
Reference in a new issue