how do I redirect back to the originally-requested url after authentication with passport-saml?

Can I send a url, or some other value, to the IdP that will get
round-tripped and POSTed back in the response SAML? And if so, how can
I access it in my /login/callback route?

To round-trip a value via the IdP you need to use RelayState. This is a value that you can send to the IdP and, if you do, they are obliged to send it back without alterations.

Here is the what the SAML specifications has to say:

3.1.1 Use of RelayState

Some bindings define a “RelayState” mechanism for preserving and
conveying state information. When such a mechanism is used in
conveying a request message as the initial step of a SAML protocol, it
places requirements on the selection and use of the binding
subsequently used to convey the response. Namely, if a SAML request
message is accompanied by RelayState data, then the SAML responder
MUST return its SAML protocol response using a binding that also
supports a RelayState mechanism, and it MUST place the exact
RelayState data it received with the request into the corresponding
RelayState parameter in the response.

To use this with passport-saml you must add it as an additionalParams value. The code below shows this happening.

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

The code above is from a multi-tenant saml implementation so I am sending my tenant.key as the RelayState param. I then retrieve this value from the body of POSTed return from the IdP and use it to re-establish all the state I need.

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

Your case might be simpler. You will probably want to store the final-destination url in a time limited cache and then send the cache-key as the RelayState param.

For what it is worth, you can avoid using RelayState altogether if you just use the original SAML request-id as your cache key. This value is always sent back to you via the InResponseTo field.

Leave a Comment