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