Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions src/auth/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
decodeToken
} from './auth'
import { DecodedToken } from '../types'
import { Logger, LogLevel } from '../logger'

describe('isAccessTokenExpiring', () => {
it('should return true if the token is expiring in an hour', () => {
Expand Down Expand Up @@ -43,6 +44,56 @@ describe('isAccessTokenExpiring', () => {
).toBeFalsy()
})

it('should route opaque-token debug message through process.logger, not console.debug', () => {
const debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => {})
const logger = new Logger(LogLevel.Debug)
const loggerSpy = jest.spyOn(logger, 'debug').mockImplementation(() => {})
const originalLogger = process.logger
Object.defineProperty(process, 'logger', {
value: logger,
configurable: true,
writable: true
})

try {
isAccessTokenExpiring('1f8da55057bd4f50a6577f0bc2b38b1a-r')
expect(loggerSpy).toHaveBeenCalledWith(
'isTokenExpiring: token is not a decodable JWT, treating it as not expiring.'
)
expect(debugSpy).not.toHaveBeenCalled()
} finally {
Object.defineProperty(process, 'logger', {
value: originalLogger,
configurable: true,
writable: true
})
debugSpy.mockRestore()
loggerSpy.mockRestore()
}
})

it('should not call console.debug for an opaque token when no logger is set', () => {
const debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => {})
const originalLogger = process.logger
Object.defineProperty(process, 'logger', {
value: undefined,
configurable: true,
writable: true
})

try {
isAccessTokenExpiring('1f8da55057bd4f50a6577f0bc2b38b1a-r')
expect(debugSpy).not.toHaveBeenCalled()
} finally {
Object.defineProperty(process, 'logger', {
value: originalLogger,
configurable: true,
writable: true
})
debugSpy.mockRestore()
}
})

it('should return false for a JWT without an exp claim', () => {
expect(isAccessTokenExpiring(generateTokenWithoutExp())).toBeFalsy()
})
Expand Down
5 changes: 4 additions & 1 deletion src/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ function isTokenExpiring(token: string, timeToLiveSeconds: number) {
if (!(err instanceof InvalidTokenError)) throw err
// Opaque tokens cannot be expiry-checked client-side.
// Assume the token is usable and let the server reject it if expired.
console.debug(
// Route through process.logger so the message respects the configured
// log level and doesn't pollute stdout in CI/test runs. Falls back to a
// no-op when no logger has been installed (e.g. library-only usage).
process.logger?.debug?.(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional chaining on .debug is harmless and arguably good defensive practice (protects against a test/library stubbing process.logger = {}), but note it's technically redundant: a real Logger instance always defines debug as an own arrow-function property. Not requesting a change — just flagging in case the intent was to guard something else.

'isTokenExpiring: token is not a decodable JWT, treating it as not expiring.'
)
return false
Expand Down
5 changes: 5 additions & 0 deletions src/types/system/process.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare namespace NodeJS {
export interface Process {
logger?: import('../../logger/logger').Logger
}
}
5 changes: 4 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@
"sourceMap": true
},
"exclude": ["node_modules", "**/*.spec.ts"],
"include": ["src"]
"include": ["src"],
"ts-node": {
"files": true
}
}
Loading