/** * OAuth Initiate Handler * * Initiates the OAuth 2.0 authorization code flow by: * 1. Validating the provider is supported and configured * 2. Generating a cryptographically secure state parameter for CSRF protection * 3. Creating an OAuth session to track the flow * 4. Building the provider's authorization URL * 5. Redirecting the user to the provider for authorization * * Route: GET /oauth/:provider/initiate?redirectUri={optional_redirect_url} */ import { GetHandler, HttpMethod, RouteProps, badRequest, internalServerError } from "@flink-app/flink"; import InitiateRequest from "../schemas/InitiateRequest"; import { generateState, generateSessionId } from "../utils/state-utils"; import { getProvider } from "../providers/ProviderRegistry"; import { validateProvider, createOAuthError, OAuthErrorCodes } from "../utils/error-utils"; /** * Path parameters for the handler */ interface PathParams { provider: string; [key: string]: string; } /** * Route configuration * Note: This handler is registered programmatically by the plugin * with dynamic provider parameter support */ export const Route: RouteProps = { path: "/oauth/:provider/initiate", method: HttpMethod.get, }; /** * OAuth Initiate Handler * * Starts the OAuth flow by generating state, creating a session, * and redirecting to the OAuth provider's authorization URL. */ const InitiateOAuth: GetHandler = async ({ ctx, req }) => { const { provider } = req.params; const { redirectUri } = req.query; try { // Validate provider is supported validateProvider(provider); // Get plugin options and provider config const { options } = ctx.plugins.oauth; const providerConfig = options.providers[provider as "github" | "google"]; if (!providerConfig) { throw createOAuthError(OAuthErrorCodes.INVALID_PROVIDER, `Provider "${provider}" is not configured`, { provider }); } // Generate cryptographically secure state and session ID const state = generateState(); const sessionId = generateSessionId(); // Store session for state validation in callback await ctx.repos.oauthSessionRepo.create({ sessionId, state, provider: provider as "github" | "google", redirectUri: redirectUri || providerConfig.callbackUrl, createdAt: new Date(), }); // Get provider instance and build authorization URL const oauthProvider = getProvider(provider as "github" | "google", providerConfig); const authorizationUrl = oauthProvider.getAuthorizationUrl({ state, redirectUri: providerConfig.callbackUrl, scope: providerConfig.scope || [], }); // Redirect user to provider's authorization page return { status: 302, headers: { Location: authorizationUrl, }, data: {}, }; } catch (error: any) { // Handle validation errors if (error.code && Object.values(OAuthErrorCodes).includes(error.code)) { return badRequest(error.message); } // Handle unexpected errors return internalServerError(error.message || "Failed to initiate OAuth flow"); } }; export default InitiateOAuth;