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:
@ -420,7 +420,8 @@ export enum ConfigVariablesGroup {
|
||||
ServerlessConfig = 'ServerlessConfig',
|
||||
StorageConfig = 'StorageConfig',
|
||||
SupportChatConfig = 'SupportChatConfig',
|
||||
TokensDuration = 'TokensDuration'
|
||||
TokensDuration = 'TokensDuration',
|
||||
TwoFactorAuthentication = 'TwoFactorAuthentication'
|
||||
}
|
||||
|
||||
export type ConfigVariablesGroupData = {
|
||||
@ -616,6 +617,12 @@ export type DeleteSsoOutput = {
|
||||
identityProviderId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type DeleteTwoFactorAuthenticationMethodOutput = {
|
||||
__typename?: 'DeleteTwoFactorAuthenticationMethodOutput';
|
||||
/** Boolean that confirms query was dispatched */
|
||||
success: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type DeleteWebhookDto = {
|
||||
id: Scalars['String'];
|
||||
};
|
||||
@ -704,6 +711,7 @@ export enum FeatureFlagKey {
|
||||
IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED',
|
||||
IS_RELATION_CONNECT_ENABLED = 'IS_RELATION_CONNECT_ENABLED',
|
||||
IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED',
|
||||
IS_TWO_FACTOR_AUTHENTICATION_ENABLED = 'IS_TWO_FACTOR_AUTHENTICATION_ENABLED',
|
||||
IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED',
|
||||
IS_WORKFLOW_FILTERING_ENABLED = 'IS_WORKFLOW_FILTERING_ENABLED',
|
||||
IS_WORKSPACE_API_KEY_WEBHOOK_GRAPHQL_ENABLED = 'IS_WORKSPACE_API_KEY_WEBHOOK_GRAPHQL_ENABLED'
|
||||
@ -1006,6 +1014,11 @@ export enum IndexType {
|
||||
GIN = 'GIN'
|
||||
}
|
||||
|
||||
export type InitiateTwoFactorAuthenticationProvisioningOutput = {
|
||||
__typename?: 'InitiateTwoFactorAuthenticationProvisioningOutput';
|
||||
uri: Scalars['String'];
|
||||
};
|
||||
|
||||
export type InvalidatePassword = {
|
||||
__typename?: 'InvalidatePassword';
|
||||
/** Boolean that confirms query was dispatched */
|
||||
@ -1078,6 +1091,7 @@ export type Mutation = {
|
||||
deleteOneRole: Scalars['String'];
|
||||
deleteOneServerlessFunction: ServerlessFunction;
|
||||
deleteSSOIdentityProvider: DeleteSsoOutput;
|
||||
deleteTwoFactorAuthenticationMethod: DeleteTwoFactorAuthenticationMethodOutput;
|
||||
deleteUser: User;
|
||||
deleteWebhook: Scalars['Boolean'];
|
||||
deleteWorkflowVersionStep: WorkflowAction;
|
||||
@ -1091,10 +1105,13 @@ export type Mutation = {
|
||||
generateApiKeyToken: ApiKeyToken;
|
||||
generateTransientToken: TransientToken;
|
||||
getAuthTokensFromLoginToken: AuthTokens;
|
||||
getAuthTokensFromOTP: AuthTokens;
|
||||
getAuthorizationUrlForSSO: GetAuthorizationUrlForSsoOutput;
|
||||
getLoginTokenFromCredentials: LoginToken;
|
||||
getLoginTokenFromEmailVerificationToken: GetLoginTokenFromEmailVerificationTokenOutput;
|
||||
impersonate: ImpersonateOutput;
|
||||
initiateOTPProvisioning: InitiateTwoFactorAuthenticationProvisioningOutput;
|
||||
initiateOTPProvisioningForAuthenticatedUser: InitiateTwoFactorAuthenticationProvisioningOutput;
|
||||
publishServerlessFunction: ServerlessFunction;
|
||||
removeRoleFromAgent: Scalars['Boolean'];
|
||||
renewToken: AuthTokens;
|
||||
@ -1138,6 +1155,7 @@ export type Mutation = {
|
||||
upsertSettingPermissions: Array<SettingPermission>;
|
||||
userLookupAdminPanel: UserLookup;
|
||||
validateApprovedAccessDomain: ApprovedAccessDomain;
|
||||
verifyTwoFactorAuthenticationMethodForAuthenticatedUser: VerifyTwoFactorAuthenticationMethodOutput;
|
||||
};
|
||||
|
||||
|
||||
@ -1296,6 +1314,11 @@ export type MutationDeleteSsoIdentityProviderArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteTwoFactorAuthenticationMethodArgs = {
|
||||
twoFactorAuthenticationMethodId: Scalars['UUID'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteWebhookArgs = {
|
||||
input: DeleteWebhookDto;
|
||||
};
|
||||
@ -1339,6 +1362,14 @@ export type MutationGetAuthTokensFromLoginTokenArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationGetAuthTokensFromOtpArgs = {
|
||||
captchaToken?: InputMaybe<Scalars['String']>;
|
||||
loginToken: Scalars['String'];
|
||||
origin: Scalars['String'];
|
||||
otp: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationGetAuthorizationUrlForSsoArgs = {
|
||||
input: GetAuthorizationUrlForSsoInput;
|
||||
};
|
||||
@ -1366,6 +1397,12 @@ export type MutationImpersonateArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationInitiateOtpProvisioningArgs = {
|
||||
loginToken: Scalars['String'];
|
||||
origin: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationPublishServerlessFunctionArgs = {
|
||||
input: PublishServerlessFunctionInput;
|
||||
};
|
||||
@ -1580,6 +1617,11 @@ export type MutationValidateApprovedAccessDomainArgs = {
|
||||
input: ValidateApprovedAccessDomainInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationVerifyTwoFactorAuthenticationMethodForAuthenticatedUserArgs = {
|
||||
otp: Scalars['String'];
|
||||
};
|
||||
|
||||
export type Object = {
|
||||
__typename?: 'Object';
|
||||
createdAt: Scalars['DateTime'];
|
||||
@ -2376,6 +2418,13 @@ export type TransientToken = {
|
||||
transientToken: AuthToken;
|
||||
};
|
||||
|
||||
export type TwoFactorAuthenticationMethodDto = {
|
||||
__typename?: 'TwoFactorAuthenticationMethodDTO';
|
||||
status: Scalars['String'];
|
||||
strategy: Scalars['String'];
|
||||
twoFactorAuthenticationMethodId: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type UuidFilter = {
|
||||
eq?: InputMaybe<Scalars['UUID']>;
|
||||
gt?: InputMaybe<Scalars['UUID']>;
|
||||
@ -2525,6 +2574,7 @@ export type UpdateWorkspaceInput = {
|
||||
isMicrosoftAuthEnabled?: InputMaybe<Scalars['Boolean']>;
|
||||
isPasswordAuthEnabled?: InputMaybe<Scalars['Boolean']>;
|
||||
isPublicInviteLinkEnabled?: InputMaybe<Scalars['Boolean']>;
|
||||
isTwoFactorAuthenticationEnforced?: InputMaybe<Scalars['Boolean']>;
|
||||
logo?: InputMaybe<Scalars['String']>;
|
||||
subdomain?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
@ -2610,6 +2660,7 @@ export type UserWorkspace = {
|
||||
/** @deprecated Use objectPermissions instead */
|
||||
objectRecordsPermissions?: Maybe<Array<PermissionsOnAllObjectRecords>>;
|
||||
settingsPermissions?: Maybe<Array<SettingPermissionType>>;
|
||||
twoFactorAuthenticationMethodSummary?: Maybe<Array<TwoFactorAuthenticationMethodDto>>;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
user: User;
|
||||
userId: Scalars['String'];
|
||||
@ -2628,6 +2679,11 @@ export type ValidatePasswordResetToken = {
|
||||
id: Scalars['String'];
|
||||
};
|
||||
|
||||
export type VerifyTwoFactorAuthenticationMethodOutput = {
|
||||
__typename?: 'VerifyTwoFactorAuthenticationMethodOutput';
|
||||
success: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type VersionInfo = {
|
||||
__typename?: 'VersionInfo';
|
||||
currentVersion?: Maybe<Scalars['String']>;
|
||||
@ -2703,6 +2759,7 @@ export type Workspace = {
|
||||
isMicrosoftAuthEnabled: Scalars['Boolean'];
|
||||
isPasswordAuthEnabled: Scalars['Boolean'];
|
||||
isPublicInviteLinkEnabled: Scalars['Boolean'];
|
||||
isTwoFactorAuthenticationEnforced: Scalars['Boolean'];
|
||||
logo?: Maybe<Scalars['String']>;
|
||||
metadataVersion: Scalars['Float'];
|
||||
subdomain: Scalars['String'];
|
||||
|
||||
Reference in New Issue
Block a user