Keycloak 会话 - 当我将它们与管理页面断开连接时,它们仍然有效

发布于 2025-01-11 00:23:33 字数 1750 浏览 0 评论 0原文

我有一个 Keycloak 领域,其中一些用户作为 Nodejs + Typescript 项目的 IdP。

我目前有这两个会议,如图所示: 照片

如果我按全部注销,它们会从这里消失,但它们仍然有效。

示例:

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.

这是我的 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};

我的受保护路由

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");
});

我是否误解了令牌流?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

看春风乍起 2025-01-18 00:23:34

一般来说,会话​​独立于 JWT。 JWT 的优点(和缺点)是,应用程序可以仅使用发行者公钥进行加密验证,该公钥可以是预共享的,也可以是动态查找和缓存的。 JWT 是自编码访问的示例令牌意味着您可以验证它,而无需不断回调中央身份验证/授权服务。这使得它们成为分布式系统和零信任架构中身份验证的理想选择,只要您可以信任发行者公钥,您就可以验证和信任所提供的 JWT。

但是这也有一个缺点。 JWT 包含一个到期时间,在此之前应用程序将认为它有效。正如已经指出的,JWT 的使用者(例如 REST API)不需要与发行服务进行对话来验证它。

因此,在您的特定情况下,您的应用程序会看到 JWT,以加密方式验证它并验证它尚未过期,从而接受它。

使用 JWT 的系统的去中心化性质意味着通常不会撤销 JWT 本身。这是因为它需要维护一个撤销列表,并且您必须有一个中央服务来做到这一点。这并不是说这在技术上是不可能的,但这样做首先会失去使用 JWT 的大部分好处。

由于会话的管理独立于 JWT,因此您可以使会话无效,同时为其颁发的 JWT 仍然有效。如果您需要有效的会话,那么您必须独立于基于 JWT 的身份验证来强制执行。

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.

想你只要分分秒秒 2025-01-18 00:23:34

您缺少的是 OAuth 2.0 的 令牌内省 。正如 RobV 所提到的,JWT 可以用来验证访问令牌,但另一方面,可以对服务器进行网络调用来验证它。

当使用keycloak-connect时,这是在grant-manager的validateAccessToken函数中实现的。另一方面,keycloak.protect() 请求处理程序的构建方式是,如果访问令牌无效,则使用刷新令牌来颁发新的令牌(比较 代码)。因此,尽管您被按下注销,但它会自动再次让您登录。

您可以使用另一个自定义处理程序来内省令牌(也在此处进行了讨论):

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 仅允许在客户端机密时调用内省端点。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文