响应中的 id_token 缺少随机数

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

我使用 Keycloak 21 进行身份验证,但遇到一个问题,即调用 /token 后返回的 id_token 中不包含随机数值。我正在 /auth 请求中传递随机数,

keycloak
1个回答
0
投票

通过这个方法就可以得到

URL

GET ${KEYCLOAK_URL}/realms/${REALM}/protocol/openid-connect/auth?

query parameters

        client_id: CLIENT_ID,
        redirect_uri: REDIRECT_URI,
        response_type: 'code',
        scope: 'openid',
        nonce: nonce,

然后在回调 URL 处获取 ID 令牌

步骤

第1步:通过docker compose启动Keycloak v21

version: '3.9'

services:
  keycloak:
    image: quay.io/keycloak/keycloak:21.1.2
    container_name: keycloak
    command:
      - start-dev
    environment:
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin
      KC_DB: postgres
      KC_DB_URL_HOST: keycloak-db
      KC_DB_URL_DATABASE: keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: keycloakpassword
      KC_HOSTNAME: localhost
    ports:
      - "8080:8080"
    depends_on:
      - keycloak-db
    volumes:
      - keycloak-data:/opt/keycloak/data

  keycloak-db:
    image: postgres:15
    container_name: keycloak-db
    environment:
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: keycloakpassword
      POSTGRES_DB: keycloak
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  keycloak-data:
  postgres-data:

更多详细信息请参见这里

第2步.创建my-realm、my-cleint和user1

enter image description here

回拨网址

http://localhost:3000/callback

enter image description here

来自 UI 的客户端机密副本

enter image description here

username
是用户 1,
password
是 1234

enter image description here

enter image description here

第3步:运行express服务器

install dependencies

npm install express axios querystring [email protected] crypto

demo.js

const express = require('express');
const axios = require('axios');
const crypto = require('crypto');
const querystring = require('querystring');
const jwtDecode = require('jwt-decode');

const app = express();
const PORT = 3000;

// Keycloak Configuration
const KEYCLOAK_URL = 'http://localhost:8080'; // Replace with your Keycloak server URL
const REALM = 'my-realm'; // Replace with your realm name
const CLIENT_ID = 'my-client'; // Replace with your client ID
const CLIENT_SECRET = '[your my-client password]'; // Replace if your client is confidential
const REDIRECT_URI = 'http://localhost:3000/callback'; // Replace with your app's redirect URI

// Generate a random nonce
const generateNonce = () => crypto.randomBytes(16).toString('base64');

// Store the nonce temporarily (use a proper session store in production)
let storedNonce;

// Routes

// 1. Start Authorization
app.get('/login', (req, res) => {
    const nonce = generateNonce();
    storedNonce = nonce; // Save the nonce for validation later

    const authUrl = `${KEYCLOAK_URL}/realms/${REALM}/protocol/openid-connect/auth?` + querystring.stringify({
        client_id: CLIENT_ID,
        redirect_uri: REDIRECT_URI,
        response_type: 'code',
        scope: 'openid',
        nonce: nonce,
    });

    res.redirect(authUrl); // Redirect user to Keycloak login
});

// Step 2. Handle Callback for get tokens
app.get('/callback', async (req, res) => {
    const { code } = req.query;

    if (!code) {
        return res.status(400).send('Authorization code not found!');
    }

    try {
        const tokenResponse = await axios.post(
            `${KEYCLOAK_URL}/realms/${REALM}/protocol/openid-connect/token`,
            querystring.stringify({
                grant_type: 'authorization_code',
                code: code,
                redirect_uri: REDIRECT_URI,
                client_id: CLIENT_ID,
                client_secret: CLIENT_SECRET,
            }),
            { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
        );

        const { id_token } = tokenResponse.data;

        // Decode and validate the ID token
        const decodedToken = jwtDecode(id_token);

        if (decodedToken.nonce !== storedNonce) {
            return res.status(401).send('Invalid nonce!');
        }

        res.send({
            message: 'Login successful!',
            id_token: decodedToken,
        });
    } catch (error) {
        console.error('Error exchanging code for tokens:', error);
        res.status(500).send('Error exchanging code for tokens.');
    }
});

// Start the Express server
app.listen(PORT, () => {
    console.log(`Server running at http://localhost:${PORT}`);
});

run it

node demo.js

enter image description here

第四步通过浏览器获取token

http://localhost:3000/login

结果

enter image description here

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