import type { APIContext } from "astro"; import { createSecureErrorResponse } from "@/lib/utils"; import { requireAuth } from "@/lib/utils/auth-helpers"; import { db, ssoProviders } from "@/lib/db"; import { nanoid } from "nanoid"; import { eq } from "drizzle-orm"; // GET /api/sso/providers - List all SSO providers export async function GET(context: APIContext) { try { const { user, response } = await requireAuth(context); if (response) return response; const providers = await db.select().from(ssoProviders); // Parse JSON fields before sending const formattedProviders = providers.map(provider => ({ ...provider, oidcConfig: provider.oidcConfig ? JSON.parse(provider.oidcConfig) : undefined, samlConfig: provider.samlConfig ? JSON.parse(provider.samlConfig) : undefined, })); return new Response(JSON.stringify(formattedProviders), { status: 200, headers: { "Content-Type": "application/json" }, }); } catch (error) { return createSecureErrorResponse(error, "SSO providers API"); } } // POST /api/sso/providers - Create a new SSO provider export async function POST(context: APIContext) { try { const { user, response } = await requireAuth(context); if (response) return response; const body = await context.request.json(); const { issuer, domain, clientId, clientSecret, authorizationEndpoint, tokenEndpoint, jwksEndpoint, userInfoEndpoint, mapping, providerId, organizationId, } = body; // Validate required fields if (!issuer || !domain || !providerId) { return new Response( JSON.stringify({ error: "Missing required fields" }), { status: 400, headers: { "Content-Type": "application/json" }, } ); } // Check if provider ID already exists const existing = await db .select() .from(ssoProviders) .where(eq(ssoProviders.providerId, providerId)) .limit(1); if (existing.length > 0) { return new Response( JSON.stringify({ error: "Provider ID already exists" }), { status: 409, headers: { "Content-Type": "application/json" }, } ); } // Create OIDC config object const oidcConfig = { clientId, clientSecret, authorizationEndpoint, tokenEndpoint, jwksEndpoint, userInfoEndpoint, mapping: mapping || { id: "sub", email: "email", emailVerified: "email_verified", name: "name", image: "picture", }, }; // Insert new provider const [newProvider] = await db .insert(ssoProviders) .values({ id: nanoid(), issuer, domain, oidcConfig: JSON.stringify(oidcConfig), userId: user.id, providerId, organizationId, }) .returning(); // Parse JSON fields before sending const formattedProvider = { ...newProvider, oidcConfig: newProvider.oidcConfig ? JSON.parse(newProvider.oidcConfig) : undefined, samlConfig: newProvider.samlConfig ? JSON.parse(newProvider.samlConfig) : undefined, }; return new Response(JSON.stringify(formattedProvider), { status: 201, headers: { "Content-Type": "application/json" }, }); } catch (error) { return createSecureErrorResponse(error, "SSO providers API"); } } // DELETE /api/sso/providers - Delete a provider by ID export async function DELETE(context: APIContext) { try { const { user, response } = await requireAuth(context); if (response) return response; const url = new URL(context.request.url); const providerId = url.searchParams.get("id"); if (!providerId) { return new Response( JSON.stringify({ error: "Provider ID is required" }), { status: 400, headers: { "Content-Type": "application/json" }, } ); } const deleted = await db .delete(ssoProviders) .where(eq(ssoProviders.id, providerId)) .returning(); if (deleted.length === 0) { return new Response(JSON.stringify({ error: "Provider not found" }), { status: 404, headers: { "Content-Type": "application/json" }, }); } return new Response(JSON.stringify({ success: true }), { status: 200, headers: { "Content-Type": "application/json" }, }); } catch (error) { return createSecureErrorResponse(error, "SSO providers API"); } }