Implement Two-Factor Authentication (2FA) (#13141)

Implementation is very simple

Established authentication dynamic is intercepted at
getAuthTokensFromLoginToken. If 2FA is required, a pattern similar to
EmailVerification is executed. That is, getAuthTokensFromLoginToken
mutation fails with either of the following errors:

1. TWO_FACTOR_AUTHENTICATION_VERIFICATION_REQUIRED
2. TWO_FACTOR_AUTHENTICATION_PROVISION_REQUIRED

UI knows how to respond accordingly.

2FA provisioning occurs at the 2FA resolver.
2FA verification, currently only OTP, is handled by auth.resolver's
getAuthTokensFromOTP

---------

Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Jean-Baptiste Ronssin <65334819+jbronssin@users.noreply.github.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.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:
oliver
2025-07-23 06:42:01 -06:00
committed by GitHub
parent dd5ae66449
commit 4d3124f840
106 changed files with 5103 additions and 103 deletions
@@ -9,6 +9,7 @@ import {
ValidationError,
validateSync,
} from 'class-validator';
import { TwoFactorAuthenticationStrategy } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { AwsRegion } from 'src/engine/core-modules/twenty-config/interfaces/aws-region.interface';
@@ -66,6 +67,16 @@ export class ConfigVariables {
@IsOptional()
IS_EMAIL_VERIFICATION_REQUIRED = false;
@ConfigVariablesMetadata({
group: ConfigVariablesGroup.TwoFactorAuthentication,
description:
'Select the two-factor authentication strategy (e.g., TOTP or HOTP) to be used for workspace logins.',
type: ConfigVariableType.ENUM,
options: Object.values(TwoFactorAuthenticationStrategy),
})
@IsOptional()
TWO_FACTOR_AUTHENTICATION_STRATEGY = TwoFactorAuthenticationStrategy.TOTP;
@ConfigVariablesMetadata({
group: ConfigVariablesGroup.TokensDuration,
description: 'Duration for which the email verification token is valid',
@@ -119,4 +119,10 @@ export const CONFIG_VARIABLES_GROUP_METADATA: Record<
'These have been set to sensible default so you probably dont need to change them unless you have a specific use-case.',
isHiddenOnLoad: true,
},
[ConfigVariablesGroup.TwoFactorAuthentication]: {
position: 2000,
description:
'These have been set to sensible default so you probably dont need to change them unless you have a specific use-case.',
isHiddenOnLoad: true,
},
};
@@ -18,4 +18,5 @@ export enum ConfigVariablesGroup {
SupportChatConfig = 'support-chat-config',
AnalyticsConfig = 'audit-config',
TokensDuration = 'tokens-duration',
TwoFactorAuthentication = 'two-factor-authentication',
}