Add captcha support to authentication features
The update includes the implementation of captcha support during the sign-in and sign-up process for user accounts. The process ensures a better level of security against bot-based attacks. Also, the code has been refactored to separate error and success alerts and unnecessary useEffect hooks have been removed. Moreover, some logic concerning the authentication rendering has been simplified.
This commit is contained in:
@@ -10,16 +10,21 @@ import { Database } from '@kit/supabase/database';
|
||||
import { requireUser } from '@kit/supabase/require-user';
|
||||
import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-client';
|
||||
|
||||
import { TeamCheckoutSchema } from '~/(dashboard)/home/[account]/_lib/schema/team-checkout.schema';
|
||||
import appConfig from '~/config/app.config';
|
||||
import billingConfig from '~/config/billing.config';
|
||||
import pathsConfig from '~/config/paths.config';
|
||||
|
||||
import { TeamCheckoutSchema } from '../../_lib/schema/team-checkout.schema';
|
||||
|
||||
export class TeamBillingService {
|
||||
private readonly namespace = 'billing.team-account';
|
||||
|
||||
constructor(private readonly client: SupabaseClient<Database>) {}
|
||||
|
||||
/**
|
||||
* @name createCheckout
|
||||
* @description Creates a checkout session for a Team account
|
||||
*/
|
||||
async createCheckout(params: z.infer<typeof TeamCheckoutSchema>) {
|
||||
// we require the user to be authenticated
|
||||
const { data: user } = await requireUser(this.client);
|
||||
@@ -126,6 +131,12 @@ export class TeamBillingService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @name createBillingPortalSession
|
||||
* @description Creates a new billing portal session for a team account
|
||||
* @param accountId
|
||||
* @param slug
|
||||
*/
|
||||
async createBillingPortalSession({
|
||||
accountId,
|
||||
slug,
|
||||
|
||||
@@ -4,13 +4,16 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { ReactQueryStreamedHydration } from '@tanstack/react-query-next-experimental';
|
||||
import { ThemeProvider } from 'next-themes';
|
||||
|
||||
import { CaptchaProvider, CaptchaTokenSetter } from '@kit/auth/captcha';
|
||||
import { I18nProvider } from '@kit/i18n/provider';
|
||||
import { AuthChangeListener } from '@kit/supabase/components/auth-change-listener';
|
||||
|
||||
import appConfig from '~/config/app.config';
|
||||
import authConfig from '~/config/auth.config';
|
||||
import pathsConfig from '~/config/paths.config';
|
||||
import { i18nResolver } from '~/lib/i18n/i18n.resolver';
|
||||
|
||||
const captchaSiteKey = authConfig.captchaTokenSiteKey;
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
export function RootProviders({
|
||||
@@ -22,18 +25,22 @@ export function RootProviders({
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ReactQueryStreamedHydration>
|
||||
<AuthChangeListener appHomePath={pathsConfig.app.home}>
|
||||
<I18nProvider lang={lang} resolver={i18nResolver}>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
defaultTheme={appConfig.theme}
|
||||
>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</I18nProvider>
|
||||
</AuthChangeListener>
|
||||
<CaptchaProvider>
|
||||
<CaptchaTokenSetter siteKey={captchaSiteKey} />
|
||||
|
||||
<AuthChangeListener appHomePath={pathsConfig.app.home}>
|
||||
<I18nProvider lang={lang} resolver={i18nResolver}>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
defaultTheme={appConfig.theme}
|
||||
>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</I18nProvider>
|
||||
</AuthChangeListener>
|
||||
</CaptchaProvider>
|
||||
</ReactQueryStreamedHydration>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { z } from 'zod';
|
||||
const providers: z.ZodType<Provider> = getProviders();
|
||||
|
||||
const AuthConfigSchema = z.object({
|
||||
captchaTokenSiteKey: z.string().min(1).optional(),
|
||||
providers: z.object({
|
||||
password: z.boolean({
|
||||
description: 'Enable password authentication.',
|
||||
@@ -17,6 +18,10 @@ const AuthConfigSchema = z.object({
|
||||
});
|
||||
|
||||
const authConfig = AuthConfigSchema.parse({
|
||||
// NB: This is a public key, so it's safe to expose.
|
||||
// Copy the value from the Supabase Dashboard.
|
||||
captchaTokenSiteKey: process.env.NEXT_PUBLIC_CAPTCHA_SITE_KEY ?? '',
|
||||
|
||||
// NB: Enable the providers below in the Supabase Console
|
||||
// in your production project
|
||||
providers: {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"@kit/supabase": "workspace:^",
|
||||
"@kit/team-accounts": "workspace:^",
|
||||
"@kit/ui": "workspace:^",
|
||||
"@marsidev/react-turnstile": "^0.5.4",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@supabase/ssr": "^0.1.0",
|
||||
"@supabase/supabase-js": "^2.42.0",
|
||||
|
||||
@@ -63,6 +63,8 @@
|
||||
"sendingEmailCode": "Sending code...",
|
||||
"resetPasswordError": "Sorry, we could not reset your password. Please try again.",
|
||||
"emailPlaceholder": "your@email.com",
|
||||
"inviteAlertHeading": "You have been invited to join a team",
|
||||
"inviteAlertBody": "You have been invited to join a team. Please sign in or sign up to accept the invite.",
|
||||
"errors": {
|
||||
"Invalid login credentials": "The credentials entered are invalid",
|
||||
"User already registered": "This credential is already in use. Please try with another one.",
|
||||
|
||||
Reference in New Issue
Block a user