Home > front end >  Keycloak sessions - when I disconnect them from the admin page, they still work
Keycloak sessions - when I disconnect them from the admin page, they still work

Time:03-01

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?

CodePudding user response:

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-shred 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.

CodePudding user response:

What you are missing, is called the Token introspection of OAuth 2.0. As mentioned by RobV, the JWT can be used to verify the access token without, but on the otherhand, it is possible to do a network call to the server to validate it.

When using keycloak-connect, this is implemented in the function validateAccessToken of grant-manager. On the otherhand, the keycloak.protect() request-handler is build in the way, that if the access-token is invalid, it is using refresh-token to issue a new one (compare code). So although you are pressed logout, it automically logs you in again.

You could use another custom handler to introspect the token (also discussed here):

const introspection = async function (req: any, res: any, next: any) {
    try {
        let grant = await keycloak.getGrant(req, res);
        let isValid = await keycloak.grantManager.validateAccessToken(grant.access_token!!);
        if (!isValid) {
            return keycloak.accessDenied(req, res);
        } else {
            return next();
        }
    } catch (e) {
        console.log(e);
        return keycloak.accessDenied(req, res);
    }
}

app.get('/protected/resource', keycloak.checkSso(), introspection, function (req, res, next) {
    console.log('I am in');
}

Keycloak only allows calling introspection endpoint when client is confidential though.

  • Related