/** * Input Validation Utilities * * Provides validation functions for OAuth handler inputs including * provider names, query parameters, and redirect URIs. */ import { createOAuthError, OAuthErrorCodes } from "./error-utils"; /** * Validate provider parameter * * @param provider - Provider name from URL parameter * @returns Validated provider name * @throws OAuthError if invalid */ export function validateProvider(provider: string): "github" | "google" { if (!provider) { throw createOAuthError(OAuthErrorCodes.INVALID_PROVIDER, "Provider is required", { provider }); } if (provider !== "github" && provider !== "google") { throw createOAuthError(OAuthErrorCodes.INVALID_PROVIDER, 'Provider "' + provider + '" is not supported. Supported providers: github, google', { provider, }); } return provider; } /** * Validate response_type parameter * * @param responseType - Response type from query parameter * @returns Validated response type or undefined * @throws OAuthError if invalid */ export function validateResponseType(responseType?: string): string | undefined { if (!responseType) { return undefined; } if (responseType !== "json") { throw createOAuthError(OAuthErrorCodes.INVALID_REQUEST, 'Invalid response_type "' + responseType + '". Supported values: json', { responseType }); } return responseType; } /** * Validate redirect URI format * * @param redirectUri - Redirect URI to validate * @returns true if valid or undefined * @throws OAuthError if invalid */ export function validateRedirectUri(redirectUri?: string): boolean { if (!redirectUri) { return true; } try { const url = new URL(redirectUri); // Check protocol if (url.protocol !== "http:" && url.protocol !== "https:") { throw createOAuthError(OAuthErrorCodes.INVALID_REQUEST, "Redirect URI must use HTTP or HTTPS protocol", { redirectUri }); } return true; } catch (error) { if (error && typeof error === "object" && "code" in error) { throw error; } throw createOAuthError(OAuthErrorCodes.INVALID_REQUEST, "Invalid redirect URI format", { redirectUri }); } } /** * Validate state parameter exists * * @param state - State parameter from query * @throws OAuthError if missing */ export function validateStateParam(state?: string): void { if (!state) { throw createOAuthError(OAuthErrorCodes.INVALID_STATE, "State parameter is required", { state }); } } /** * Validate authorization code exists * * @param code - Authorization code from query * @throws OAuthError if missing */ export function validateAuthCode(code?: string): void { if (!code) { throw createOAuthError(OAuthErrorCodes.MISSING_CODE, "Authorization code is required", { code }); } } /** * Check for OAuth error in callback query parameters * * @param errorParam - Error parameter from OAuth provider * @param errorDescription - Error description from OAuth provider * @throws OAuthError if error present */ export function checkOAuthErrorParam(errorParam?: string, errorDescription?: string): void { if (errorParam) { const message = errorDescription || "Authentication failed"; throw createOAuthError(errorParam === "access_denied" ? OAuthErrorCodes.ACCESS_DENIED : OAuthErrorCodes.SERVER_ERROR, message, { error: errorParam, description: errorDescription, }); } }