node.jsjwtkeycloakkeycloak-connect

Keycloak sessions - when I disconnect them from the admin page, they still work


I have a Keycloak realm with some users as an IdP for a nodejs + typescript project.

I currently have those two sessions, as it shows: photo

if I press on Logout all, they disappear from here but they still work.

Example:

1) I create a new session. I get its JWT token as a response
2) I do a GET req on one of the protected routes with the token attached. It works.
4) I logout all sessions by pressing the button in that photo
5) I do the same GET req on the same protected route. It still works.
 I expect it NOT to work, because I previously logged out all sessions.

Here's my keycloak-config

import express, {Application} from 'express';
import { Keycloak as KeycloakType } from "keycloak-connect";

var session = require('express-session');
var Keycloak = require('keycloak-connect');

let _keycloak: KeycloakType;

var memoryStore = new session.MemoryStore();

let kcConfig = {
    clientId: 'restapi',
    bearerOnly: true,
    serverUrl: 'http://localhost:8080/auth',
    realm: 'supercatalog',
    realmPublicKey: 'deleted'
};

function getKeycloak() {
    if (_keycloak) {
        return _keycloak;
    } 
    console.log("Initializing Keycloak...");
    _keycloak = new Keycloak({ store: memoryStore }, kcConfig);
    return _keycloak;
}

export {getKeycloak, memoryStore};

my protected route

router.get('/', keycloak.protect(), async (req:Request, res:Response):Promise<void> => {
    var bearerToken: string = await (await keycloak.getGrant(req, res)).toString() as string;
    var decoded: any = jwtDecode(bearerToken);
    console.log(decoded.resource_access.restapi.roles);
    res.send("hello");
});

Am I misunderstanding the token flow?


Solution

  • Generally speaking a session is independent of a JWT. The advantage (and disadvantage) of JWTs is that an app can cryptographically verify them with only the issuers public key(s), which may either be pre-shared or dynamically looked up and cached. A JWT is an example of a self-encoded access token meaning that you can verify it without continually calling back to a central authentication/authorization service. This makes them ideal for authentication in distributed systems and zero-trust architectures where provided you can trust the issuers public key you can verify and trust a presented JWT.

    BUT that comes with a downside. The JWT includes an expiry time, until which time applications will consider it as valid. And as already noted consumers of a JWT, like a REST API, don't need to talk to the issuing service to verify it.

    So in your specific case your application sees a JWT, cryptographically validates it and verifies that it is has not expired and thus accepts it.

    The decentralised nature of systems that use JWT means revoking the JWTs themselves is typically not done. This is because it would require maintaining a revocation list and you'd have to have a central service to do that. That's not to say it isn't technically possible but that to do so loses much of the benefits of using JWTs in the first place.

    As sessions are managed independently of JWTs you can invalidate a session while the JWT that was issued for it remains valid. If you require a valid session then that's something you'll have to enforce independently of JWT based authentication.