Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 4ef4f87cf4 fix(email-verification): add EMAIL_UPDATE trigger support to resend flow
https://sonarly.com/issue/33360?type=bug

The `resendEmailVerificationToken` method hardcodes `EmailVerificationTrigger.SIGN_UP`, so users who updated their email and request a resend receive "Welcome to Twenty: Please Confirm Your Email" instead of "Please confirm your updated email".

Fix: **What changed:** Added support for an optional `verificationTrigger` parameter in the resend email verification flow, threading it from the GraphQL mutation through the resolver to the service layer.

**6 files changed:**

1. **`email-verification.constants.ts`** — Added `registerEnumType(EmailVerificationTrigger, ...)` so the enum is exposed as a GraphQL enum type. This follows the same pattern used for `WorkspaceActivationStatus`, `AnalyticsType`, `OnboardingStatus`, and other enums in the codebase.

2. **`resend-email-verification-token.input.ts`** — Added an optional `verificationTrigger` field to the DTO with `@Field(() => EmailVerificationTrigger, { nullable: true })`, `@IsOptional()`, and `@IsEnum(EmailVerificationTrigger)` decorators.

3. **`email-verification.resolver.ts`** — Passes `resendEmailVerificationTokenInput.verificationTrigger` as a fourth argument to the service method.

4. **`email-verification.service.ts`** — The `resendEmailVerificationToken` method now accepts a `verificationTrigger` parameter (defaulting to `SIGN_UP`) and passes it to `sendVerificationEmail` instead of the hardcoded value.

5. **`resendEmailVerificationToken.ts` (frontend mutation)** — Added `$verificationTrigger: EmailVerificationTrigger` variable to the GraphQL mutation.

6. **`useHandleResendEmailVerificationToken.ts` (frontend hook)** — Added an optional `verificationTrigger` parameter that gets spread into the mutation variables when provided.

**Backward compatibility:** All existing callers (in `EmailVerificationSent.tsx`) pass only `email` — the trigger defaults to `SIGN_UP`, preserving the current sign-up verification behavior. The hook is now ready for email update contexts to pass `'EMAIL_UPDATE'`.

**Note:** After merging, `npx nx run twenty-front:graphql:generate --configuration=metadata` should be run to regenerate the frontend GraphQL types to include the new enum and mutation argument.
2026-05-01 17:31:02 +00:00
6 changed files with 29 additions and 5 deletions
@@ -1,8 +1,16 @@
import { gql } from '@apollo/client';
export const RESEND_EMAIL_VERIFICATION_TOKEN = gql`
mutation ResendEmailVerificationToken($email: String!, $origin: String!) {
resendEmailVerificationToken(email: $email, origin: $origin) {
mutation ResendEmailVerificationToken(
$email: String!
$origin: String!
$verificationTrigger: EmailVerificationTrigger
) {
resendEmailVerificationToken(
email: $email
origin: $origin
verificationTrigger: $verificationTrigger
) {
success
}
}
@@ -15,7 +15,7 @@ export const useHandleResendEmailVerificationToken = () => {
const { origin } = useOrigin();
const handleResendEmailVerificationToken = useCallback(
(email: string | null) => {
(email: string | null, verificationTrigger?: string) => {
return async () => {
if (!email) {
enqueueErrorSnackBar({
@@ -29,6 +29,7 @@ export const useHandleResendEmailVerificationToken = () => {
variables: {
email,
origin,
...(verificationTrigger ? { verificationTrigger } : {}),
},
});
@@ -1,6 +1,8 @@
import { ArgsType, Field } from '@nestjs/graphql';
import { IsEmail, IsNotEmpty } from 'class-validator';
import { IsEmail, IsEnum, IsNotEmpty, IsOptional } from 'class-validator';
import { EmailVerificationTrigger } from 'src/engine/core-modules/email-verification/email-verification.constants';
@ArgsType()
export class ResendEmailVerificationTokenInput {
@@ -8,4 +10,9 @@ export class ResendEmailVerificationTokenInput {
@IsEmail()
@IsNotEmpty()
email: string;
@Field(() => EmailVerificationTrigger, { nullable: true })
@IsOptional()
@IsEnum(EmailVerificationTrigger)
verificationTrigger?: EmailVerificationTrigger;
}
@@ -1,4 +1,10 @@
import { registerEnumType } from '@nestjs/graphql';
export enum EmailVerificationTrigger {
SIGN_UP = 'SIGN_UP',
EMAIL_UPDATE = 'EMAIL_UPDATE',
}
registerEnumType(EmailVerificationTrigger, {
name: 'EmailVerificationTrigger',
});
@@ -43,6 +43,7 @@ export class EmailVerificationResolver {
resendEmailVerificationTokenInput.email,
workspace,
context.req.locale,
resendEmailVerificationTokenInput.verificationTrigger,
);
}
}
@@ -123,6 +123,7 @@ export class EmailVerificationService {
email: string,
workspace: WorkspaceDomainConfig | undefined,
locale: keyof typeof APP_LOCALES,
verificationTrigger: EmailVerificationTrigger = EmailVerificationTrigger.SIGN_UP,
) {
if (!this.twentyConfigService.get('IS_EMAIL_VERIFICATION_REQUIRED')) {
throw new EmailVerificationException(
@@ -176,7 +177,7 @@ export class EmailVerificationService {
email,
workspace,
locale,
verificationTrigger: EmailVerificationTrigger.SIGN_UP,
verificationTrigger,
});
return { success: true };