jwtkeycloaklogoutkeycloak-rest-apikeycloak-nodejs-connect

keycloak logout doesn't invalidate token when call a rest api


I've a React app that uses Keycloak as a authentication service. Also I've a Nodejs rest api with endpoints secured by keycloak, so the React app sends JWT when needs call an api. In Keycloak admin console I created 1 public client with users and roles.

All works fine, but the only problems is when a I logout through admin console, or from my React application berfore that expiration time, I still can call to my app with these token.

Why my backend app doesn't validate the token with server?

My node app uses keycloak-node-connect adapter and my keycloak.json is:

{
    "client-id": "my-public-client",
    "bearer-only": true,
    "auth-server-url": "http://localhost:8180/auth",
    "realm": "my-realm"    
}

Solution

  • Solved

    I can solved my probleam like suggested in Keycloak: Access token validation end point

    keycloak.config.js

    var session = require('express-session');
    var Keycloak = require('keycloak-connect');
    var request = require('request');
    const createError = require('http-errors');
    
    let _keycloak;
    
    var memoryStore = new session.MemoryStore();
    
    function initKeycloak() {
        if (_keycloak) {
            console.log("Trying to init Keycloak again!");
            return _keycloak;
        }
        else {
            console.log("Initializing Keycloak...");
            _keycloak = new Keycloak({ store: memoryStore });
            return _keycloak;
        }
    }
    
    function getKeycloak() {
        if (!_keycloak) {
            console.error('Keycloak has not been initialized. Please called init first.');
        }
        return _keycloak;
    }
    
    async function validateTokenKeycloak(req, res, next) {
        if (req.kauth && req.kauth.grant) {        
            console.log('--- Verify token ---');
            try {
                var result = await _keycloak.grantManager.userInfo(req.kauth.grant.access_token);
                //var result = await _keycloak.grantManager.validateAccessToken(req.kauth.grant.access_token);
                if(!result) {
                    console.log(`result:`,  result); 
                    throw Error('Invalid Token');
                }                        
            } catch (error) {
                console.log(`Error: ${error.message}`);
                return next(createError.Unauthorized());
            }
        }
        next();  
    }
    
    module.exports = {
        memoryStore,
        initKeycloak,
        getKeycloak,
        validateTokenKeycloak
    };

    app.js

    const express = require('express');
    const createError = require('http-errors');
    const dotenv = require('dotenv').config();
    const session = require('express-session');
    const keycloakConfig = require('./config/keycloak.config');
    
    const app = express();
    
    // Keycloak
    app.use(session({
        secret: 'secret',
        resave: false,
        saveUninitialized: true,
        store: keycloakConfig.memoryStore
    }));
      
    const keycloak = keycloakConfig.initKeycloak();
      
    app.use(keycloak.middleware());
    
    app.use(keycloakConfig.validateTokenKeycloak);
    
    app.use("/health", require('./routes/health.route'));
    
    // 404 handler and pass to error handler
    app.use((req, res, next) => {    
        next(createError(404, 'Not found'));
    });
    
    // Error Handler
    app.use((err, req, res, next) => {  
        res.status(err.status || 500);
        res.send({
            error : {
                status : err.status || 500,
                message : err.message
            }
        });
    });
    
    const PORT = process.env.PORT || 3000;
    
    
    app.listen(PORT, () => {
        console.log(`Server starter on port ${PORT}`);
    });