Add user id parameter to multi-factor authentication functions
The multi-factor authentication functions have been modified to accept a user id as a parameter. This provides more flexibility as it allows a more specific targeting of users. The `useFetchAuthFactors` function has been updated to export the function rather than default, and the `useFactorsMutationKey` function has been updated to take a user id.
This commit is contained in:
@@ -2,6 +2,7 @@ import { redirect } from 'next/navigation';
|
||||
|
||||
import { MultiFactorChallengeContainer } from '@kit/auth/mfa';
|
||||
import { checkRequiresMultiFactorAuthentication } from '@kit/supabase/check-requires-mfa';
|
||||
import { requireUser } from '@kit/supabase/require-user';
|
||||
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
|
||||
|
||||
import pathsConfig from '~/config/paths.config';
|
||||
@@ -31,9 +32,15 @@ async function VerifyPage(props: Props) {
|
||||
}
|
||||
|
||||
const redirectPath = props.searchParams.next ?? pathsConfig.app.home;
|
||||
const auth = await requireUser(client);
|
||||
|
||||
if (auth.error) {
|
||||
redirect(auth.redirectTo);
|
||||
}
|
||||
|
||||
return (
|
||||
<MultiFactorChallengeContainer
|
||||
userId={auth.data.id}
|
||||
paths={{
|
||||
redirectPath,
|
||||
}}
|
||||
|
||||
@@ -143,7 +143,7 @@ export function PersonalAccountSettingsContainer(
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
<MultiFactorAuthFactorsList />
|
||||
<MultiFactorAuthFactorsList userId={props.userId} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { ShieldCheck, X } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import useFetchAuthFactors from '@kit/supabase/hooks/use-fetch-mfa-factors';
|
||||
import { useFetchAuthFactors } from '@kit/supabase/hooks/use-fetch-mfa-factors';
|
||||
import { useSupabase } from '@kit/supabase/hooks/use-supabase';
|
||||
import { useFactorsMutationKey } from '@kit/supabase/hooks/use-user-factors-mutation-key';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||
@@ -48,8 +48,13 @@ import { MultiFactorAuthSetupDialog } from './multi-factor-auth-setup-dialog';
|
||||
|
||||
const MAX_FACTOR_COUNT = 10;
|
||||
|
||||
export function MultiFactorAuthFactorsList() {
|
||||
const { data: factors, isLoading, isError } = useFetchAuthFactors();
|
||||
export function MultiFactorAuthFactorsList(props: { userId: string }) {
|
||||
const {
|
||||
data: factors,
|
||||
isLoading,
|
||||
isError,
|
||||
} = useFetchAuthFactors(props.userId);
|
||||
|
||||
const [unEnrolling, setUnenrolling] = useState<string>();
|
||||
|
||||
if (isLoading) {
|
||||
@@ -100,7 +105,7 @@ export function MultiFactorAuthFactorsList() {
|
||||
</Alert>
|
||||
|
||||
<div>
|
||||
<MultiFactorAuthSetupDialog />
|
||||
<MultiFactorAuthSetupDialog userId={props.userId} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -114,13 +119,14 @@ export function MultiFactorAuthFactorsList() {
|
||||
|
||||
<If condition={canAddNewFactors}>
|
||||
<div>
|
||||
<MultiFactorAuthSetupDialog />
|
||||
<MultiFactorAuthSetupDialog userId={props.userId} />
|
||||
</div>
|
||||
</If>
|
||||
|
||||
<If condition={unEnrolling}>
|
||||
{(factorId) => (
|
||||
<ConfirmUnenrollFactorModal
|
||||
userId={props.userId}
|
||||
factorId={factorId}
|
||||
setIsModalOpen={() => setUnenrolling(undefined)}
|
||||
/>
|
||||
@@ -133,11 +139,12 @@ export function MultiFactorAuthFactorsList() {
|
||||
function ConfirmUnenrollFactorModal(
|
||||
props: React.PropsWithChildren<{
|
||||
factorId: string;
|
||||
userId: string;
|
||||
setIsModalOpen: (isOpen: boolean) => void;
|
||||
}>,
|
||||
) {
|
||||
const { t } = useTranslation();
|
||||
const unEnroll = useUnenrollFactor();
|
||||
const unEnroll = useUnenrollFactor(props.userId);
|
||||
|
||||
const onUnenrollRequested = useCallback(
|
||||
(factorId: string) => {
|
||||
@@ -261,10 +268,10 @@ function FactorsTable({
|
||||
);
|
||||
}
|
||||
|
||||
function useUnenrollFactor() {
|
||||
function useUnenrollFactor(userId: string) {
|
||||
const queryClient = useQueryClient();
|
||||
const client = useSupabase();
|
||||
const mutationKey = useFactorsMutationKey();
|
||||
const mutationKey = useFactorsMutationKey(userId);
|
||||
|
||||
const mutationFn = async (factorId: string) => {
|
||||
const { data, error } = await client.auth.mfa.unenroll({
|
||||
|
||||
@@ -44,7 +44,7 @@ import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { refreshAuthSession } from '../../../server/personal-accounts-server-actions';
|
||||
|
||||
export function MultiFactorAuthSetupDialog() {
|
||||
export function MultiFactorAuthSetupDialog(props: { userId: string }) {
|
||||
const { t } = useTranslation();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
@@ -77,6 +77,7 @@ export function MultiFactorAuthSetupDialog() {
|
||||
|
||||
<div>
|
||||
<MultiFactorAuthSetupForm
|
||||
userId={props.userId}
|
||||
onCancel={() => setIsOpen(false)}
|
||||
onEnrolled={onEnrollSuccess}
|
||||
/>
|
||||
@@ -90,11 +91,13 @@ export function MultiFactorAuthSetupDialog() {
|
||||
function MultiFactorAuthSetupForm({
|
||||
onEnrolled,
|
||||
onCancel,
|
||||
userId,
|
||||
}: React.PropsWithChildren<{
|
||||
userId: string;
|
||||
onCancel: () => void;
|
||||
onEnrolled: () => void;
|
||||
}>) {
|
||||
const verifyCodeMutation = useVerifyCodeMutation();
|
||||
const verifyCodeMutation = useVerifyCodeMutation(userId);
|
||||
|
||||
const verificationCodeForm = useForm({
|
||||
resolver: zodResolver(
|
||||
@@ -161,6 +164,7 @@ function MultiFactorAuthSetupForm({
|
||||
<div className={'flex flex-col space-y-4'}>
|
||||
<div className={'flex justify-center'}>
|
||||
<FactorQrCode
|
||||
userId={userId}
|
||||
onCancel={onCancel}
|
||||
onSetFactorId={(factorId) =>
|
||||
verificationCodeForm.setValue('factorId', factorId)
|
||||
@@ -241,11 +245,13 @@ function MultiFactorAuthSetupForm({
|
||||
function FactorQrCode({
|
||||
onSetFactorId,
|
||||
onCancel,
|
||||
userId,
|
||||
}: React.PropsWithChildren<{
|
||||
userId: string;
|
||||
onCancel: () => void;
|
||||
onSetFactorId: (factorId: string) => void;
|
||||
}>) {
|
||||
const enrollFactorMutation = useEnrollFactor();
|
||||
const enrollFactorMutation = useEnrollFactor(userId);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const form = useForm({
|
||||
@@ -385,9 +391,9 @@ function QrImage({ src }: { src: string }) {
|
||||
return <Image alt={'QR Code'} src={src} width={160} height={160} />;
|
||||
}
|
||||
|
||||
function useEnrollFactor() {
|
||||
function useEnrollFactor(userId: string) {
|
||||
const client = useSupabase();
|
||||
const mutationKey = useFactorsMutationKey();
|
||||
const mutationKey = useFactorsMutationKey(userId);
|
||||
|
||||
const mutationFn = async (factorName: string) => {
|
||||
const { data, error } = await client.auth.mfa.enroll({
|
||||
@@ -408,8 +414,8 @@ function useEnrollFactor() {
|
||||
});
|
||||
}
|
||||
|
||||
function useVerifyCodeMutation() {
|
||||
const mutationKey = useFactorsMutationKey();
|
||||
function useVerifyCodeMutation(userId: string) {
|
||||
const mutationKey = useFactorsMutationKey(userId);
|
||||
const client = useSupabase();
|
||||
|
||||
const mutationFn = async (params: { factorId: string; code: string }) => {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useMutation } from '@tanstack/react-query';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
import useFetchAuthFactors from '@kit/supabase/hooks/use-fetch-mfa-factors';
|
||||
import { useFetchAuthFactors } from '@kit/supabase/hooks/use-fetch-mfa-factors';
|
||||
import { useSignOut } from '@kit/supabase/hooks/use-sign-out';
|
||||
import { useSupabase } from '@kit/supabase/hooks/use-supabase';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||
@@ -35,7 +35,9 @@ import { Trans } from '@kit/ui/trans';
|
||||
|
||||
export function MultiFactorChallengeContainer({
|
||||
paths,
|
||||
userId,
|
||||
}: React.PropsWithChildren<{
|
||||
userId: string;
|
||||
paths: {
|
||||
redirectPath: string;
|
||||
};
|
||||
@@ -65,6 +67,7 @@ export function MultiFactorChallengeContainer({
|
||||
if (!factorId) {
|
||||
return (
|
||||
<FactorsListContainer
|
||||
userId={userId}
|
||||
onSelect={(factorId) => {
|
||||
verificationCodeForm.setValue('factorId', factorId);
|
||||
}}
|
||||
@@ -195,12 +198,14 @@ function useVerifyMFAChallenge() {
|
||||
function FactorsListContainer({
|
||||
onSuccess,
|
||||
onSelect,
|
||||
userId,
|
||||
}: React.PropsWithChildren<{
|
||||
userId: string;
|
||||
onSuccess: () => void;
|
||||
onSelect: (factor: string) => void;
|
||||
}>) {
|
||||
const signOut = useSignOut();
|
||||
const { data: factors, isLoading, error } = useFetchAuthFactors();
|
||||
const { data: factors, isLoading, error } = useFetchAuthFactors(userId);
|
||||
|
||||
const isSuccess = factors && !isLoading && !error;
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import { useSupabase } from './use-supabase';
|
||||
import { useFactorsMutationKey } from './use-user-factors-mutation-key';
|
||||
|
||||
function useFetchAuthFactors() {
|
||||
export function useFetchAuthFactors(userId: string) {
|
||||
const client = useSupabase();
|
||||
const queryKey = useFactorsMutationKey();
|
||||
const queryKey = useFactorsMutationKey(userId);
|
||||
|
||||
const queryFn = async () => {
|
||||
const { data, error } = await client.auth.mfa.listFactors();
|
||||
@@ -22,5 +22,3 @@ function useFetchAuthFactors() {
|
||||
queryFn,
|
||||
});
|
||||
}
|
||||
|
||||
export default useFetchAuthFactors;
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
import { useUserSession } from './use-user-session';
|
||||
|
||||
export function useFactorsMutationKey() {
|
||||
const user = useUserSession();
|
||||
const userId = user?.data?.user.id;
|
||||
|
||||
export function useFactorsMutationKey(userId: string) {
|
||||
return ['mfa-factors', userId];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user