我无法使用 Keycloak API 通过授权代码获取访问和刷新令牌

问题描述 投票:0回答:1

我想使用 Keycloak 验证我的应用程序。我已设法获得授权代码,但我想获得访问令牌和刷新令牌。这是我的代码:

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;
    }
}

但是即使我的用户和客户端已在 Keycloak 服务器中注册,我总是收到以下错误:

Token exchange failed: {
    error: 'unauthorized_client',
    error_description: 'Invalid client or Invalid client credentials'
}
keycloak openid-connect
1个回答
0
投票

您也需要发送

client_secret

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;
    }
}

它应该是从 UI 复制的

演示代码

另存为“token.js”文件名

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
设置

运行服务器

node token.js

登录网址

http://localhost:3000/login

结果

© www.soinside.com 2019 - 2024. All rights reserved.