Confidential Rest-Api w/ Permissions - 总是 403s - 我做错了什么?

Confidential Rest-Api w/ Permissions - Always 403s - What Am I Doing Wrong?

我已经尝试了好几个小时,但似乎碰壁了。任何 advice/help 将不胜感激。

目标: 我想授权 express rest-api (ex client-id: "my-rest-api") 路由 (示例资源:“WeatherForecast”)跨越映射到客户端范围的各种 HTTP 方法(示例:“创建”/“读取”/“更新”/“删除”)。我想通过策略控制这些权限(例如 - 如果满足策略“仅限管理员组”(用户属于管理员组),将授予“读取 - WeatherForecast - 权限”。

Rest-api 不会让用户登录(将从前端直接与 keycloak 对话完成,然后他们将使用该令牌与 rest-api 对话)。

环境:

发生了什么: 我可以通过 postman 从 keycloak 登录页面登录并获取访问令牌。但是,当我访问任何使用 keycloak.protect() 或 keycloak.enforce() 的端点(有或没有指定资源权限)时,我无法通过。在以下代码中,删除端点 returns 返回 200 + 邮递员中 keycloak 登录页面的 HTML 和 Get returns 返回 403 +“拒绝访问”。

当前境界

Nodejs 代码的当前状态:

import express from 'express';
import bodyParser from 'body-parser';
import session from "express-session";
import KeycloakConnect from 'keycloak-connect';

const app = express();

app.use(bodyParser.json());

const memoryStore = new session.MemoryStore();
app.use(session({
    secret: 'some secret',
    resave: false,
    saveUninitialized: true,
    store: memoryStore
  }));

const kcConfig: any = { 
    clientId: 'my-rest-api',
    bearerOnly: true,
    serverUrl: 'http://localhost:8080/auth',
    realm: 'my-realm',
};
const keycloak = new KeycloakConnect({ store: memoryStore }, kcConfig);

app.use(keycloak.middleware({
    logout: '/logout',
    admin: '/',
}));

app.get('/api/WeatherForecast', keycloak.enforcer(['WeatherForecast:read'],{  resource_server_id: "my-rest-api"}), function (req, res) {
   res.json("GET worked")
  });

app.delete('/api/WeatherForecast', keycloak.protect(), function (req, res) {
     res.json("DELETE worked")
});

app.listen(8081, () => {
    console.log(`server running on port 8081`);
  });
  

尝试了一些其他的东西:

我真的很想使用 Keycloak,我觉得我的团队非常接近能够做到这一点,但需要一些帮助才能通过这一部分。谢谢!

-------------------- 结束原始问题 -------------- ----------

EDIT/UPDATE #1: 好吧,所以又花了几个小时。决定通读它命中的每一行 keycloak-connect 库并在进行时进行调试。发现在 checkPermissions 的最后一行 keycloak-connect/middleware/auth-utils/grant-manager.js 里面失败了。没有显示任何错误或要调试的 catch 块 - 进一步追逐兔子洞我发现它发生在使用带有选项的 http 的获取方法中:

'{"protocol":"http:","slashes":true,"auth":null,"host":"localhost:8080","port":"8080","hostname":"localhost","hash":null,"search":null,"query":null,"pathname":"/auth/realms/my-realm/protocol/openid-connect/token","path":"/auth/realms/my-realm/protocol/openid-connect/token","href":"http://localhost:8080/auth/realms/my-realm/protocol/openid-connect/token","headers":{"Content-Type":"application/x-www-form-urlencoded","X-Client":"keycloak-nodejs-connect","Authorization":"Basic YW(etc...)Z2dP","Content-Length":1498},"method":"POST"}'

它似乎没有进入那个 fetch/http 包装器的回调。我在我的启动命令中添加了 NODE_DEBUG=http 并且能够找到吞下的错误,这似乎我回到了起跑线:

HTTP 31: SOCKET ERROR: connect ECONNREFUSED 127.0.0.1:8080 Error: connect ECONNREFUSED 127.0.0.1:8080

    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1157:16)

    at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17)

然后我看到一些我认为可能与我的 docker 网络设置 () 相关的东西,并尝试更改主机名 dns 以便我可以使用本地主机以外的东西,但是它也没有用(甚至添加到重定向 uri 等)。

更新#2: 好吧,我现在可以使用 keycloak.protect()(纯身份验证)端点了。我通过阅读 keycloak-connect lib 代码发现了更多选项,并且似乎在实例化 keycloak-connect 时将“realmPublicKey”添加到 keycloak 配置对象修复了那个。在授权 keycloak.enforce 方面仍然没有成功。

const kcConfig: any = { 
    clientId: 'my-rest-api',
    bearerOnly: true,
    serverUrl: 'http://localhost:8080/auth',
    realm: 'my-realm',
    realmPublicKey : "MIIBIjANBgk (...etc) uQIDAQAB",
};

所以我的团队终于想通了 - 解决方案分为两部分:

  1. 遵循类似问题 Whosebug 问题答案的说明,例如: 粗略的步骤以防 link 以某种方式被破坏:
  • 为 127.0.0.1 密钥斗篷添加主机条目(如果 'keycloak' 是密钥斗篷的 docker 容器的名称,我将 docker-compose 更改为指定容器名称以使其成为再多一点 fool-proof)
  • 将 keycloak-connect 配置 authServerUrl 设置更改为:'http://keycloak:8080/auth/' 而不是 'http://localhost:8080/auth/'
  1. Postman OAuth 2.0 令牌请求 Auth URL 和访问令牌 URL 更改为使用现在更新的主机条目:
  • "http://localhost:8080/auth/realms/abra/protocol/openid-connect/auth" -> "http://keycloak:8080/auth/realms/abra/protocol/openid-connect/auth"
  • "http://localhost:8080/auth/realms/abra/protocol/openid-connect/token" -> “http://keycloak:8080/auth/realms/abra/protocol/openid-connect/token”