Skip to content

Commit 47fb852

Browse files
authored
💥 TLS enabled if API key is provided (#1847)
1 parent 716a8ad commit 47fb852

File tree

7 files changed

+42
-4
lines changed

7 files changed

+42
-4
lines changed

‎packages/client/src/connection.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ function normalizeGRPCConfig(options: ConnectionOptions): ConnectionOptions {
210210
if (rest.address) {
211211
rest.address = normalizeGrpcEndpointAddress(rest.address, DEFAULT_TEMPORAL_GRPC_PORT);
212212
}
213-
const tls = normalizeTlsConfig(tlsFromConfig);
213+
const tls = normalizeTlsConfig(tlsFromConfig, options.apiKey);
214214
if (tls) {
215215
if (credentials) {
216216
throw new TypeError('Both `tls` and `credentials` ConnectionOptions were provided');

‎packages/cloud/src/cloud-operations-client.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ function normalizeGRPCConfig(options: CloudOperationsConnectionOptions): Resolve
229229
} = options;
230230

231231
const address = addressFromConfig ? normalizeGrpcEndpointAddress(addressFromConfig, 443) : 'saas-api.tmprl.cloud:443';
232-
const tls = normalizeTlsConfig(tlsFromConfig) ?? {};
232+
const tls = normalizeTlsConfig(tlsFromConfig, options.apiKey) ?? {};
233233

234234
return {
235235
address,

‎packages/common/src/internal-non-workflow/tls-config.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ export type TLSConfigOption = TLSConfig | boolean | undefined | null;
3232
/**
3333
* Normalize {@link TLSConfigOption} by turning false and null to undefined and true to and empty object
3434
*/
35-
export function normalizeTlsConfig(tls: TLSConfigOption): TLSConfig | undefined {
35+
export function normalizeTlsConfig(
36+
tls: TLSConfigOption,
37+
apiKey?: string | (() => string) | undefined
38+
): TLSConfig | undefined {
39+
tls = tls ?? (apiKey !== undefined ? true : undefined);
3640
return typeof tls === 'object' ? (tls === null ? undefined : tls) : tls ? {} : undefined;
3741
}

‎packages/test/src/test-client-connection.ts‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ test('withMetadata / withDeadline / withAbortSignal set the CallContext for RPC
114114
address: `127.0.0.1:${port}`,
115115
metadata: { staticKey: 'set', 'staticKey-bin': Buffer.from([0x00]) },
116116
apiKey: 'test-token',
117+
tls: false,
117118
});
118119
await conn.withMetadata({ test: 'true' }, () =>
119120
conn.withMetadata({ otherKey: 'set', 'otherKey-bin': Buffer.from([0x01]) }, () =>
@@ -164,6 +165,7 @@ test('apiKey sets temporal-namespace header appropriately', async (t) => {
164165
address: `127.0.0.1:${port}`,
165166
metadata: { staticKey: 'set' },
166167
apiKey: 'test-token',
168+
tls: false,
167169
});
168170

169171
await conn.workflowService.startWorkflowExecution({ namespace: 'test-namespace' });
@@ -610,3 +612,17 @@ async function withHttp2Server(
610612
});
611613
});
612614
}
615+
616+
test('Client Connection: TLS is enabled by default when apiKey is provided and tls is not configured', async (t) => {
617+
const conn = Connection.lazy({ apiKey: 'test-api-key' });
618+
// When TLS is enabled, credentials should NOT be insecure
619+
const isInsecure = conn.options.credentials._isSecure !== undefined && !conn.options.credentials._isSecure();
620+
t.false(isInsecure, 'Connection should use secure credentials when apiKey is provided');
621+
});
622+
623+
test('Client Connection: TLS can be explicitly disabled even when apiKey is provided', async (t) => {
624+
const conn = Connection.lazy({ apiKey: 'test-api-key', tls: false });
625+
// When TLS is explicitly disabled, credentials should be insecure
626+
const isInsecure = conn.options.credentials._isSecure !== undefined && !conn.options.credentials._isSecure();
627+
t.true(isInsecure, 'Connection should use insecure credentials when tls: false');
628+
});

‎packages/test/src/test-native-connection-headers.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ test('NativeConnection passes headers provided in options', async (t) => {
8484
address: `127.0.0.1:${port}`,
8585
metadata: { initial: 'true' },
8686
apiKey: 'enchi_cat',
87+
tls: false,
8788
});
8889
t.true(gotInitialHeader);
8990
t.true(gotApiKey);
@@ -130,6 +131,7 @@ test('apiKey sets temporal-namespace header appropriately', async (t) => {
130131
address: `127.0.0.1:${port}`,
131132
metadata: { staticKey: 'set' },
132133
apiKey: 'test-token',
134+
tls: false,
133135
});
134136

135137
await conn.workflowService.startWorkflowExecution({ namespace: 'test-namespace' });

‎packages/test/src/test-native-connection.ts‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as protoLoader from '@grpc/proto-loader';
88
import { Client, NamespaceNotFoundError, WorkflowNotFoundError } from '@temporalio/client';
99
import { InternalConnectionOptions, InternalConnectionOptionsSymbol } from '@temporalio/client/lib/connection';
1010
import { IllegalStateError, NativeConnection, NativeConnectionOptions, TransportError } from '@temporalio/worker';
11+
import { toNativeClientOptions } from '@temporalio/worker/lib/connection-options';
1112
import { temporal } from '@temporalio/proto';
1213
import { TestWorkflowEnvironment } from '@temporalio/testing';
1314
import { RUN_INTEGRATION_TESTS, Worker } from './helpers';
@@ -475,3 +476,18 @@ test('setMetadata accepts binary headers', async (t) => {
475476
await connection.close();
476477
server.forceShutdown();
477478
});
479+
480+
test('NativeConnection: TLS is enabled by default when apiKey is provided and tls is not configured', (t) => {
481+
// Use toNativeClientOptions to inspect the resulting config without actually connecting
482+
const options = toNativeClientOptions({ apiKey: 'test-api-key' });
483+
// When TLS is enabled, targetUrl should use https://
484+
t.true(options.targetUrl.startsWith('https://'), 'targetUrl should use https when apiKey is provided');
485+
t.not(options.tls, null, 'TLS config should not be null when apiKey is provided');
486+
});
487+
488+
test('NativeConnection: TLS can be explicitly disabled even when apiKey is provided', (t) => {
489+
const options = toNativeClientOptions({ apiKey: 'test-api-key', tls: false });
490+
// When TLS is explicitly disabled, targetUrl should use http://
491+
t.true(options.targetUrl.startsWith('http://'), 'targetUrl should use http when tls: false');
492+
t.is(options.tls, null, 'TLS config should be null when tls: false');
493+
});

‎packages/worker/src/connection-options.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export interface NativeConnectionOptions {
7979
export function toNativeClientOptions(options: NativeConnectionOptions): native.ClientOptions {
8080
const address = normalizeGrpcEndpointAddress(options.address ?? 'localhost:7233', DEFAULT_TEMPORAL_GRPC_PORT);
8181

82-
const tlsInput = normalizeTlsConfig(options.tls);
82+
const tlsInput = normalizeTlsConfig(options.tls, options.apiKey);
8383
const tls: native.TLSConfig | null = tlsInput
8484
? {
8585
domain: tlsInput.serverNameOverride ?? null,

0 commit comments

Comments
 (0)