diff --git a/lib/keycloak.ts b/lib/keycloak.ts index 3232e913..0281cb53 100644 --- a/lib/keycloak.ts +++ b/lib/keycloak.ts @@ -53,45 +53,54 @@ export async function getKeycloakAdminClient(): Promise { console.log(`Connecting to Keycloak at ${keycloakUrl}, realm: ${realmName}, client: ${adminClientId}`); try { - const kcAdminClient = new KcAdminClient({ - baseUrl: keycloakUrl, - realmName: 'master', // Use master realm for admin operations - }); - - // Log auth configuration (don't log the actual secret or password) - console.log('Auth configuration:', { - clientId: adminClientId, - hasClientSecret: !!clientSecret, - hasUsername: !!adminUsername, - hasPassword: !!adminPassword, - authUrl: `${keycloakUrl}/realms/master/protocol/openid-connect/token` - }); - - // Authenticate admin client - let authParams: Credentials; + // Try a direct authentication approach first to debug the issue + console.log('Trying direct authentication to debug...'); - // If we have a client secret, try to use client credentials grant + // Create form data for authentication + const formData = new URLSearchParams(); + formData.append('client_id', adminClientId); if (clientSecret) { - console.log('Using client credentials grant with client secret'); - authParams = { - clientId: adminClientId, - clientSecret: clientSecret, - grantType: 'client_credentials' - }; + formData.append('client_secret', clientSecret); + formData.append('grant_type', 'client_credentials'); } else { - // Fall back to password grant - console.log('Using password grant without client secret'); - authParams = { - clientId: adminClientId, - username: adminUsername, - password: adminPassword, - grantType: 'password' - }; + formData.append('username', adminUsername); + formData.append('password', adminPassword); + formData.append('grant_type', 'password'); } - await kcAdminClient.auth(authParams); - - console.log('Successfully authenticated with Keycloak admin client'); + // Determine the token URL + const tokenUrl = `${keycloakUrl}/realms/master/protocol/openid-connect/token`; + console.log(`Authenticating to: ${tokenUrl}`); + + // Make the request directly + const response = await fetch(tokenUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: formData, + }); + + // Log detailed response information + console.log(`Authentication response status: ${response.status}`); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + console.log('Authentication error:', errorData); + throw new Error(`Direct authentication failed: ${errorData.error || response.statusText}`); + } + + const tokenData = await response.json(); + console.log('Authentication successful, received token data'); + + // Now proceed with the Keycloak admin client + const kcAdminClient = new KcAdminClient({ + baseUrl: keycloakUrl, + realmName: 'master', + }); + + // Set the token manually since we already authenticated + kcAdminClient.setAccessToken(tokenData.access_token); // Now that we're authenticated, we can specify the realm we want to work with // This could be different from the authentication realm (master) @@ -105,10 +114,23 @@ export async function getKeycloakAdminClient(): Promise { return kcAdminClient; } catch (error) { console.error('Error connecting to Keycloak:', error); + // Add more detailed error information if (error instanceof Error) { console.error(`Error message: ${error.message}`); console.error(`Error cause: ${error.cause}`); + + // Try to extract more information from the error + const anyError = error as any; + if (anyError.response) { + console.error('Response status:', anyError.response.status); + console.error('Response headers:', anyError.response.headers); + + // Try to extract response data if available + if (anyError.responseData) { + console.error('Response data:', JSON.stringify(anyError.responseData, null, 2)); + } + } } // For debugging - show what values we're trying to use (without exposing the password)