Keycloak-Connect(Node.js)Bearer-Only客户端自定义属性配置及相关技术问题咨询
Hey there, let’s break down your three questions one by one based on my hands-on experience with Keycloak-Connect and Node.js setups:
To add attributes that are only visible to your Bearer-Only backend client (and excluded from tokens to avoid bloat), you can use Client Scopes + Targeted Mappers—here's how:
- First, create a new Client Scope in the Keycloak console (e.g.,
backend-private-attrs). Associate this scope only with your Bearer-Only client, and leave your Public frontend client unlinked. - Add a Script Mapper to this Client Scope with these settings:
- Set
Mapper TypetoScript Mapper - Check
Add to userinfo, but uncheckAdd to ID tokenandAdd to access token(this ensures the attribute never enters tokens) - Use a custom script to inject the private attribute only when the request comes from your Bearer-Only client:
// Example mapper script if (client.clientId === 'your-bearer-only-client-id') { // Pull the private attribute from the user's stored attributes const privateAttr = user.getAttributes().get('private_attribute')?.get(0); if (privateAttr) { userInfo.set('private_attribute', privateAttr); } }
- Set
- When your backend calls the userInfo endpoint, it will receive the private attribute, while your Public frontend client (which doesn't have access to this scope) won't see it.
As an alternative, you can skip modifying userInfo entirely: after validating the access token, have your Bearer-Only client call Keycloak's Admin API /users/{userId} endpoint (you'll need to grant the client view-users permissions) to fetch private attributes directly. This gives you full control without touching the userInfo flow.
The behavior depends entirely on your client mode:
- Bearer-Only Mode: This mode is designed for server-to-server authentication—Keycloak-Connect acts purely as a token validator. It doesn't create or maintain user sessions at all. It just verifies the incoming access token's validity (via Keycloak's introspection or JWKS endpoint) and attaches user data to
req.kauth. This is why you don't see anything in Redis: no sessions are stored here, and this is expected behavior. - Confidential/Public Client Mode (Browser Apps): For apps that need to maintain user login state (like your frontend Public client), Keycloak-Connect relies on Express's session system. By default, it uses in-memory storage, but to use Redis:
- Install dependencies:
npm install express-session connect-redis - Configure Redis as your Express session store, then pass it to Keycloak:
const session = require('express-session'); const RedisStore = require('connect-redis').default; const Keycloak = require('keycloak-connect'); // Initialize Redis store const redisStore = new RedisStore({ /* your Redis config */ }); // Initialize Keycloak with the Redis store const keycloak = new Keycloak({ store: redisStore }, keycloakConfig); // Add Express session middleware before Keycloak app.use(session({ store: redisStore, secret: 'your-secure-session-secret', resave: false, saveUninitialized: false })); - Install dependencies:
Again, this only applies to client modes that need session persistence—your Bearer-Only backend doesn't need this setup.
Short answer: grantmanager.validateAccessToken does NOT get you userInfo—they serve different purposes:
validateAccessToken(when using online validation) calls Keycloak's Token Introspection endpoint. It only returns basic token validity data (e.g., expired, active, audience) but no user attributes or full userInfo.grantmanager.userInfocalls Keycloak's userInfo endpoint, which returns the user's attributes (including your private ones, if configured correctly) but automatically validates the token first (it returns a 401 if the token is invalid).
To reduce request overhead, try these optimizations:
- Embed critical user data in the access token: Use Keycloak mappers to add frequently used user attributes directly to the access token's payload. After validation, you can pull this data from
req.kauth.token.contentwithout calling userInfo. Just avoid overdoing it to keep token size small. - Skip separate validation: Instead of calling
validateAccessTokenfirst, calluserInfodirectly. Since userInfo validates the token automatically, this cuts out one round trip to Keycloak—just handle the 401 error if the token is invalid.
内容的提问来源于stack exchange,提问作者Skander

