diff --git a/apps/cli/src/commands/auth.command.ts b/apps/cli/src/commands/auth.command.ts index b03e33d0..39992beb 100644 --- a/apps/cli/src/commands/auth.command.ts +++ b/apps/cli/src/commands/auth.command.ts @@ -187,19 +187,29 @@ export class AuthCommand extends Command { if (credentials.expiresAt) { const expiresAt = new Date(credentials.expiresAt); const now = new Date(); - const hoursRemaining = Math.floor( - (expiresAt.getTime() - now.getTime()) / (1000 * 60 * 60) - ); + const timeRemaining = expiresAt.getTime() - now.getTime(); + const hoursRemaining = Math.floor(timeRemaining / (1000 * 60 * 60)); + const minutesRemaining = Math.floor(timeRemaining / (1000 * 60)); - if (hoursRemaining > 0) { - console.log( - chalk.gray( - ` Expires: ${expiresAt.toLocaleString()} (${hoursRemaining} hours remaining)` - ) - ); + if (timeRemaining > 0) { + // Token is still valid + if (hoursRemaining > 0) { + console.log( + chalk.gray( + ` Expires at: ${expiresAt.toLocaleString()} (${hoursRemaining} hours remaining)` + ) + ); + } else { + console.log( + chalk.gray( + ` Expires at: ${expiresAt.toLocaleString()} (${minutesRemaining} minutes remaining)` + ) + ); + } } else { + // Token has expired console.log( - chalk.yellow(` Token expired at: ${expiresAt.toLocaleString()}`) + chalk.yellow(` Expired at: ${expiresAt.toLocaleString()}`) ); } } else { diff --git a/packages/tm-core/src/auth/auth-manager.ts b/packages/tm-core/src/auth/auth-manager.ts index 9df106ae..95f33a6d 100644 --- a/packages/tm-core/src/auth/auth-manager.ts +++ b/packages/tm-core/src/auth/auth-manager.ts @@ -90,18 +90,28 @@ export class AuthManager { allowExpired: true }); - // Only attempt refresh if we have expired credentials with a refresh token - if (expiredCredentials && expiredCredentials.refreshToken) { - try { - this.logger.info('Token expired, attempting automatic refresh...'); - return await this.refreshToken(); - } catch (error) { - this.logger.warn('Automatic token refresh failed:', error); - return null; - } + // Check if we have any credentials at all + if (!expiredCredentials) { + // No credentials found + return null; } - return null; + // Check if refresh token is available + if (!expiredCredentials.refreshToken) { + this.logger.warn( + 'Token expired but no refresh token available. Please re-authenticate.' + ); + return null; + } + + // Attempt refresh + try { + this.logger.info('Token expired, attempting automatic refresh...'); + return await this.refreshToken(); + } catch (error) { + this.logger.warn('Automatic token refresh failed:', error); + return null; + } } return credentials; diff --git a/packages/tm-core/tests/integration/auth-token-refresh.test.ts b/packages/tm-core/tests/integration/auth-token-refresh.test.ts index 7e78e110..aedd5db0 100644 --- a/packages/tm-core/tests/integration/auth-token-refresh.test.ts +++ b/packages/tm-core/tests/integration/auth-token-refresh.test.ts @@ -199,6 +199,26 @@ describe('AuthManager - Token Auto-Refresh Integration', () => { expect(credentials).toBeNull(); }); + + it('should return null if credentials missing expiresAt', async () => { + const credentialsWithoutExpiry: AuthCredentials = { + token: 'test-token', + refreshToken: 'refresh-token', + userId: 'test-user-id', + email: 'test@example.com', + // Missing expiresAt + savedAt: new Date().toISOString() + } as any; + + credentialStore.saveCredentials(credentialsWithoutExpiry); + + authManager = AuthManager.getInstance(); + + const credentials = await authManager.getCredentials(); + + // Should return null because no valid expiration + expect(credentials).toBeNull(); + }); }); describe('Clock Skew Tolerance', () => {