BREAKING - feat(auth): refactor tokens logic & enhance email verification flow (#13487)

- Replaced `getAuthTokensFromLoginToken` with
`getAccessTokensFromLoginToken` for clarity.
- Introduced `getWorkspaceAgnosticTokenFromEmailVerificationToken`.
- Extended mutation inputs to include `locale` and
`verifyEmailNextPath`.
- Added email verification check and sending to various handlers.
- Updated GraphQL types and hooks to reflect these changes.


Fix #13412

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Antoine Moreaux
2025-07-31 12:00:59 +02:00
committed by GitHub
parent 463e2e89c8
commit 00d12e854a
53 changed files with 594 additions and 306 deletions
@@ -2,8 +2,12 @@ import { CustomException } from 'src/utils/custom-exception';
export class EmailVerificationException extends CustomException {
declare code: EmailVerificationExceptionCode;
constructor(message: string, code: EmailVerificationExceptionCode) {
super(message, code);
constructor(
message: string,
code: EmailVerificationExceptionCode,
{ userFriendlyMessage }: { userFriendlyMessage?: string } = {},
) {
super(message, code, userFriendlyMessage ?? message);
}
}
@@ -46,7 +46,7 @@ export class EmailVerificationService {
| WorkspaceSubdomainCustomDomainAndIsCustomDomainEnabledType
| undefined,
locale: keyof typeof APP_LOCALES,
verifyEmailNextPath?: string,
verifyEmailRedirectPath?: string,
) {
if (!this.twentyConfigService.get('IS_EMAIL_VERIFICATION_REQUIRED')) {
return { success: false };
@@ -60,8 +60,8 @@ export class EmailVerificationService {
searchParams: {
emailVerificationToken,
email,
...(isDefined(verifyEmailNextPath)
? { nextPath: verifyEmailNextPath }
...(isDefined(verifyEmailRedirectPath)
? { nextPath: verifyEmailRedirectPath }
: {}),
},
};
@@ -79,13 +79,12 @@ export class EmailVerificationService {
const emailTemplate = SendEmailVerificationLinkEmail(emailData);
const html = await render(emailTemplate);
const text = await render(emailTemplate, {
const html = render(emailTemplate);
const text = render(emailTemplate, {
plainText: true,
});
i18n.activate(locale);
await this.emailService.send({
from: `${this.twentyConfigService.get(
'EMAIL_FROM_NAME',