[Billing for self host] End dummy enterprise key validity (#19560)

<img width="1504" height="755" alt="Screenshot 2026-04-10 at 16 40 07"
src="https://github.com/user-attachments/assets/68a12e40-a077-48df-9e18-885493520a32"
/>


Re-using hasValidEnterpriseKey to avoid breaking changes. 
This will be entirely removed in the next versions.
This commit is contained in:
Marie
2026-04-15 15:26:38 +02:00
committed by GitHub
parent 762fb6fd64
commit 2fccd194f3
9 changed files with 140 additions and 50 deletions
@@ -286,6 +286,7 @@ describe('EnterprisePlanService', () => {
});
});
// hasValidEnterpriseKey now means "has any ENTERPRISE_KEY configured"
describe('hasValidEnterpriseKey', () => {
it('should return true when signed enterprise key is valid', async () => {
await setupValidState();
@@ -293,13 +294,12 @@ describe('EnterprisePlanService', () => {
expect(service.hasValidEnterpriseKey()).toBe(true);
});
it('should return true with legacy unsigned key as fallback', async () => {
it('should return true with unsigned legacy key', async () => {
setupEnterpriseKey('some-legacy-key');
mockCryptoVerify.mockReturnValue(false);
appTokenFindOneMock.mockResolvedValue(null);
await service.onModuleInit();
expect(service.hasValidSignedEnterpriseKey()).toBe(false);
expect(service.hasValidEnterpriseKey()).toBe(true);
});
@@ -318,13 +318,13 @@ describe('EnterprisePlanService', () => {
expect(service.isValid()).toBe(true);
});
it('should return true with legacy key as fallback', async () => {
it('should return false with unsigned legacy key', async () => {
setupEnterpriseKey('some-legacy-key');
mockCryptoVerify.mockReturnValue(false);
appTokenFindOneMock.mockResolvedValue(null);
await service.onModuleInit();
expect(service.isValid()).toBe(true);
expect(service.isValid()).toBe(false);
});
it('should return false when no key or token exists', async () => {
@@ -399,7 +399,7 @@ describe('EnterprisePlanService', () => {
});
});
it('should return legacy license info when only legacy key exists', async () => {
it('should return invalid license info when only unsigned legacy key exists', async () => {
setupEnterpriseKey('some-legacy-key');
mockCryptoVerify.mockReturnValue(false);
appTokenFindOneMock.mockResolvedValue(null);
@@ -407,7 +407,7 @@ describe('EnterprisePlanService', () => {
const licenseInfo = await service.getLicenseInfo();
expect(licenseInfo).toEqual({
isValid: true,
isValid: false,
licensee: null,
expiresAt: null,
subscriptionId: null,
@@ -143,40 +143,22 @@ export class EnterprisePlanService implements OnModuleInit {
}
hasValidEnterpriseKey(): boolean {
if (this.hasValidSignedEnterpriseKey()) {
return true;
}
return this.checkLegacyKey();
return this.hasValidSignedEnterpriseKey() || this.checkLegacyKey();
}
isValid(): boolean {
if (this.hasValidEnterpriseValidityToken()) {
return true;
}
return this.hasValidEnterpriseValidityToken();
}
return this.checkLegacyKey(); // temporary
private checkLegacyKey(): boolean {
// temporary
return isDefined(this.twentyConfigService.get('ENTERPRISE_KEY'));
}
isValidEnterpriseKeyFormat(key: string): boolean {
return this.verifyJwt<EnterpriseKeyPayload>(key) !== null;
}
private checkLegacyKey(): boolean {
const enterpriseKey = this.twentyConfigService.get('ENTERPRISE_KEY');
if (!isDefined(enterpriseKey)) {
return false;
}
this.logger.warn(
'Unsigned enterprise keys are deprecated and will stop working ' +
'in a future version. Please obtain a signed key from twenty.com.',
);
return true;
}
async getLicenseInfo(): Promise<EnterpriseLicenseInfo> {
this.refreshKeyPayload();
await this.loadValidityToken();
@@ -192,15 +174,6 @@ export class EnterprisePlanService implements OnModuleInit {
};
}
if (this.checkLegacyKey()) {
return {
isValid: true,
licensee: null,
expiresAt: null,
subscriptionId: null,
};
}
return {
isValid: false,
licensee: null,
@@ -477,11 +450,15 @@ export class EnterprisePlanService implements OnModuleInit {
}
}
// In development, try both keys so production keys work when testing locally
// In development and Jest integration tests, try both keys so production keys
// work locally
private getPublicKeysToTry(): string[] {
const nodeEnv = this.twentyConfigService.get('NODE_ENV');
if (nodeEnv === NodeEnvironment.DEVELOPMENT) {
if (
nodeEnv === NodeEnvironment.DEVELOPMENT ||
nodeEnv === NodeEnvironment.TEST
) {
return [ENTERPRISE_JWT_PUBLIC_KEY, ENTERPRISE_JWT_DEV_PUBLIC_KEY];
}