import { NextRequest, NextResponse } from "next/server"; import { getServerSession } from "next-auth/next"; import { authOptions } from "../../auth/[...nextauth]/route"; /** * Get an admin token */ async function getAdminToken() { try { const tokenResponse = await fetch( `${process.env.KEYCLOAK_ISSUER}/protocol/openid-connect/token`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ grant_type: "client_credentials", client_id: process.env.KEYCLOAK_CLIENT_ID!, client_secret: process.env.KEYCLOAK_CLIENT_SECRET!, }), } ); const data = await tokenResponse.json(); if (!tokenResponse.ok || !data.access_token) { console.error("Token Error:", data); return null; } return data.access_token; } catch (error) { console.error("Token Error:", error); return null; } } /** * Get users from Keycloak */ export async function GET(request: NextRequest) { const session = await getServerSession(authOptions); // Check authentication if (!session || !session.user) { return NextResponse.json( { error: "Unauthorized" }, { status: 401 } ); } // Only administrators can list users const isAdmin = session.user.role?.includes('admin'); if (!isAdmin) { return NextResponse.json( { error: "Forbidden: Admin role required" }, { status: 403 } ); } try { // Get admin token const token = await getAdminToken(); if (!token) { return NextResponse.json( { error: "Failed to get admin token" }, { status: 500 } ); } // Parse search params const { searchParams } = new URL(request.url); const query = searchParams.get('query') || ''; const max = searchParams.get('max') || '100'; // Fetch users from Keycloak const keycloakUrl = `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users`; const queryParams = new URLSearchParams(); if (query) { queryParams.append('search', query); } queryParams.append('max', max); const usersResponse = await fetch( `${keycloakUrl}?${queryParams.toString()}`, { headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, } ); if (!usersResponse.ok) { const errorData = await usersResponse.json(); return NextResponse.json( { error: "Failed to fetch users", details: errorData }, { status: usersResponse.status } ); } const users = await usersResponse.json(); // Return user data return NextResponse.json(users); } catch (error) { console.error("Error fetching users:", error); return NextResponse.json( { error: "Server error fetching users" }, { status: 500 } ); } } /** * Create a new user in Keycloak */ export async function POST(request: NextRequest) { const session = await getServerSession(authOptions); // Check authentication if (!session || !session.user) { return NextResponse.json( { error: "Unauthorized" }, { status: 401 } ); } // Only administrators can create users const isAdmin = session.user.role?.includes('admin'); if (!isAdmin) { return NextResponse.json( { error: "Forbidden: Admin role required" }, { status: 403 } ); } try { // Get admin token const token = await getAdminToken(); if (!token) { return NextResponse.json( { error: "Failed to get admin token" }, { status: 500 } ); } // Get user data from request const userData = await request.json(); // Validate user data if (!userData.username || !userData.email) { return NextResponse.json( { error: "Username and email are required" }, { status: 400 } ); } // Create user in Keycloak const keycloakUrl = `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users`; const createResponse = await fetch(keycloakUrl, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify({ username: userData.username, email: userData.email, enabled: true, emailVerified: true, firstName: userData.firstName || '', lastName: userData.lastName || '', credentials: userData.password ? [ { type: "password", value: userData.password, temporary: false } ] : undefined }), }); if (!createResponse.ok) { const errorData = await createResponse.json().catch(() => ({})); return NextResponse.json( { error: "Failed to create user", details: errorData }, { status: createResponse.status } ); } // If user was created successfully, get the user ID const userResponse = await fetch( `${keycloakUrl}?username=${encodeURIComponent(userData.username)}`, { headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, } ); if (!userResponse.ok) { return NextResponse.json( { error: "User created but failed to retrieve user details" }, { status: 207 } // Partial success ); } const users = await userResponse.json(); const createdUser = users[0]; // If roles are specified, assign them if (userData.roles && Array.isArray(userData.roles) && userData.roles.length > 0) { // Get available roles const rolesResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/roles`, { headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, } ); if (rolesResponse.ok) { const availableRoles = await rolesResponse.json(); // Filter valid roles const validRoles = userData.roles.map((roleName: string) => availableRoles.find((r: any) => r.name === roleName) ).filter(Boolean); if (validRoles.length > 0) { // Assign roles to user await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${createdUser.id}/role-mappings/realm`, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify(validRoles), } ); } } } return NextResponse.json({ success: true, user: createdUser }); } catch (error) { console.error("Error creating user:", error); return NextResponse.json( { error: "Server error creating user" }, { status: 500 } ); } }