-
-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Description
Add optional debug logging for HTTP requests and responses to help developers debug OAuth flows. This should be configurable and use structured logging format for easy parsing.
What needs to be done
- Add logging configuration to
ServerOptionsandGetAuthCodeOptions - Implement structured logging for:
- Incoming HTTP requests (method, URL, headers)
- Outgoing HTTP responses (status, headers)
- OAuth flow events (server started, callback received, errors)
- Timing information (request duration)
- Make logging pluggable (console, file, custom logger)
- Ensure sensitive data is redacted (tokens, secrets)
Why this matters
OAuth debugging is challenging without visibility:
- Hard to diagnose redirect issues
- Difficult to understand provider-specific behaviors
- No insight into timing issues
- Can't see what parameters are being sent/received
Good logging enables:
- Faster debugging of integration issues
- Better understanding of OAuth flows
- Performance monitoring
- Security auditing
Implementation considerations
-
Security: How do we ensure tokens and secrets are NEVER logged? Should we have a whitelist of safe parameters?
-
Performance: Logging can impact performance. Should it be compile-time removable for production?
-
Alternative approach: Instead of built-in logging, provide hooks/events that users can subscribe to for custom logging
-
Log levels: Should we support different verbosity levels (DEBUG, INFO, WARN, ERROR)?
-
Structured vs text: JSON logs are machine-readable but less human-friendly. Which should we prioritize?
Suggested implementation
// Logging types
interface LogEvent {
timestamp: string;
level: 'debug' | 'info' | 'warn' | 'error';
event: string;
data: Record<string, any>;
duration?: number;
}
interface Logger {
log(event: LogEvent): void;
}
// Built-in loggers
class ConsoleLogger implements Logger {
log(event: LogEvent): void {
const { timestamp, level, event: eventName, data, duration } = event;
const message = `[${timestamp}] ${level.toUpperCase()} ${eventName}`;
if (level === 'error') {
console.error(message, data);
} else if (level === 'warn') {
console.warn(message, data);
} else {
console.log(message, data);
}
if (duration !== undefined) {
console.log(` Duration: ${duration}ms`);
}
}
}
class StructuredLogger implements Logger {
log(event: LogEvent): void {
// Output JSON for parsing by log aggregation tools
console.log(JSON.stringify(event));
}
}
// Redaction utility
function redactSensitive(obj: any): any {
const sensitive = ['access_token', 'refresh_token', 'client_secret', 'code'];
if (typeof obj !== 'object' || obj === null) {
return obj;
}
const result: any = Array.isArray(obj) ? [] : {};
for (const [key, value] of Object.entries(obj)) {
if (sensitive.includes(key.toLowerCase())) {
result[key] = '[REDACTED]';
} else if (typeof value === 'object') {
result[key] = redactSensitive(value);
} else {
result[key] = value;
}
}
return result;
}
// Updated ServerOptions
interface ServerOptions {
// ... existing options
logger?: Logger;
logLevel?: 'debug' | 'info' | 'warn' | 'error';
logRequests?: boolean;
logResponses?: boolean;
logTimings?: boolean;
}
// Usage in server implementation
private handleRequest(request: Request): Response {
const startTime = performance.now();
if (this.logger && this.logRequests) {
this.logger.log({
timestamp: new Date().toISOString(),
level: 'debug',
event: 'http.request',
data: redactSensitive({
method: request.method,
url: request.url,
headers: Object.fromEntries(request.headers.entries()),
ip: request.headers.get('x-forwarded-for') || 'localhost'
})
});
}
// ... handle request
const response = new Response(...);
if (this.logger && this.logResponses) {
const duration = performance.now() - startTime;
this.logger.log({
timestamp: new Date().toISOString(),
level: 'debug',
event: 'http.response',
data: redactSensitive({
status: response.status,
headers: Object.fromEntries(response.headers.entries())
}),
duration
});
}
return response;
}Usage examples
// Basic console logging
await getAuthCode({
authorizationUrl: "...",
logger: new ConsoleLogger(),
logLevel: 'debug',
logRequests: true,
logResponses: true,
logTimings: true
});
// Custom logger
class CustomLogger implements Logger {
log(event: LogEvent): void {
// Send to logging service
fetch('https://logs.example.com', {
method: 'POST',
body: JSON.stringify(event)
});
}
}
// Production-safe logging (no sensitive data)
await getAuthCode({
authorizationUrl: "...",
logger: new StructuredLogger(),
logLevel: 'info' // Only important events
});Testing requirements
- Verify sensitive data is redacted
- Test all log levels
- Verify performance impact is minimal
- Test custom logger integration
- Ensure logs are useful for debugging
Skills required
- TypeScript
- Logging best practices
- Security awareness (data redaction)
- Performance optimization
- Structured logging concepts
Difficulty
Easy - Straightforward implementation with important security considerations