I want to authenticate my application with Keycloak. I have managed to get an authorization code but I want to get the access and refresh tokens. Here is my code:
async function exchangeAuthorizationCodeForTokens(authorizationCode, clientId, redirectUri, realmName, keycloakUrl) {
const tokenEndpoint = `${keycloakUrl}/realms/${realmName}/protocol/openid-connect/token`;
const codeVerifier = generateCodeVerifier();
const data = {
grant_type: 'authorization_code',
client_id: clientId,
redirect_uri: redirectUri,
code: authorizationCode,
code_verifier: codeVerifier
};
try {
const response = await axios.post(tokenEndpoint, new URLSearchParams(data), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false }), // Ignore self-signed certificate
});
console.log('Token exchange successful');
console.log(response.data);
return {
access_token: response.data.access_token,
refresh_token: response.data.refresh_token,
};
} catch (error) {
console.error('Token exchange failed:', error.response ? error.response.data : error.message);
return false;
}
}
But I always get the following error, even if my user and my client are registered in the Keycloak server:
Token exchange failed: {
error: 'unauthorized_client',
error_description: 'Invalid client or Invalid client credentials'
}
You need to send client_secret
too.
async function exchangeAuthorizationCodeForTokens(authorizationCode, clientId, redirectUri, realmName, keycloakUrl) {
const tokenEndpoint = `${keycloakUrl}/realms/${realmName}/protocol/openid-connect/token`;
const codeVerifier = generateCodeVerifier();
const data = {
grant_type: 'authorization_code',
client_id: clientId,
client_secret: 'Srgc2z4UWrx4RGNg0GZWijLa2uLeR9Yl',
redirect_uri: redirectUri,
code: authorizationCode,
code_verifier: codeVerifier
};
try {
const response = await axios.post(tokenEndpoint, new URLSearchParams(data), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false }), // Ignore self-signed certificate
});
console.log('Token exchange successful');
console.log(response.data);
return {
access_token: response.data.access_token,
refresh_token: response.data.refresh_token,
};
} catch (error) {
console.error('Token exchange failed:', error.response ? error.response.data : error.message);
return false;
}
}
Save as 'token.js' file name
const express = require('express');
const axios = require('axios');
const crypto = require('crypto');
// Express setup
const app = express();
const port = 3000;
// Function to generate a code verifier for PKCE
function generateCodeVerifier() {
return crypto.randomBytes(32).toString('base64')
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
// Your existing function
async function exchangeAuthorizationCodeForTokens(authorizationCode, clientId, redirectUri, realmName, keycloakUrl) {
const tokenEndpoint = `${keycloakUrl}/realms/${realmName}/protocol/openid-connect/token`;
const codeVerifier = generateCodeVerifier();
const data = {
grant_type: 'authorization_code',
client_id: clientId,
client_secret: 'Srgc2z4UWrx4RGNg0GZWijLa2uLeR9Yl',
redirect_uri: redirectUri,
code: authorizationCode,
code_verifier: codeVerifier
};
try {
const response = await axios.post(tokenEndpoint, new URLSearchParams(data), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false }), // Ignore self-signed certificate
});
console.log('Token exchange successful');
console.log(response.data);
return {
access_token: response.data.access_token,
refresh_token: response.data.refresh_token,
};
} catch (error) {
console.error('Token exchange failed:', error.response ? error.response.data : error.message);
return false;
}
}
// TODO: Replace these with your actual configuration values
const clientId = 'my_client';
const redirectUri = 'http://localhost:3000/auth/callback';
const realmName = 'my-realm';
const keycloakUrl = 'http://localhost:8080/auth';
const responseType = 'code';
app.get('/login', (req, res) => {
// Construct the Keycloak login URL
const keycloakLoginUrl = `${keycloakUrl}/realms/${realmName}/protocol/openid-connect/auth?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=${encodeURIComponent(responseType)}&scope=openid`;
// Redirect the user to the Keycloak login page
res.redirect(keycloakLoginUrl);
});
app.get('/auth/callback', async (req, res) => {
const authorizationCode = req.query.code;
if (!authorizationCode) {
return res.status(400).send('Authorization code is required');
}
// Exchange authorization code for tokens
const tokens = await exchangeAuthorizationCodeForTokens(authorizationCode, clientId, redirectUri, realmName, keycloakUrl);
if (tokens) {
res.send(`Access Token: ${tokens.access_token}<br>Refresh Token: ${tokens.refresh_token}`);
} else {
res.status(500).send('Failed to exchange authorization code for tokens');
}
});
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
Redirect URL
& Authorization
setting
Running server
node token.js
Login URL
http://localhost:3000/login