在使用passport-saml进行身份验证后,如何重定向回原始请求的URL?

Dav*_*rns 20 node.js express saml-2.0 passport.js

很抱歉,如果这是一个傻瓜问题,但我在理解如何将客户端浏览器重定向回到我们的SAML身份提供商(IdP)成功验证后最初请求的任何URL时遇到一些麻烦.我正在使用最新版本的护照 - 护照,护照和快递.

例如,假设客户端最初/foo/bar从另一个未受保护的页面上的链接请求,但由于这是受保护的资源,我会使用重定向进行响应/login,这是我调用的地方passport.authenticate('saml').

app.get('/login', passport.authenticate('saml'));

function ensureAuth(req, res, next) {
    if (req.user.isAuthenticated()) {return next();}
    else {res.redirect('/login');}
}

app.get('/foo/bar', ensureAuth, function(req, res) {
    ...
});
Run Code Online (Sandbox Code Playgroud)

该调用将浏览器重定向到我的IdP的登录页面,并且在成功验证后,IdP POST返回到我的/login/callback路由.在那条路线中,我再次passport.authenticate(saml)用来验证响应SAML,如果一切都好,我就可以将浏览器重定向回所请求的资源......但是我怎么知道请求的资源是什么?因为它是一个POST回调,我丢失了与原始请求相关的任何状态.

app.post('/login/callback', passport.authenticate('saml'), function(req, res) {
    res.redirect('...can I know which url to redirect back to?...');
});
Run Code Online (Sandbox Code Playgroud)

passport-saml自述文件中的示例只显示了一个硬编码重定向回到根资源,但我想重定向回原始请求的URL(/foo/bar).

我可以向IdP发送一个url或其他值,这些ID将在SAML响应中进行往返并回发吗?如果是这样,我如何在我的/login/callback路线中访问它?

还是有一些更好的,快递/护照方式来做到这一点,我错过了?

您将提供的任何帮助将非常感谢!

bio*_*tal 21

我可以向IdP发送一个url或其他值,这些ID将在SAML响应中进行往返并回发吗?如果是这样,我如何在我的/ login/callback路由中访问它?

要通过IdP往返一个值,您需要使用RelayState.这是您可以发送给IdP的值,如果您这样做,他们有义务将其发送回去而不做任何更改.

以下是SAML规范所说的内容:

3.1.1 RelayState的使用

一些绑定定义了用于保存和传递状态信息的"RelayState"机制.当这种机制用于传送请求消息作为SAML协议的初始步骤时,它对随后用于传达响应的绑定的选择和使用提出了要求.也就是说,如果SAML请求消息伴随着RelayState数据,那么SAML响应者必须使用也支持RelayState机制的绑定返回其SAML协议响应,并且它必须将随接收到的请求的确切RelayState数据放入相应的RelayState中.响应中的参数.

要使用passport-saml,必须将其添加为additionalParams值.下面的代码显示了这种情况.

saml = new SamlStrategy
    path: appConfig.passport.saml.path
    decryptionPvk: fs.readFileSync(appConfig.passport.saml.privateKeyFile)
    issuer: appConfig.passport.saml.issuer
    identifierFormat: tenant.strategy.identifierFormat
    entryPoint: tenant.strategy.entryPoint
    additionalParams:{'RelayState':tenant.key}
    ,
    (profile, next) -> 
        # get the user from the profile
Run Code Online (Sandbox Code Playgroud)

上面的代码来自多租户saml实现,所以我发送我tenant.key作为RelayState参数.然后,我从IdP的POSTed返回体中检索此值,并使用它重新建立我需要的所有状态.

getTenantKey: (req, next) ->
    key = req.body?.RelayState ? routes.match(req.path).params.tenentKey
    next null, key
Run Code Online (Sandbox Code Playgroud)

你的情况可能会更简单.您可能希望将最终目标URL存储在时间限制的缓存中,然后将缓存键作为RelayState参数发送.

对于它的价值,RelayState如果您只使用原始SAML请求ID作为缓存密钥,则可以完全避免使用.该值始终通过InResponseTo字段发回给您.

  • @MForMarlon经过一些战斗,我放弃了接力状态,只使用了会话.基本上我在用户尝试登录时保存会话中的位置,然后在他们回来时访问同一会话.我不喜欢它,但这是我看到Java所做的. (3认同)
  • 策略创建后是否可以设置RelayState?在我的服务器启动的早期,我有"passport.use(新的SamlStrategy("就像你上面一样.在运行期间我有"passport.authenticate("......那么可以提供RelayState吗?我会在运行时应该返回什么. (2认同)

Bob*_*son 9

如果要在每个请求的基础上指定RelayState,则认为可以这样做:

passport.authenticate('saml', { additionalParams: { RelayState: "foo" } })
Run Code Online (Sandbox Code Playgroud)

但这不起作用。如果您查看实现:https : //github.com/bergie/passport-saml/blob/6d1215bf96e9e352c25e92d282cba513ed8e876c/lib/passport-saml/saml.js#L326,您会看到从初始配置选项中选择了additionalParams或来自req对象(但不是来自每个请求选项)。

但幸运的是,Passport SAML策略会按要求执行以下操作:

var RelayState = req.query && req.query.RelayState || req.body && req.body.RelayState;
Run Code Online (Sandbox Code Playgroud)

然后,它在配置选项中查找更多(全局)additionalParams。

因此,如果要基于每个请求指定RelayParam,则必须在调用authorize之前将它们加载到req中。在我来说,我做这样的事情里面的路由处理的:

req.query.RelayState = req.params.redirect_to;
passport.authenticate('saml')(req, res, next);
Run Code Online (Sandbox Code Playgroud)

然后,当重定向的请求从SAML IdP返回时,您应该能够使用req.body.RelayParams访问每个请求状态。

请注意,如果您的RelayParams值是一个对象,则可能必须使用JSON.stringify将其编码为RelayParams,并使用JSON.parse将其解码回去(在我的情况下,我必须这样做)。