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:
giancarlo
2024-05-28 21:13:36 +07:00
parent ae162f7471
commit cbf116c688
7 changed files with 46 additions and 28 deletions

View File

@@ -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,
}}

View File

@@ -143,7 +143,7 @@ export function PersonalAccountSettingsContainer(
</CardHeader>
<CardContent>
<MultiFactorAuthFactorsList />
<MultiFactorAuthFactorsList userId={props.userId} />
</CardContent>
</Card>

View File

@@ -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({

View File

@@ -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 }) => {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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];
}