Deprecate legacy encryption (#21831)

# Introduction
Still preserving the cross-upgrade flow

close https://github.com/twentyhq/core-team-issues/issues/2465


<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21831?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Paul Rastoin
2026-06-19 13:32:39 +02:00
committed by GitHub
parent 4de9f45015
commit 26db3f5735
32 changed files with 206 additions and 249 deletions
@@ -1,4 +1,4 @@
import { Injectable, Logger } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import { isDefined } from 'twenty-shared/utils';
@@ -20,10 +20,6 @@ import { ACCOUNT_TYPES } from 'twenty-shared/constants';
@Injectable()
export class ConnectedAccountTokenEncryptionService {
private readonly logger = new Logger(
ConnectedAccountTokenEncryptionService.name,
);
constructor(
private readonly secretEncryptionService: SecretEncryptionService,
) {}
@@ -75,7 +71,7 @@ export class ConnectedAccountTokenEncryptionService {
);
}
return this.secretEncryptionService.decryptVersioned(ciphertext, {
return this.secretEncryptionService.decryptVersionedOrThrow(ciphertext, {
workspaceId,
});
}
@@ -179,30 +175,6 @@ export class ConnectedAccountTokenEncryptionService {
protocolParams: EncryptedConnectionParameters;
workspaceId: string;
}): PlaintextConnectionParameters {
const isEncrypted = protocolParams.password.startsWith(
SECRET_ENCRYPTION_ENVELOPE_PREFIX,
);
// TODO: Remove in follow-up PR once all legacy encryption fallbacks are dropped.
// TODO: Remove after 2-5 slow instance command has been run everywhere.
// During the rollout window protocolParams.password may be a legacy
// unencrypted plaintext value living in the same column. We trust the
// entity-level brand at the type layer (column is EncryptedString) but
// still re-validate at runtime to handle the un-backfilled tail; the
// assert above splits the two.
if (!isEncrypted) {
this.logger.warn(
'Protocol password is not encrypted. Expected during the rollout window until the slow instance command finishes backfilling.',
);
const rawPassword: string = protocolParams.password;
return {
...protocolParams,
password: rawPassword as PlaintextString,
};
}
return {
...protocolParams,
password: this.decrypt({