AtAuth
at main 85 lines 2.6 kB view raw
1/** 2 * Error Handling Utilities 3 * 4 * Provides safe error responses that don't leak internal details. 5 * Express 5 automatically forwards async errors to the error handler. 6 */ 7 8/** 9 * Standard error response format. 10 */ 11export interface ErrorResponse { 12 error: string; 13 message: string; 14} 15 16/** 17 * HTTP error with status code and machine-readable error code. 18 * Thrown from route handlers and caught by the global error middleware. 19 */ 20export class HttpError extends Error { 21 constructor( 22 public readonly statusCode: number, 23 public readonly code: string, 24 message: string 25 ) { 26 super(message); 27 this.name = 'HttpError'; 28 } 29} 30 31/** 32 * Convenience factory functions for common HTTP errors. 33 */ 34export const httpError = { 35 badRequest: (code: string, message: string) => new HttpError(400, code, message), 36 unauthorized: (code: string, message: string) => new HttpError(401, code, message), 37 forbidden: (code: string, message: string) => new HttpError(403, code, message), 38 notFound: (code: string, message: string) => new HttpError(404, code, message), 39 conflict: (code: string, message: string) => new HttpError(409, code, message), 40 internalServerError: (code: string, message: string) => new HttpError(500, code, message), 41}; 42 43/** 44 * Sanitize an error for client response. 45 * Logs the full error server-side but returns a safe message to clients. 46 * 47 * @param error - The caught error 48 * @param context - Context for logging (e.g., "Token verify") 49 * @returns Safe error message for client response 50 */ 51export function sanitizeError(error: unknown, context: string): string { 52 // Log full error details server-side for debugging 53 console.error(`${context} error:`, error); 54 55 // In development, return more details for debugging 56 // In production, return a generic message 57 if (process.env.NODE_ENV === 'development') { 58 if (error instanceof Error) { 59 // Even in dev, don't expose stack traces or sensitive paths 60 return error.message.replace(/\/[^\s:]+/g, '[path]'); 61 } 62 } 63 64 // Generic message that doesn't leak implementation details 65 return 'An internal error occurred. Please try again later.'; 66} 67 68/** 69 * Create a safe 500 error response. 70 * 71 * @param errorCode - Machine-readable error code 72 * @param error - The caught error 73 * @param context - Context for logging 74 * @returns Error response object 75 */ 76export function internalError( 77 errorCode: string, 78 error: unknown, 79 context: string 80): ErrorResponse { 81 return { 82 error: errorCode, 83 message: sanitizeError(error, context), 84 }; 85}