import { Request, Response } from 'express'; import { validationResult } from 'express-validator'; import crypto from 'crypto'; import { getOAuthClients, findOAuthClientById, createOAuthClient, updateOAuthClient, deleteOAuthClient, } from '../models/OAuth.js'; import { IOAuthClient } from '../types/index.js'; /** * GET /api/oauth/clients * Get all OAuth clients */ export const getAllClients = async (req: Request, res: Response): Promise => { try { const clients = await getOAuthClients(); // Don't expose client secrets in the list const sanitizedClients = clients.map((client) => ({ clientId: client.clientId, name: client.name, redirectUris: client.redirectUris, grants: client.grants, scopes: client.scopes, owner: client.owner, })); res.json({ success: true, clients: sanitizedClients, }); } catch (error) { console.error('Get OAuth clients error:', error); res.status(500).json({ success: false, message: 'Failed to retrieve OAuth clients', }); } }; /** * GET /api/oauth/clients/:clientId * Get a specific OAuth client */ export const getClient = async (req: Request, res: Response): Promise => { try { const { clientId } = req.params; const client = await findOAuthClientById(clientId); if (!client) { res.status(404).json({ success: false, message: 'OAuth client not found', }); return; } // Don't expose client secret const sanitizedClient = { clientId: client.clientId, name: client.name, redirectUris: client.redirectUris, grants: client.grants, scopes: client.scopes, owner: client.owner, }; res.json({ success: true, client: sanitizedClient, }); } catch (error) { console.error('Get OAuth client error:', error); res.status(500).json({ success: false, message: 'Failed to retrieve OAuth client', }); } }; /** * POST /api/oauth/clients * Create a new OAuth client */ export const createClient = async (req: Request, res: Response): Promise => { try { // Validate request const errors = validationResult(req); if (!errors.isEmpty()) { res.status(400).json({ success: false, message: 'Validation failed', errors: errors.array(), }); return; } const { name, redirectUris, grants, scopes, requireSecret } = req.body; const user = (req as any).user; // Generate client ID const clientId = crypto.randomBytes(16).toString('hex'); // Generate client secret if required const clientSecret = requireSecret !== false ? crypto.randomBytes(32).toString('hex') : undefined; // Create client const client: IOAuthClient = { clientId, clientSecret, name, redirectUris: Array.isArray(redirectUris) ? redirectUris : [redirectUris], grants: grants || ['authorization_code', 'refresh_token'], scopes: scopes || ['read', 'write'], owner: user?.username || 'admin', }; const createdClient = await createOAuthClient(client); // Return client with secret (only shown once) res.status(201).json({ success: true, message: 'OAuth client created successfully', client: { clientId: createdClient.clientId, clientSecret: createdClient.clientSecret, name: createdClient.name, redirectUris: createdClient.redirectUris, grants: createdClient.grants, scopes: createdClient.scopes, owner: createdClient.owner, }, warning: clientSecret ? 'Client secret is only shown once. Please save it securely.' : undefined, }); } catch (error) { console.error('Create OAuth client error:', error); if (error instanceof Error && error.message.includes('already exists')) { res.status(409).json({ success: false, message: error.message, }); } else { res.status(500).json({ success: false, message: 'Failed to create OAuth client', }); } } }; /** * PUT /api/oauth/clients/:clientId * Update an OAuth client */ export const updateClient = async (req: Request, res: Response): Promise => { try { const { clientId } = req.params; const { name, redirectUris, grants, scopes } = req.body; const updates: Partial = {}; if (name) updates.name = name; if (redirectUris) updates.redirectUris = Array.isArray(redirectUris) ? redirectUris : [redirectUris]; if (grants) updates.grants = grants; if (scopes) updates.scopes = scopes; const updatedClient = await updateOAuthClient(clientId, updates); if (!updatedClient) { res.status(404).json({ success: false, message: 'OAuth client not found', }); return; } // Don't expose client secret res.json({ success: true, message: 'OAuth client updated successfully', client: { clientId: updatedClient.clientId, name: updatedClient.name, redirectUris: updatedClient.redirectUris, grants: updatedClient.grants, scopes: updatedClient.scopes, owner: updatedClient.owner, }, }); } catch (error) { console.error('Update OAuth client error:', error); res.status(500).json({ success: false, message: 'Failed to update OAuth client', }); } }; /** * DELETE /api/oauth/clients/:clientId * Delete an OAuth client */ export const deleteClient = async (req: Request, res: Response): Promise => { try { const { clientId } = req.params; const deleted = await deleteOAuthClient(clientId); if (!deleted) { res.status(404).json({ success: false, message: 'OAuth client not found', }); return; } res.json({ success: true, message: 'OAuth client deleted successfully', }); } catch (error) { console.error('Delete OAuth client error:', error); res.status(500).json({ success: false, message: 'Failed to delete OAuth client', }); } }; /** * POST /api/oauth/clients/:clientId/regenerate-secret * Regenerate client secret */ export const regenerateSecret = async (req: Request, res: Response): Promise => { try { const { clientId } = req.params; const client = await findOAuthClientById(clientId); if (!client) { res.status(404).json({ success: false, message: 'OAuth client not found', }); return; } // Generate new secret const newSecret = crypto.randomBytes(32).toString('hex'); const updatedClient = await updateOAuthClient(clientId, { clientSecret: newSecret }); if (!updatedClient) { res.status(500).json({ success: false, message: 'Failed to regenerate client secret', }); return; } res.json({ success: true, message: 'Client secret regenerated successfully', clientSecret: newSecret, warning: 'Client secret is only shown once. Please save it securely.', }); } catch (error) { console.error('Regenerate secret error:', error); res.status(500).json({ success: false, message: 'Failed to regenerate client secret', }); } };