I have created a node.js app with Keycloak middleware for Express JS, which proverbially "works on my computer".
const Keycloak = require('keycloak-connect')
const express = require('express')
const session = require('express-session')
const app = express()
const LOGIN_PATH = '/login'
const LOGOUT_PATH = '/logout'
const SESSION_STORE_PASS = '123456789012345678901234567890!!!'
const server = app.listen(3000, function () {
const host = server.address().address
const port = server.address().port
console.log('Example app listening at http://%s:%s', host, port)
})
app.get('/', function (req, res) {
res.redirect(LOGIN_PATH)
})
// Create a session-store to be used by both the express-session
// middleware and the keycloak middleware.
const memoryStore = new session.MemoryStore()
app.use(session({
secret: SESSION_STORE_PASS,
resave: false,
saveUninitialized: true,
store: memoryStore
}))
const kcOptions = {
"realm": "nodejs-example",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth",
"ssl-required": "external",
"resource": "nodejs-connect",
"public-client": true
}
console.log("Starting Keycloak connector with options: ", kcOptions)
const keycloak = new Keycloak({store: memoryStore}, kcOptions)
app.use(keycloak.middleware({
logout: LOGOUT_PATH, // <-- this is supposed to delete the session and log you out from KC as well
admin: '/'
}))
app.get('/login', keycloak.protect(), async function (req, res) {
let token
try {
const grant = await keycloak.getGrant(req, res)
token = grant.access_token
console.log(`Found token ${token}`)
} catch (e) {
console.log("Unable to find the token in KC response ", req.session)
throw new Error(e)
}
const userProfile = await keycloak.grantManager.userInfo(token)
console.log("Found user profile:", userProfile)
res.header("Content-Type", 'application/json')
return res.send(JSON.stringify(userProfile, null, 4))
})
This app listens on port 3000, but the problem is that browsers are going to reach it through the reverse proxy (port 80). And there is no way for me to make the middleware send the correct "redirect_uri" query parameter to Keycloak. It always puts a URL with same port 3000 where the local server is listening.
I found redirection-url
setting in the documentation, but I found no evidence of that string in the Github repository.
From some code analisys in the keycloak-nodejs-connect repository, the
redirect_uri
query parameter is merely assembled from the incoming request, assuming the app is directly reachable.And this issue affects both login and logout phase. Therefore - unfurtunately - there's no way to use this library in a realistic situation (where a reverse proxy, or load balancer is involved).
Not to mention the strange disconnect between features described in the documentation and the actually implemented ones.