Files
gitea-mirror/src/pages/api/sso/providers.ts
Arunavo Ray 5f45a9a03d updates
2025-07-26 22:06:29 +05:30

262 lines
7.4 KiB
TypeScript

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 as any).samlConfig ? JSON.parse((provider as any).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,
scopes,
} = 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,
scopes: scopes || ["openid", "email", "profile"],
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 as any).samlConfig ? JSON.parse((newProvider as any).samlConfig) : undefined,
};
return new Response(JSON.stringify(formattedProvider), {
status: 201,
headers: { "Content-Type": "application/json" },
});
} catch (error) {
return createSecureErrorResponse(error, "SSO providers API");
}
}
// PUT /api/sso/providers - Update an existing SSO provider
export async function PUT(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 body = await context.request.json();
const {
issuer,
domain,
clientId,
clientSecret,
authorizationEndpoint,
tokenEndpoint,
jwksEndpoint,
userInfoEndpoint,
scopes,
organizationId,
} = body;
// Get existing provider
const [existingProvider] = await db
.select()
.from(ssoProviders)
.where(eq(ssoProviders.id, providerId))
.limit(1);
if (!existingProvider) {
return new Response(
JSON.stringify({ error: "Provider not found" }),
{
status: 404,
headers: { "Content-Type": "application/json" },
}
);
}
// Parse existing config
const existingConfig = JSON.parse(existingProvider.oidcConfig);
// Create updated OIDC config
const updatedOidcConfig = {
...existingConfig,
clientId: clientId || existingConfig.clientId,
clientSecret: clientSecret || existingConfig.clientSecret,
authorizationEndpoint: authorizationEndpoint || existingConfig.authorizationEndpoint,
tokenEndpoint: tokenEndpoint || existingConfig.tokenEndpoint,
jwksEndpoint: jwksEndpoint || existingConfig.jwksEndpoint,
userInfoEndpoint: userInfoEndpoint || existingConfig.userInfoEndpoint,
scopes: scopes || existingConfig.scopes || ["openid", "email", "profile"],
};
// Update provider
const [updatedProvider] = await db
.update(ssoProviders)
.set({
issuer: issuer || existingProvider.issuer,
domain: domain || existingProvider.domain,
oidcConfig: JSON.stringify(updatedOidcConfig),
organizationId: organizationId !== undefined ? organizationId : existingProvider.organizationId,
updatedAt: new Date(),
})
.where(eq(ssoProviders.id, providerId))
.returning();
// Parse JSON fields before sending
const formattedProvider = {
...updatedProvider,
oidcConfig: JSON.parse(updatedProvider.oidcConfig),
samlConfig: (updatedProvider as any).samlConfig ? JSON.parse((updatedProvider as any).samlConfig) : undefined,
};
return new Response(JSON.stringify(formattedProvider), {
status: 200,
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");
}
}