core/apps/webapp/app/routes/api.oauth.clients.$clientId.tsx
Harshith Mullapudi 714399cf41
Feat: OAuth support for external apps (#22)
* Feat: OAuth support for external apps

* Fix: OAuth screen

---------

Co-authored-by: Manoj K <saimanoj58@gmail.com>
2025-07-19 16:44:15 +05:30

186 lines
5.3 KiB
TypeScript

import { type ActionFunctionArgs, type LoaderFunctionArgs, json } from "@remix-run/node";
import { PrismaClient } from "@prisma/client";
import { requireAuth } from "~/utils/auth-helper";
import crypto from "crypto";
const prisma = new PrismaClient();
// GET /api/oauth/clients/:clientId - Get specific OAuth client
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
const user = await requireAuth(request);
const { clientId } = params;
if (!clientId) {
return json({ error: "Client ID is required" }, { status: 400 });
}
try {
// Get user's workspace
const userRecord = await prisma.user.findUnique({
where: { id: user.id },
include: { Workspace: true },
});
if (!userRecord?.Workspace) {
return json({ error: "No workspace found" }, { status: 404 });
}
const client = await prisma.oAuthClient.findFirst({
where: {
id: clientId,
workspaceId: userRecord.Workspace.id,
},
select: {
id: true,
clientId: true,
name: true,
description: true,
redirectUris: true,
allowedScopes: true,
requirePkce: true,
logoUrl: true,
homepageUrl: true,
isActive: true,
createdAt: true,
updatedAt: true,
createdBy: {
select: {
id: true,
email: true,
name: true,
},
},
},
});
if (!client) {
return json({ error: "OAuth client not found" }, { status: 404 });
}
return json({ client });
} catch (error) {
console.error("Error fetching OAuth client:", error);
return json({ error: "Internal server error" }, { status: 500 });
}
};
export const action = async ({ request, params }: ActionFunctionArgs) => {
const user = await requireAuth(request);
const { clientId } = params;
const method = request.method;
if (!clientId) {
return json({ error: "Client ID is required" }, { status: 400 });
}
try {
// Get user's workspace
const userRecord = await prisma.user.findUnique({
where: { id: user.id },
include: { Workspace: true },
});
if (!userRecord?.Workspace) {
return json({ error: "No workspace found" }, { status: 404 });
}
// Verify client exists and belongs to user's workspace
const existingClient = await prisma.oAuthClient.findFirst({
where: {
id: clientId,
workspaceId: userRecord.Workspace.id,
},
});
if (!existingClient) {
return json({ error: "OAuth client not found" }, { status: 404 });
}
// PATCH - Update OAuth client
if (method === "PATCH") {
const body = await request.json();
const { name, description, redirectUris, allowedScopes, requirePkce, logoUrl, homepageUrl, isActive } = body;
const updateData: any = {};
if (name !== undefined) updateData.name = name;
if (description !== undefined) updateData.description = description;
if (redirectUris !== undefined) {
updateData.redirectUris = Array.isArray(redirectUris) ? redirectUris.join(',') : redirectUris;
}
if (allowedScopes !== undefined) {
updateData.allowedScopes = Array.isArray(allowedScopes) ? allowedScopes.join(',') : allowedScopes;
}
if (requirePkce !== undefined) updateData.requirePkce = requirePkce;
if (logoUrl !== undefined) updateData.logoUrl = logoUrl;
if (homepageUrl !== undefined) updateData.homepageUrl = homepageUrl;
if (isActive !== undefined) updateData.isActive = isActive;
const updatedClient = await prisma.oAuthClient.update({
where: { id: clientId },
data: updateData,
select: {
id: true,
clientId: true,
name: true,
description: true,
redirectUris: true,
allowedScopes: true,
requirePkce: true,
logoUrl: true,
homepageUrl: true,
isActive: true,
createdAt: true,
updatedAt: true,
},
});
return json({ success: true, client: updatedClient });
}
// POST - Regenerate client secret
if (method === "POST") {
const body = await request.json();
const { action } = body;
if (action === "regenerate_secret") {
const newClientSecret = crypto.randomBytes(32).toString('hex');
const updatedClient = await prisma.oAuthClient.update({
where: { id: clientId },
data: { clientSecret: newClientSecret },
select: {
id: true,
clientId: true,
clientSecret: true,
name: true,
},
});
return json({
success: true,
client: updatedClient,
message: "Client secret regenerated successfully. Save it securely - it won't be shown again."
});
}
return json({ error: "Invalid action" }, { status: 400 });
}
// DELETE - Delete OAuth client
if (method === "DELETE") {
await prisma.oAuthClient.delete({
where: { id: clientId },
});
return json({ success: true, message: "OAuth client deleted successfully" });
}
return json({ error: "Method not allowed" }, { status: 405 });
} catch (error) {
console.error("Error managing OAuth client:", error);
return json({ error: "Internal server error" }, { status: 500 });
}
};