Keycloak:访问令牌验证终点

时间:2018-01-16 04:45:32

标签: node.js keycloak

在独立模式下运行keycloak。并使用node.js适配器创建一个微服务来验证api调用。

来自keyclaok的jwt令牌正在与每个api调用一起发送。它只会在发送的令牌有效时才会响应。

  • 如何验证微服务中的访问令牌?
  • keycloak是否有任何令牌验证?

4 个答案:

答案 0 :(得分:5)

要在troger19's answer上展开:

  

问题1:如何从微服务验证访问令牌?

实现一个功能,以检查每个对承载令牌的请求,并在将令牌传递给api的路由处理程序之前,将该令牌发送出去,供您的keycloak服务器在userinfo端点进行验证。

您可以通过请求密钥访问服务器well-known configuration来找到密钥斗篷服务器的特定终结点(例如userinfo路由)。

如果您在节点api中使用expressjs,则可能如下所示:

const express = require("express");
const request = require("request");

const app = express();

/*
 * additional express app config
 * app.use(bodyParser.json());
 * app.use(bodyParser.urlencoded({ extended: false }));
 */

const keycloakHost = 'your keycloak host';
const keycloakPort = 'your keycloak port';
const realmName = 'your keycloak realm';

// check each request for a valid bearer token
app.use((req, res, next) => {
  // assumes bearer token is passed as an authorization header
  if (req.headers.authorization) {
    // configure the request to your keycloak server
    const options = {
      method: 'GET',
      url: `https://${keycloakHost}:${keycloakPort}/auth/realms/${realmName}/protocol/openid-connect/userinfo`,
      headers: {
        // add the token you received to the userinfo request, sent to keycloak
        Authorization: req.headers.authorization,
      },
    };

    // send a request to the userinfo endpoint on keycloak
    request(options, (error, response, body) => {
      if (error) throw new Error(error);

      // if the request status isn't "OK", the token is invalid
      if (response.statusCode !== 200) {
        res.status(401).json({
          error: `unauthorized`,
        });
      }
      // the token is valid pass request onto your next function
      else {
        next();
      }
    });
  } else {
    // there is no token, don't process request further
    res.status(401).json({
    error: `unauthorized`,
  });
});

// configure your other routes
app.use('/some-route', (req, res) => {
  /*
  * api route logic
  */
});


// catch 404 and forward to error handler
app.use((req, res, next) => {
  const err = new Error('Not Found');
  err.status = 404;
  next(err);
});
  

问题2:Keycloak是否提供令牌验证?

向Keycloak的userinfo端点发出请求是一种验证令牌有效的简便方法。

来自有效令牌的用户信息响应:

  

状态:200 OK

{
    "sub": "xxx-xxx-xxx-xxx-xxx",
    "name": "John Smith",
    "preferred_username": "jsmith",
    "given_name": "John",
    "family_name": "Smith",
    "email": "john.smith@example.com"
}

来自无效的有效令牌的用户信息响应:

  

状态:401未经授权

{
    "error": "invalid_token",
    "error_description": "Token invalid: Token is not active"
}

其他信息:

Keycloak提供了自己的名为keycloak-connect的npm软件包。该文档描述了路由上的简单身份验证,要求用户登录才能访问资源:

app.get( '/complain', keycloak.protect(), complaintHandler );

我还没有发现这种方法仅适用于仅承载身份验证。以我的经验,在路由上实施这种简单的身份验证方法会导致“访问被拒绝”响应。 This question还询问如何使用Keycloak访问令牌对rest api进行身份验证。 The accepted answer建议使用keycloak-connect提供的简单身份验证方法,但也要使用Alex在评论中指出的方式:

  

“ keyloak.protect()函数(不)从以下位置获取承载令牌   标头。我仍在寻找仅做承载的解决方案   身份验证– Alex 17年11月2日在14:02

答案 1 :(得分:2)

我会使用此UserInfo端点,您还可以使用它来检查其他属性,如电子邮件以及您在映射器中定义的内容。您必须使用Bearer在标头属性中发送访问令牌 授权:Bearer access_token

http://localhost:8081/auth/realms/demo/protocol/openid-connect/userinfo

答案 2 :(得分:1)

@kfrisbie感谢您的答复,对于您的示例,我可以使用keycloak连接适配器重构代码:

// app.js
app.use(keycloakConfig.validateTokenKeycloak); // valid token with keycloak server

// add routes
const MyProtectedRoute = require('./routes/protected-routes'); // routes using keycloak.protect('some-role')
app.use('/protected', MyProtectedRoute);

因此,当发送授权标头时,我可以验证令牌对于keycloak服务器仍然有效,因此,如果从管理控制台注销,或者在到期令牌之前从前台spa注销,我的api会抛出401错误,在其他情况下为keycloak使用保护方法。

// keycloak.config.js
let memoryStore = new session.MemoryStore();
let _keycloak = new Keycloak({ store: memoryStore });

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 = {
    validateTokenKeycloak
};

答案 3 :(得分:0)

两种验证令牌的方法

  • 在线
  • 离线

上述变体是在线验证。当然,这很昂贵,因为它引入了另一个http /往返行程。

更有效的是 离线验证。例如,您可以使用此nodejs库keycloak-backend进行验证,而无需任何远程请求。脱机验证可以这么简单:

token = await keycloak.jwt.verifyOffline(someAccessToken, cert);
console.log(token); //prints the complete contents, with all the user/token/claim information...

您可能会知道 JWT令牌 base64 编码的JSON对象,其中包含所有信息(声明)以进行离线验证。您只需要公共密钥并验证签名(以确保内容为“有效”)。

为什么不使用官方的keycloak-connect nodejs库?该库更专注于作为中间件的Express框架,并且(据我所知)确实没有直接公开任何验证功能。或者,您可以使用任何JWT / OICD库,因为验证是标准化过程。