在使用passport-saml进行身份validation之后,如何redirect到最初请求的url?

对不起,如果这是一个笨蛋的问题,但我有一些麻烦,理解我可以redirect客户端浏览器回到我们的SAML身份提供商(IdP)成功身份validation后最初请求任何URL。 我正在使用最新版本的护照,护照和快递。

例如,假设客户端从另一个不受保护的页面上的链接请求了/foo/bar ,但由于这是一个受保护的资源,因此我将redirect到/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) { ... }); 

这个调用将把浏览器redirect到我的IdP的login页面,并且在成功的authentication之后,IdP POST回到我的/login/callback路由。 在这条路线上,我再次使用passport.authenticate(saml)来validation响应SAML,如果一切顺利的话,我将redirect浏览器回请求的资源…但我怎么知道那个请求的资源是? 因为这是一个POSTcallback,我已经失去了与原始请求相关的任何状态。

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

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

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

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

任何帮助你可以提供将非常感激!

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

要通过IdP对值进行往返,您需要使用RelayState 。 这是一个你可以发送给IdP的价值,如果你这样做,他们有义务没有改变地发回。

以下是SAML规范的内容 :

3.1.1使用RelayState

一些绑定定义了用于保存和传送状态信息的“RelayState”机制。 当这种机制被用于传递请求消息作为SAML协议的初始步骤时,它将要求select和使用随后用于传递响应的绑定。 也就是说,如果一个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 

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

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

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

如果您只是使用原始SAML请求标识作为您的caching密钥,那么可以避免使用RelayState 。 此值始终通过InResponseTo字段发回给您。

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

 passport.authenticate('saml', { additionalParams: { RelayState: "foo" } }) 

但是这不起作用。 如果你看执行: https : //github.com/bergie/passport-saml/blob/6d1215bf96e9e352c25e92d282cba513ed8e876c/lib/passport-saml/saml.js#L326你会看到,additionParams是从初始configuration选项或从req对象(而不是从每个请求选项)。

幸运的是Passport的SAML策略可以按照这个请求来做:

 var RelayState = req.query && req.query.RelayState || req.body && req.body.RelayState; 

然后它看起来更多(全局)additionalParams的configuration选项。

所以如果你想要在每个请求的基础上指定RelayParams,你必须在授权被调用之前将它们加载到req中。 在我的情况下,我在路由处理程序中做这样的事情:

 req.query.RelayState = req.params.redirect_to; passport.authenticate('saml')(req, res, next); 

然后,当redirect的请求从SAML IdP返回时,您应该可以使用req.body.RelayParams来访问您的每个请求状态。

请注意,如果您的RelayParams值是一个对象,则可能必须使用JSON.stringify将其编码到RelayParams中,然后使用JSON.parse将其解码(我必须这样做)。