Skip to content

Commit ac81b7d

Browse files
authored
Merge pull request #2666 from sachiniyer/siyer/optional-expiration-time-exec-auth
fix: don't cast undefined expiration times in exec auth
2 parents f4debf5 + 286e4a4 commit ac81b7d

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

src/exec_auth.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export interface CredentialStatus {
1010
readonly token: string;
1111
readonly clientCertificateData: string;
1212
readonly clientKeyData: string;
13-
readonly expirationTimestamp: string;
13+
readonly expirationTimestamp?: string;
1414
}
1515

1616
export interface Credential {
@@ -74,6 +74,9 @@ export class ExecAuth implements Authenticator {
7474
// TODO: Add a unit test for token caching.
7575
const cachedToken = this.tokenCache[user.name];
7676
if (cachedToken) {
77+
if (!cachedToken.status.expirationTimestamp) {
78+
return cachedToken;
79+
}
7780
const date = Date.parse(cachedToken.status.expirationTimestamp);
7881
if (date > Date.now()) {
7982
return cachedToken;

src/exec_auth_test.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,77 @@ describe('ExecAuth', () => {
225225
strictEqual(execCount, 2);
226226
});
227227

228+
it('should cache tokens without expirationTimestamp (non-expiring tokens)', async () => {
229+
// TODO: fix this test for Windows
230+
if (process.platform === 'win32') {
231+
return;
232+
}
233+
const auth = new ExecAuth();
234+
let execCount = 0;
235+
const tokenValue = 'non-expiring-token';
236+
237+
(auth as any).execFn = (
238+
command: string,
239+
args?: readonly string[],
240+
options?: child_process.SpawnOptionsWithoutStdio,
241+
): child_process.ChildProcessWithoutNullStreams => {
242+
execCount++;
243+
return {
244+
stdout: {
245+
setEncoding: () => {},
246+
on: (_data: string, f: (data: Buffer | string) => void) => {
247+
// Note: No expirationTimestamp field - token should never expire
248+
f(
249+
Buffer.from(
250+
JSON.stringify({
251+
status: { token: tokenValue },
252+
}),
253+
),
254+
);
255+
},
256+
},
257+
stderr: {
258+
setEncoding: () => {},
259+
on: () => {},
260+
},
261+
on: (op: string, f: any) => {
262+
if (op === 'close') {
263+
f(0);
264+
}
265+
},
266+
} as unknown as child_process.ChildProcessWithoutNullStreams;
267+
};
268+
269+
const user = {
270+
name: 'user',
271+
authProvider: {
272+
config: {
273+
exec: {
274+
command: 'echo',
275+
},
276+
},
277+
},
278+
};
279+
280+
const opts = {} as https.RequestOptions;
281+
opts.headers = {} as OutgoingHttpHeaders;
282+
283+
// First call - should execute the command
284+
await auth.applyAuthentication(user, opts);
285+
strictEqual(opts.headers.Authorization, `Bearer ${tokenValue}`);
286+
strictEqual(execCount, 1);
287+
288+
// Second call - should use cached token (no expiration means never expires)
289+
await auth.applyAuthentication(user, opts);
290+
strictEqual(opts.headers.Authorization, `Bearer ${tokenValue}`);
291+
strictEqual(execCount, 1, 'exec should not be called again for non-expiring token');
292+
293+
// Third call - still should use cached token
294+
await auth.applyAuthentication(user, opts);
295+
strictEqual(opts.headers.Authorization, `Bearer ${tokenValue}`);
296+
strictEqual(execCount, 1, 'exec should still not be called again');
297+
});
298+
228299
it('should return null on no exec info', async () => {
229300
const auth = new ExecAuth();
230301
const opts = {} as https.RequestOptions;

0 commit comments

Comments
 (0)