One-Time Password (OTP) package added with comprehensive token management, including OTP verification for team account deletion and ownership transfer.
105 lines
2.7 KiB
TypeScript
105 lines
2.7 KiB
TypeScript
'use server';
|
|
|
|
import { revalidatePath } from 'next/cache';
|
|
import { redirect } from 'next/navigation';
|
|
|
|
import { enhanceAction } from '@kit/next/actions';
|
|
import { createOtpApi } from '@kit/otp';
|
|
import { getLogger } from '@kit/shared/logger';
|
|
import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client';
|
|
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
|
|
|
import { DeletePersonalAccountSchema } from '../schema/delete-personal-account.schema';
|
|
import { createDeletePersonalAccountService } from './services/delete-personal-account.service';
|
|
|
|
const enableAccountDeletion =
|
|
process.env.NEXT_PUBLIC_ENABLE_PERSONAL_ACCOUNT_DELETION === 'true';
|
|
|
|
export async function refreshAuthSession() {
|
|
const client = getSupabaseServerClient();
|
|
|
|
await client.auth.refreshSession();
|
|
|
|
return {};
|
|
}
|
|
|
|
export const deletePersonalAccountAction = enhanceAction(
|
|
async (formData: FormData, user) => {
|
|
const logger = await getLogger();
|
|
|
|
// validate the form data
|
|
const { success } = DeletePersonalAccountSchema.safeParse(
|
|
Object.fromEntries(formData.entries()),
|
|
);
|
|
|
|
if (!success) {
|
|
throw new Error('Invalid form data');
|
|
}
|
|
|
|
const ctx = {
|
|
name: 'account.delete',
|
|
userId: user.id,
|
|
};
|
|
|
|
const otp = formData.get('otp') as string;
|
|
|
|
if (!otp) {
|
|
throw new Error('OTP is required');
|
|
}
|
|
|
|
if (!enableAccountDeletion) {
|
|
logger.warn(ctx, `Account deletion is not enabled`);
|
|
|
|
throw new Error('Account deletion is not enabled');
|
|
}
|
|
|
|
logger.info(ctx, `Deleting account...`);
|
|
|
|
// verify the OTP
|
|
const client = getSupabaseServerClient();
|
|
const otpApi = createOtpApi(client);
|
|
|
|
const otpResult = await otpApi.verifyToken({
|
|
token: otp,
|
|
userId: user.id,
|
|
purpose: 'delete-personal-account',
|
|
});
|
|
|
|
if (!otpResult.valid) {
|
|
throw new Error('Invalid OTP');
|
|
}
|
|
|
|
// validate the user ID matches the nonce's user ID
|
|
if (otpResult.user_id !== user.id) {
|
|
logger.error(
|
|
ctx,
|
|
`This token was meant to be used by a different user. Exiting.`,
|
|
);
|
|
|
|
throw new Error('Nonce mismatch');
|
|
}
|
|
|
|
// create a new instance of the personal accounts service
|
|
const service = createDeletePersonalAccountService();
|
|
|
|
// delete the user's account and cancel all subscriptions
|
|
await service.deletePersonalAccount({
|
|
adminClient: getSupabaseServerAdminClient(),
|
|
userId: user.id,
|
|
userEmail: user.email ?? null,
|
|
});
|
|
|
|
// sign out the user after deleting their account
|
|
await client.auth.signOut();
|
|
|
|
logger.info(ctx, `Account request successfully sent`);
|
|
|
|
// clear the cache for all pages
|
|
revalidatePath('/', 'layout');
|
|
|
|
// redirect to the home page
|
|
redirect('/');
|
|
},
|
|
{},
|
|
);
|