import { getServerSession } from "next-auth/next"; import { authOptions } from "@/app/api/auth/[...nextauth]/route"; import { NextResponse } from "next/server"; export async function GET() { const session = await getServerSession(authOptions); if (!session) { return NextResponse.json({ error: "Non autorisé" }, { status: 401 }); } console.log("Session:", { accessToken: session.accessToken?.substring(0, 20) + "...", user: session.user, }); try { // Get client credentials token const tokenResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/realms/${process.env.KEYCLOAK_REALM}/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 tokenData = await tokenResponse.json(); console.log("Token response:", { ok: tokenResponse.ok, status: tokenResponse.status, data: tokenData.access_token ? "Token received" : tokenData, }); if (!tokenResponse.ok) { console.error("Failed to get token:", tokenData); return NextResponse.json([getCurrentUser(session)]); } // Get users list with brief=false to get full user details const usersResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users?briefRepresentation=false`, { headers: { Authorization: `Bearer ${tokenData.access_token}`, 'Content-Type': 'application/json', }, } ); if (!usersResponse.ok) { console.error("Failed to fetch users:", await usersResponse.text()); return NextResponse.json([getCurrentUser(session)]); } const users = await usersResponse.json(); console.log("Raw users data:", users.map((u: any) => ({ id: u.id, username: u.username, realm: u.realm, serviceAccountClientId: u.serviceAccountClientId, }))); // Filter out service accounts and users from other realms const filteredUsers = users.filter((user: any) => !user.serviceAccountClientId && // Remove service accounts (!user.realm || user.realm === process.env.KEYCLOAK_REALM) // Only users from our realm ); console.log("Filtered users count:", filteredUsers.length); // Fetch groups for each user const usersWithGroups = await Promise.all(filteredUsers.map(async (user: any) => { try { const groupsResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${user.id}/groups`, { headers: { Authorization: `Bearer ${tokenData.access_token}`, 'Content-Type': 'application/json', }, } ); let groups = []; if (groupsResponse.ok) { const groupsData = await groupsResponse.json(); groups = groupsData.map((group: any) => group.name); console.log(`Groups for user ${user.username}:`, groups); } return { id: user.id, username: user.username, firstName: user.firstName || '', lastName: user.lastName || '', email: user.email, createdTimestamp: user.createdTimestamp, roles: groups, }; } catch (error) { console.error(`Error fetching groups for user ${user.id}:`, error); return { id: user.id, username: user.username, firstName: user.firstName || '', lastName: user.lastName || '', email: user.email, createdTimestamp: user.createdTimestamp, roles: [], }; } })); console.log("Final users data:", usersWithGroups.map(u => ({ username: u.username, roles: u.roles, }))); return NextResponse.json(usersWithGroups); } catch (error) { console.error("Error:", error); return NextResponse.json([getCurrentUser(session)]); } } // Helper function to get current user data function getCurrentUser(session: any) { return { id: session.user.id, username: session.user.username, firstName: session.user.first_name, lastName: session.user.last_name, email: session.user.email, createdTimestamp: Date.now(), roles: session.user.role || [], }; } export async function POST(req: Request) { const session = await getServerSession(authOptions); if (!session) { return NextResponse.json({ error: "Non autorisé" }, { status: 401 }); } try { const data = await req.json(); console.log("Creating user:", data); // First get an admin token using client credentials const tokenResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/realms/${process.env.KEYCLOAK_REALM}/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 tokenData = await tokenResponse.json(); if (!tokenResponse.ok) { console.error("Failed to get admin token:", tokenData); return NextResponse.json( { error: "Erreur d'authentification" }, { status: 401 } ); } // Format user data for Keycloak const keycloakUser = { username: data.username, enabled: true, emailVerified: true, firstName: data.firstName, lastName: data.lastName, email: data.email, groups: [data.realmRoles], // Use groups instead of roles }; // Create user in Keycloak using admin token const createResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users`, { method: "POST", headers: { Authorization: `Bearer ${tokenData.access_token}`, "Content-Type": "application/json", }, body: JSON.stringify(keycloakUser), } ); console.log("Keycloak create response:", { status: createResponse.status, ok: createResponse.ok }); if (!createResponse.ok) { const errorData = await createResponse.json(); console.log("Keycloak error:", errorData); if (errorData.errorMessage?.includes("User exists with same username")) { return NextResponse.json( { error: "Un utilisateur existe déjà avec ce nom d'utilisateur" }, { status: 400 } ); } else if (errorData.errorMessage?.includes("User exists with same email")) { return NextResponse.json( { error: "Un utilisateur existe déjà avec cet email" }, { status: 400 } ); } return NextResponse.json( { error: "Erreur création utilisateur", details: errorData }, { status: 400 } ); } // Get the created user const userResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users?username=${data.username}`, { headers: { Authorization: `Bearer ${tokenData.access_token}`, }, } ); const users = await userResponse.json(); const user = users[0]; if (!user) { return NextResponse.json( { error: "Utilisateur créé mais impossible de le récupérer" }, { status: 500 } ); } // Add user to group await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${user.id}/groups/${data.realmRoles}`, { method: "PUT", headers: { Authorization: `Bearer ${tokenData.access_token}`, }, } ); return NextResponse.json({ success: true, user: { ...user, roles: [data.realmRoles], }, }); } catch (error) { console.error("Error creating user:", error); return NextResponse.json( { error: "Erreur serveur", details: error }, { status: 500 } ); } }