/** * OAuth Error Interface and Utilities * * Provides standardized error handling for OAuth flows including * user-friendly messages and error code mapping. */ /** * Standardized OAuth error structure */ export interface OAuthError { /** Error code for programmatic handling */ code: string; /** User-friendly error message */ message: string; /** Additional error details (for logging, not user display) */ details?: any; } /** * OAuth error codes */ export const OAuthErrorCodes = { INVALID_STATE: "invalid_state", ACCESS_DENIED: "access_denied", INVALID_GRANT: "invalid_grant", NETWORK_ERROR: "network_error", UNKNOWN_PROVIDER: "unknown_provider", JWT_GENERATION_FAILED: "jwt_generation_failed", INVALID_REQUEST: "invalid_request", SERVER_ERROR: "server_error", INVALID_PROVIDER: "invalid_provider", SESSION_EXPIRED: "session_expired", MISSING_CODE: "missing_code", } as const; /** * Create a standardized OAuth error * * @param code - Error code from OAuthErrorCodes * @param message - User-friendly error message * @param details - Additional error details for logging * @returns Standardized OAuthError object */ export function createOAuthError(code: string, message: string, details?: any): OAuthError { return { code, message, details, }; } /** * Map provider-specific errors to standardized OAuth errors * * This function converts various error types from OAuth providers * into user-friendly standardized errors while sanitizing sensitive data. * * @param error - Error from OAuth provider or internal error * @returns Standardized OAuthError */ export function handleProviderError(error: any): OAuthError { // Handle OAuth provider error responses if (error.error) { const errorCode = error.error; switch (errorCode) { case "access_denied": return createOAuthError(OAuthErrorCodes.ACCESS_DENIED, "You denied access to your account. Please try again if you want to continue.", { providerError: errorCode, }); case "invalid_grant": case "invalid_code": return createOAuthError(OAuthErrorCodes.INVALID_GRANT, "The authorization code has expired or is invalid. Please try logging in again.", { providerError: errorCode, }); case "invalid_request": return createOAuthError(OAuthErrorCodes.INVALID_REQUEST, "There was a problem with the authentication request. Please try again.", { providerError: errorCode, }); default: return createOAuthError(OAuthErrorCodes.SERVER_ERROR, "An error occurred during authentication. Please try again.", { providerError: errorCode, }); } } // Handle network errors if (error.code === "ENOTFOUND" || error.code === "ECONNREFUSED" || error.code === "ETIMEDOUT") { return createOAuthError(OAuthErrorCodes.NETWORK_ERROR, "Unable to connect to the authentication service. Please check your connection and try again.", { networkError: error.code, }); } // Handle JWT generation errors if (error.message && error.message.includes("JWT")) { return createOAuthError(OAuthErrorCodes.JWT_GENERATION_FAILED, "Failed to generate authentication token. Please try again.", { originalError: error.message, }); } // Generic error fallback return createOAuthError(OAuthErrorCodes.SERVER_ERROR, "An unexpected error occurred. Please try again.", { originalError: error.message || "Unknown error", }); } /** * Validate provider name * * @param provider - Provider name to validate * @returns true if valid provider * @throws OAuthError if invalid */ export function validateProvider(provider: string): provider is "github" | "google" { if (provider !== "github" && provider !== "google") { throw createOAuthError(OAuthErrorCodes.INVALID_PROVIDER, `Provider "${provider}" is not supported. Supported providers: github, google`, { provider }); } return true; } /** * Validate response_type parameter * * @param responseType - Response type to validate * @returns true if valid * @throws OAuthError if invalid */ export function validateResponseType(responseType?: string): boolean { if (responseType && responseType !== "json") { throw createOAuthError(OAuthErrorCodes.INVALID_REQUEST, `Invalid response_type "${responseType}". Supported values: json`, { responseType }); } return true; }