From ce9f2a238fe63817b0376e25d393f61f2cdf01a1 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Mon, 23 Feb 2026 00:37:56 +0200 Subject: [PATCH 1/3] fix: signup confirmation --- packages/server/src/modules/Auth/Auth.controller.ts | 2 +- packages/server/src/modules/Auth/Authed.controller.ts | 8 ++------ .../src/modules/Auth/commands/AuthSignup.service.ts | 5 ++--- .../Auth/commands/AuthSignupConfirmResend.service.ts | 1 - .../src/modules/Organization/Organization.controller.ts | 2 ++ .../modules/Tenancy/EnsureTenantIsInitialized.guard.ts | 7 ++++++- .../src/modules/Tenancy/EnsureTenantIsSeeded.guards.ts | 8 +++++++- packages/webapp/src/hooks/query/authentication.tsx | 2 +- 8 files changed, 21 insertions(+), 14 deletions(-) diff --git a/packages/server/src/modules/Auth/Auth.controller.ts b/packages/server/src/modules/Auth/Auth.controller.ts index 9a12562ee..534e66c01 100644 --- a/packages/server/src/modules/Auth/Auth.controller.ts +++ b/packages/server/src/modules/Auth/Auth.controller.ts @@ -65,7 +65,7 @@ export class AuthController { return this.authApp.signUp(signupDto); } - @Post('/signup/confirm') + @Post('/signup/verify') @ApiOperation({ summary: 'Confirm user signup' }) @ApiBody({ schema: { diff --git a/packages/server/src/modules/Auth/Authed.controller.ts b/packages/server/src/modules/Auth/Authed.controller.ts index 79c4a27da..e18b3635c 100644 --- a/packages/server/src/modules/Auth/Authed.controller.ts +++ b/packages/server/src/modules/Auth/Authed.controller.ts @@ -7,17 +7,13 @@ import { import { GetAuthenticatedAccount } from './queries/GetAuthedAccount.service'; import { Controller, Get, Post } from '@nestjs/common'; import { Throttle } from '@nestjs/throttler'; -import { IgnoreTenantSeededRoute } from '../Tenancy/EnsureTenantIsSeeded.guards'; -import { IgnoreTenantInitializedRoute } from '../Tenancy/EnsureTenantIsInitialized.guard'; +import { TenantAgnosticRoute } from '../Tenancy/TenancyGlobal.guard'; import { AuthenticationApplication } from './AuthApplication.sevice'; -import { TenancyContext } from '../Tenancy/TenancyContext.service'; import { IgnoreUserVerifiedRoute } from './guards/EnsureUserVerified.guard'; @Controller('/auth') @ApiTags('Auth') -@ApiExcludeController() -@IgnoreTenantSeededRoute() -@IgnoreTenantInitializedRoute() +@TenantAgnosticRoute() @IgnoreUserVerifiedRoute() @Throttle({ auth: {} }) export class AuthedController { diff --git a/packages/server/src/modules/Auth/commands/AuthSignup.service.ts b/packages/server/src/modules/Auth/commands/AuthSignup.service.ts index 90188c77d..8cf42390a 100644 --- a/packages/server/src/modules/Auth/commands/AuthSignup.service.ts +++ b/packages/server/src/modules/Auth/commands/AuthSignup.service.ts @@ -13,7 +13,6 @@ import { IAuthSignedUpEventPayload, IAuthSigningUpEventPayload, } from '../Auth.interfaces'; -import { defaultTo } from 'ramda'; import { ERRORS } from '../Auth.constants'; import { hashPassword } from '../Auth.utils'; import { ClsService } from 'nestjs-cls'; @@ -51,10 +50,10 @@ export class AuthSignupService { const signupConfirmation = this.configService.get('signupConfirmation'); const verifyTokenCrypto = crypto.randomBytes(64).toString('hex'); - const verifiedEnabed = defaultTo(signupConfirmation.enabled, false); + const verifiedEnabed = signupConfirmation.enabled ?? false; const verifyToken = verifiedEnabed ? verifyTokenCrypto : ''; const verified = !verifiedEnabed; - + const inviteAcceptedAt = moment().format('YYYY-MM-DD'); // Triggers signin up event. diff --git a/packages/server/src/modules/Auth/commands/AuthSignupConfirmResend.service.ts b/packages/server/src/modules/Auth/commands/AuthSignupConfirmResend.service.ts index 3f923a032..7e0721bab 100644 --- a/packages/server/src/modules/Auth/commands/AuthSignupConfirmResend.service.ts +++ b/packages/server/src/modules/Auth/commands/AuthSignupConfirmResend.service.ts @@ -4,7 +4,6 @@ import { SystemUser } from '@/modules/System/models/SystemUser'; import { ServiceError } from '@/modules/Items/ServiceError'; import { ERRORS } from '../Auth.constants'; import { events } from '@/common/events/events'; -import { ModelObject } from 'objection'; import { ISignUpConfigmResendedEventPayload } from '../Auth.interfaces'; import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service'; diff --git a/packages/server/src/modules/Organization/Organization.controller.ts b/packages/server/src/modules/Organization/Organization.controller.ts index dc38fdfa9..272b531b9 100644 --- a/packages/server/src/modules/Organization/Organization.controller.ts +++ b/packages/server/src/modules/Organization/Organization.controller.ts @@ -28,6 +28,7 @@ import { UpdateOrganizationService } from './commands/UpdateOrganization.service import { IgnoreTenantInitializedRoute } from '../Tenancy/EnsureTenantIsInitialized.guard'; import { IgnoreTenantSeededRoute } from '../Tenancy/EnsureTenantIsSeeded.guards'; import { IgnoreTenantModelsInitialize } from '../Tenancy/TenancyInitializeModels.guard'; +import { IgnoreUserVerifiedRoute } from '../Auth/guards/EnsureUserVerified.guard'; import { GetBuildOrganizationBuildJob } from './commands/GetBuildOrganizationJob.service'; import { OrganizationBaseCurrencyLocking } from './Organization/OrganizationBaseCurrencyLocking.service'; import { @@ -93,6 +94,7 @@ export class OrganizationController { @Get('current') @HttpCode(200) + @IgnoreUserVerifiedRoute() @ApiOperation({ summary: 'Get current organization' }) @ApiResponse({ status: 200, diff --git a/packages/server/src/modules/Tenancy/EnsureTenantIsInitialized.guard.ts b/packages/server/src/modules/Tenancy/EnsureTenantIsInitialized.guard.ts index 01333e2a3..67282b5ee 100644 --- a/packages/server/src/modules/Tenancy/EnsureTenantIsInitialized.guard.ts +++ b/packages/server/src/modules/Tenancy/EnsureTenantIsInitialized.guard.ts @@ -8,6 +8,7 @@ import { import { TenancyContext } from './TenancyContext.service'; import { Reflector } from '@nestjs/core'; import { IS_PUBLIC_ROUTE } from '../Auth/Auth.constants'; +import { IS_TENANT_AGNOSTIC } from './TenancyGlobal.guard'; export const IS_IGNORE_TENANT_INITIALIZED = 'IS_IGNORE_TENANT_INITIALIZED'; export const IgnoreTenantInitializedRoute = () => @@ -35,8 +36,12 @@ export class EnsureTenantIsInitializedGuard implements CanActivate { IS_PUBLIC_ROUTE, [context.getHandler(), context.getClass()], ); + const isTenantAgnostic = this.reflector.getAllAndOverride( + IS_TENANT_AGNOSTIC, + [context.getHandler(), context.getClass()], + ); // Skip the guard early if the route marked as public or ignored. - if (isPublic || isIgnoreEnsureTenantInitialized) { + if (isPublic || isIgnoreEnsureTenantInitialized || isTenantAgnostic) { return true; } const tenant = await this.tenancyContext.getTenant(); diff --git a/packages/server/src/modules/Tenancy/EnsureTenantIsSeeded.guards.ts b/packages/server/src/modules/Tenancy/EnsureTenantIsSeeded.guards.ts index 8149179bc..cab864c8f 100644 --- a/packages/server/src/modules/Tenancy/EnsureTenantIsSeeded.guards.ts +++ b/packages/server/src/modules/Tenancy/EnsureTenantIsSeeded.guards.ts @@ -9,6 +9,7 @@ import { import { TenancyContext } from './TenancyContext.service'; import { Reflector } from '@nestjs/core'; import { IS_PUBLIC_ROUTE } from '../Auth/Auth.constants'; +import { IS_TENANT_AGNOSTIC } from './TenancyGlobal.guard'; export const IS_IGNORE_TENANT_SEEDED = 'IS_IGNORE_TENANT_SEEDED'; export const IgnoreTenantSeededRoute = () => @@ -36,7 +37,12 @@ export class EnsureTenantIsSeededGuard implements CanActivate { context.getHandler(), context.getClass(), ]); - if (isPublic || isIgnoreEnsureTenantSeeded) { + const isTenantAgnostic = this.reflector.getAllAndOverride( + IS_TENANT_AGNOSTIC, + [context.getHandler(), context.getClass()], + ); + // Skip the guard early if the route marked as public, tenant agnostic or ignored. + if (isPublic || isIgnoreEnsureTenantSeeded || isTenantAgnostic) { return true; } const tenant = await this.tenancyContext.getTenant(); diff --git a/packages/webapp/src/hooks/query/authentication.tsx b/packages/webapp/src/hooks/query/authentication.tsx index 657729d31..c6aa42df8 100644 --- a/packages/webapp/src/hooks/query/authentication.tsx +++ b/packages/webapp/src/hooks/query/authentication.tsx @@ -128,7 +128,7 @@ export const useAuthMetadata = (props = {}) => { * Resend the mail of signup verification. */ export const useAuthSignUpVerifyResendMail = (props) => { - const apiRequest = useAuthApiRequest(); + const apiRequest = useApiRequest(); return useMutation( () => apiRequest.post(AuthRoute.SignupVerifyResend), From fcee85e358637c894cbba05870ca4af1c9bbde4c Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Mon, 23 Feb 2026 00:48:32 +0200 Subject: [PATCH 2/3] fix: add dark mode support to email confirmation UI Refactored RegisterVerify component to use xstyled for styling with proper dark mode color values instead of hardcoded light theme colors. --- .../Authentication/RegisterVerify.module.scss | 18 -------------- .../Authentication/RegisterVerify.tsx | 24 +++++++++++++++---- 2 files changed, 19 insertions(+), 23 deletions(-) delete mode 100644 packages/webapp/src/containers/Authentication/RegisterVerify.module.scss diff --git a/packages/webapp/src/containers/Authentication/RegisterVerify.module.scss b/packages/webapp/src/containers/Authentication/RegisterVerify.module.scss deleted file mode 100644 index f0754f470..000000000 --- a/packages/webapp/src/containers/Authentication/RegisterVerify.module.scss +++ /dev/null @@ -1,18 +0,0 @@ - -.root { - text-align: center; -} - -.title{ - font-size: 18px; - font-weight: 600; - margin-bottom: 0.5rem; - color: #252A31; -} - -.description{ - margin-bottom: 1rem; - font-size: 15px; - line-height: 1.45; - color: #404854; -} \ No newline at end of file diff --git a/packages/webapp/src/containers/Authentication/RegisterVerify.tsx b/packages/webapp/src/containers/Authentication/RegisterVerify.tsx index 46148a426..d022ff454 100644 --- a/packages/webapp/src/containers/Authentication/RegisterVerify.tsx +++ b/packages/webapp/src/containers/Authentication/RegisterVerify.tsx @@ -1,12 +1,13 @@ // @ts-nocheck import { Button, Intent } from '@blueprintjs/core'; +import { x } from '@xstyled/emotion'; import AuthInsider from './AuthInsider'; import { AuthInsiderCard } from './_components'; -import styles from './RegisterVerify.module.scss'; import { AppToaster, Stack } from '@/components'; import { useAuthActions, useAuthUserVerifyEmail } from '@/hooks/state'; import { useAuthSignUpVerifyResendMail } from '@/hooks/query'; import { AuthContainer } from './AuthContainer'; +import { useIsDarkMode } from '@/hooks/useDarkMode'; export default function RegisterVerify() { const { setLogout } = useAuthActions(); @@ -14,6 +15,7 @@ export default function RegisterVerify() { useAuthSignUpVerifyResendMail(); const emailAddress = useAuthUserVerifyEmail(); + const isDarkMode = useIsDarkMode(); const handleResendMailBtnClick = () => { resendSignUpVerifyMail() @@ -37,12 +39,24 @@ export default function RegisterVerify() { return ( - -

Please verify your email

-

+ + + Please verify your email + + We sent an email to {emailAddress} Click the link inside to get started. -

+