Fix email verification exception are not caught (#12174)
cf [sentry](https://twenty-v7.sentry.io/issues/6606375182/?project=4507072499810304&query=is%3Aunresolved%20%21issue.type%3A%5Bperformance_consecutive_db_queries%2Cperformance_consecutive_http%2Cperformance_file_io_main_thread%2Cperformance_db_main_thread%2Cperformance_n_plus_one_db_queries%2Cperformance_n_plus_one_api_calls%2Cperformance_p95_endpoint_regression%2Cperformance_slow_db_query%2Cperformance_render_blocking_asset_span%2Cperformance_uncompressed_assets%2Cperformance_http_overhead%2Cperformance_large_http_payload%5D&referrer=issue-stream&sort=date&stream_index=4) Email verification exception were not caught when thrown outside of email-verification resolver, which be the case at authentication step.
This commit is contained in:
@ -37,6 +37,7 @@ import { RenewTokenService } from 'src/engine/core-modules/auth/token/services/r
|
|||||||
import { TransientTokenService } from 'src/engine/core-modules/auth/token/services/transient-token.service';
|
import { TransientTokenService } from 'src/engine/core-modules/auth/token/services/transient-token.service';
|
||||||
import { CaptchaGuard } from 'src/engine/core-modules/captcha/captcha.guard';
|
import { CaptchaGuard } from 'src/engine/core-modules/captcha/captcha.guard';
|
||||||
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
|
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
|
||||||
|
import { EmailVerificationExceptionFilter } from 'src/engine/core-modules/email-verification/email-verification-exception-filter.util';
|
||||||
import { EmailVerificationService } from 'src/engine/core-modules/email-verification/services/email-verification.service';
|
import { EmailVerificationService } from 'src/engine/core-modules/email-verification/services/email-verification.service';
|
||||||
import { I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type';
|
import { I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type';
|
||||||
import { SSOService } from 'src/engine/core-modules/sso/services/sso.service';
|
import { SSOService } from 'src/engine/core-modules/sso/services/sso.service';
|
||||||
@ -66,7 +67,11 @@ import { WorkspaceInviteHashValidInput } from './dto/workspace-invite-hash.input
|
|||||||
import { AuthService } from './services/auth.service';
|
import { AuthService } from './services/auth.service';
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
@UseFilters(AuthGraphqlApiExceptionFilter, PermissionsGraphqlApiExceptionFilter)
|
@UseFilters(
|
||||||
|
AuthGraphqlApiExceptionFilter,
|
||||||
|
PermissionsGraphqlApiExceptionFilter,
|
||||||
|
EmailVerificationExceptionFilter,
|
||||||
|
)
|
||||||
export class AuthResolver {
|
export class AuthResolver {
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(User, 'core')
|
@InjectRepository(User, 'core')
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { Catch, ExceptionFilter } from '@nestjs/common';
|
||||||
|
|
||||||
|
import {
|
||||||
|
EmailVerificationException,
|
||||||
|
EmailVerificationExceptionCode,
|
||||||
|
} from 'src/engine/core-modules/email-verification/email-verification.exception';
|
||||||
|
import {
|
||||||
|
ForbiddenError,
|
||||||
|
UserInputError,
|
||||||
|
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||||
|
|
||||||
|
@Catch(EmailVerificationException)
|
||||||
|
export class EmailVerificationExceptionFilter implements ExceptionFilter {
|
||||||
|
catch(exception: EmailVerificationException) {
|
||||||
|
switch (exception.code) {
|
||||||
|
case EmailVerificationExceptionCode.INVALID_TOKEN:
|
||||||
|
case EmailVerificationExceptionCode.INVALID_APP_TOKEN_TYPE:
|
||||||
|
case EmailVerificationExceptionCode.TOKEN_EXPIRED:
|
||||||
|
case EmailVerificationExceptionCode.RATE_LIMIT_EXCEEDED:
|
||||||
|
throw new ForbiddenError(exception.message);
|
||||||
|
case EmailVerificationExceptionCode.EMAIL_MISSING:
|
||||||
|
case EmailVerificationExceptionCode.EMAIL_ALREADY_VERIFIED:
|
||||||
|
case EmailVerificationExceptionCode.EMAIL_VERIFICATION_NOT_REQUIRED:
|
||||||
|
throw new UserInputError(exception.message);
|
||||||
|
default: {
|
||||||
|
const _exhaustiveCheck: never = exception.code;
|
||||||
|
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,29 +0,0 @@
|
|||||||
import {
|
|
||||||
EmailVerificationException,
|
|
||||||
EmailVerificationExceptionCode,
|
|
||||||
} from 'src/engine/core-modules/email-verification/email-verification.exception';
|
|
||||||
import {
|
|
||||||
ForbiddenError,
|
|
||||||
UserInputError,
|
|
||||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
|
||||||
|
|
||||||
export const emailVerificationGraphqlApiExceptionHandler = (
|
|
||||||
error: EmailVerificationException,
|
|
||||||
) => {
|
|
||||||
switch (error.code) {
|
|
||||||
case EmailVerificationExceptionCode.INVALID_TOKEN:
|
|
||||||
case EmailVerificationExceptionCode.INVALID_APP_TOKEN_TYPE:
|
|
||||||
case EmailVerificationExceptionCode.TOKEN_EXPIRED:
|
|
||||||
case EmailVerificationExceptionCode.RATE_LIMIT_EXCEEDED:
|
|
||||||
throw new ForbiddenError(error.message);
|
|
||||||
case EmailVerificationExceptionCode.EMAIL_MISSING:
|
|
||||||
case EmailVerificationExceptionCode.EMAIL_ALREADY_VERIFIED:
|
|
||||||
case EmailVerificationExceptionCode.EMAIL_VERIFICATION_NOT_REQUIRED:
|
|
||||||
throw new UserInputError(error.message);
|
|
||||||
default: {
|
|
||||||
const _exhaustiveCheck: never = error.code;
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { UseFilters } from '@nestjs/common';
|
||||||
import { Args, Context, Mutation, Resolver } from '@nestjs/graphql';
|
import { Args, Context, Mutation, Resolver } from '@nestjs/graphql';
|
||||||
|
|
||||||
import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||||
@ -5,13 +6,13 @@ import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
|||||||
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
|
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
|
||||||
import { ResendEmailVerificationTokenInput } from 'src/engine/core-modules/email-verification/dtos/resend-email-verification-token.input';
|
import { ResendEmailVerificationTokenInput } from 'src/engine/core-modules/email-verification/dtos/resend-email-verification-token.input';
|
||||||
import { ResendEmailVerificationTokenOutput } from 'src/engine/core-modules/email-verification/dtos/resend-email-verification-token.output';
|
import { ResendEmailVerificationTokenOutput } from 'src/engine/core-modules/email-verification/dtos/resend-email-verification-token.output';
|
||||||
import { emailVerificationGraphqlApiExceptionHandler } from 'src/engine/core-modules/email-verification/email-verification-exception-handler.util';
|
import { EmailVerificationExceptionFilter } from 'src/engine/core-modules/email-verification/email-verification-exception-filter.util';
|
||||||
import { EmailVerificationException } from 'src/engine/core-modules/email-verification/email-verification.exception';
|
|
||||||
import { EmailVerificationService } from 'src/engine/core-modules/email-verification/services/email-verification.service';
|
import { EmailVerificationService } from 'src/engine/core-modules/email-verification/services/email-verification.service';
|
||||||
import { I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type';
|
import { I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type';
|
||||||
import { OriginHeader } from 'src/engine/decorators/auth/origin-header.decorator';
|
import { OriginHeader } from 'src/engine/decorators/auth/origin-header.decorator';
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
|
@UseFilters(EmailVerificationExceptionFilter)
|
||||||
export class EmailVerificationResolver {
|
export class EmailVerificationResolver {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly emailVerificationService: EmailVerificationService,
|
private readonly emailVerificationService: EmailVerificationService,
|
||||||
@ -25,22 +26,15 @@ export class EmailVerificationResolver {
|
|||||||
@OriginHeader() origin: string,
|
@OriginHeader() origin: string,
|
||||||
@Context() context: I18nContext,
|
@Context() context: I18nContext,
|
||||||
): Promise<ResendEmailVerificationTokenOutput> {
|
): Promise<ResendEmailVerificationTokenOutput> {
|
||||||
try {
|
const workspace =
|
||||||
const workspace =
|
await this.domainManagerService.getWorkspaceByOriginOrDefaultWorkspace(
|
||||||
await this.domainManagerService.getWorkspaceByOriginOrDefaultWorkspace(
|
origin,
|
||||||
origin,
|
|
||||||
);
|
|
||||||
|
|
||||||
return await this.emailVerificationService.resendEmailVerificationToken(
|
|
||||||
resendEmailVerificationTokenInput.email,
|
|
||||||
workspace,
|
|
||||||
context.req.headers['x-locale'] ?? SOURCE_LOCALE,
|
|
||||||
);
|
);
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof EmailVerificationException) {
|
return await this.emailVerificationService.resendEmailVerificationToken(
|
||||||
return emailVerificationGraphqlApiExceptionHandler(error);
|
resendEmailVerificationTokenInput.email,
|
||||||
}
|
workspace,
|
||||||
throw error;
|
context.req.headers['x-locale'] ?? SOURCE_LOCALE,
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user