Good time to all, As in keycloak version 26.0.6. how to enable Allow token exchange (for token renewal)
I tried to turn it on through the console but it didn't help
I would be very grateful if you would send a link to the documentation where it is written about it
curl --location 'http://localhost:8080/realms/master/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=[your client id]' \
--data-urlencode 'client_secret=[your client secret]' \
--data-urlencode 'audience=[your audience client]' \
--data-urlencode 'requested_token_type=urn:ietf:params:oauth:token-type:refresh_token' \
--data-urlencode 'subject_token=[your client token]'
By kc.sh
bin/kc.sh build --features="preview,token-exchange"
OR
By environment variable in docker-compose.yml
KC_FEATURES: preview,token-exchange
docker-compose.yml
version: '3.9'
services:
keycloak_web:
image: quay.io/keycloak/keycloak:26.0.6
container_name: keycloak_web
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://keycloak_db:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: password
KC_HOSTNAME: localhost
KC_HOSTNAME_PORT: 8080
KC_HOSTNAME_STRICT: false
KC_HOSTNAME_STRICT_HTTPS: false
KC_LOG_LEVEL: info
KC_METRICS_ENABLED: true
KC_HEALTH_ENABLED: true
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
KC_FEATURES: preview,token-exchange
command:
- start-dev
depends_on:
- keycloak_db
ports:
- 8080:8080
keycloak_db:
image: postgres:15
container_name: keycloak_db
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: password
volumes:
postgres_data:
docker exec -it keycloak_web ./opt/keycloak/bin/kc.sh show-config
Should be this shape
kc.features = preview,token-exchange
Current Mode: development
Current Configuration:
kc.cache = local (classpath keycloak.conf)
kc.config.args = show-config (SysPropConfigSource)
kc.config.built = true (SysPropConfigSource)
kc.db = postgres (ENV)
kc.db-password = ******* (ENV)
kc.db-url = jdbc:postgresql://keycloak_db:5432/keycloak (ENV)
kc.db-username = keycloak (ENV)
kc.features = preview,token-exchange (ENV)
kc.health-enabled = true (ENV)
kc.hostname = localhost (ENV)
kc.hostname-port = 8080 (ENV)
kc.hostname-strict = false (ENV)
kc.hostname-strict-https = false (ENV)
kc.http-enabled = true (classpath keycloak.conf)
kc.log-console-output = default (classpath keycloak.conf)
kc.log-level = info (ENV)
kc.metrics-enabled = true (ENV)
kc.run-in-container = true (ENV)
kc.spi-hostname-v2-hostname = localhost (ENV)
kc.spi-hostname-v2-hostname-strict = false (ENV)
kc.spi-theme-cache-templates = false (classpath keycloak.conf)
kc.spi-theme-cache-themes = false (classpath keycloak.conf)
kc.spi-theme-static-max-age = -1 (classpath keycloak.conf)
kc.version = 26.0.6 (SysPropConfigSource)
permission
tabWhen you create client
Example
Client : `my-client`
Audience: `target-client`
Copy my-client's secret
my-client
curl
Using bash terminal with jq
If not have jq
, download in here
Replace my-client client secret
in client_secret
Get client token first
CLIENT_TOKEN=$(curl --location 'http://localhost:8080/realms/master/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=my-client' \
--data-urlencode 'client_secret=GpiPmEsiqvZbF0YV55Q7KxTrmN1vk7tI' | jq -r '.access_token')
echo 'CLIENT_TOKEN = '$CLIENT_TOKEN
Exchange token with client token
EXCHANGE_TOKEN=$(curl --location 'http://localhost:8080/realms/master/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=my-client' \
--data-urlencode 'client_secret=GpiPmEsiqvZbF0YV55Q7KxTrmN1vk7tI' \
--data-urlencode 'audience=target-client' \
--data-urlencode 'requested_token_type=urn:ietf:params:oauth:token-type:refresh_token' \
--data-urlencode 'subject_token='$CLIENT_TOKEN | jq -r '.access_token')
echo 'EXCHANGE_TOKEN = '$EXCHANGE_TOKEN
var jsonData = JSON.parse(responseBody);
postman.setEnvironmentVariable("client_token", jsonData.access_token);
grant_type : client_credentials
client_id: my-client
client_secret: [my-client's secret]
grant_type : client_credentials
client_id: my-client
client_secret: [my-client's secret]
audience: [your audience client]
requested_token_type: urn:ietf:params:oauth:token-type:refresh_token
subject_token : [your client token]
demo.js
// demo.js
const axios = require('axios');
const jwt = require('jsonwebtoken'); // Import jsonwebtoken
// Utility function to delay execution
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const decodeToken = (token) => {
try {
// Decode the token without verifying it
const decoded = jwt.decode(token, { complete: true });
return decoded;
} catch (error) {
console.error('Failed to decode token:', error.message);
return null;
}
};
const getClientToken = async (clientId, clientSecret) => {
try {
const resp = await axios.post(
'http://localhost:8080/realms/master/protocol/openid-connect/token',
new URLSearchParams({
'client_id': clientId,
'client_secret': clientSecret,
'grant_type': 'client_credentials'
})
);
return resp.data.access_token;
} catch (err) {
console.error(err);
}
};
const getExchangeToken = async (clientId, clientSecret, audience, subjectToken) => {
try {
const resp = await axios.post(
'http://localhost:8080/realms/master/protocol/openid-connect/token',
new URLSearchParams({
'grant_type': 'client_credentials',
'client_id': clientId,
'client_secret': clientSecret,
'audience': audience,
'requested_token_type': 'urn:ietf:params:oauth:token-type:refresh_token',
'subject_token': subjectToken
}),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);
return resp.data.access_token;
} catch (err) {
console.error('Error fetching exchange token:', err.response?.data || err.message);
}
};
(async () => {
const clientId = 'my-client';
const clientSecret = 'replace your client secret';
const audienceClientId = 'target-client';
const client_token = await getClientToken(clientId, clientSecret);
if (client_token) {
const decodedToken = await decodeToken(client_token);
if (decodedToken) {
console.log('Decoded Client Token:', JSON.stringify(decodedToken, null, 2));
}
console.log('\nWaiting for 2 seconds before exchanging token...');
await delay(2000); // Wait for 2 seconds
const exchangeToken = await getExchangeToken(clientId, clientSecret, audienceClientId, client_token);
if (exchangeToken) {
const decodedExchangeToken = decodeToken(exchangeToken);
if (decodedExchangeToken) {
console.log('Decoded Exchange Token:', JSON.stringify(decodedExchangeToken, null, 2));
}
}
}
})();
npm install axios jsonwebtoken
In here search by exchange
keyword