kubernetes-client/javascript

Exec auth incorrectly invalidates cached tokens without expiration timestamps

Summary

  • Context: The ExecAuth class handles authentication for Kubernetes exec auth providers by executing external commands to obtain credentials and caching tokens to avoid unnecessary command executions.

  • Bug: When a cached token lacks an expirationTimestamp, the code attempts to parse undefined as a date string, causing Date.parse() to return NaN, which incorrectly invalidates the cached token.

  • Actual vs. expected: Tokens without expiration timestamps are treated as expired and cause unnecessary re-execution of the auth command, instead of being treated as non-expiring tokens that should remain cached indefinitely.

  • Impact: Non-expiring tokens are not properly cached, leading to unnecessary command executions on every authentication request and potential performance degradation.

Code with bug

export interface CredentialStatus {
    readonly token: string;
    readonly clientCertificateData: string;
    readonly clientKeyData: string;
    readonly expirationTimestamp: string; 
    // <-- BUG 🔴 Should be optional since tokens may not have expiration
}

private async getCredential(user: User): Promise<Credential | null> {
    // TODO: Add a unit test for token caching.

    const cachedToken = this.tokenCache[user.name];
    if (cachedToken) {
        const date = Date.parse(
            cachedToken.status.expirationTimestamp,
        ); 
        // <-- BUG 🔴 Parses undefined when expirationTimestamp is missing, returns NaN

        if (date > Date.now()) {
            return cachedToken;
        }

        this.tokenCache[user.name] = null;
    }

    // ... rest of method
}

Evidence

When expirationTimestamp is undefined, Date.parse(undefined) returns NaN. The comparison NaN > Date.now() always evaluates to false, causing the cached token to be invalidated even though it should be treated as non-expiring. According to Kubernetes exec auth documentation and the Go client implementation, expirationTimestamp is optional and tokens without it should never expire.

Recommended fix

Make expirationTimestamp optional in the CredentialStatus interface and add a check to return cached tokens immediately if they lack an expiration timestamp. Change readonly expirationTimestamp: string; to readonly expirationTimestamp?: string; and add a guard clause: if (!cachedToken.status.expirationTimestamp) { return cachedToken; } before parsing the timestamp. // <– FIX 🟢