Как мне перенаправить обратно на первоначально запрошенный URL-адрес после аутентификации с помощью паспорт-saml?
Извините, если это вопрос о головной стороне, но у меня возникли проблемы с пониманием того, как я могу перенаправить клиентский браузер обратно на любой URL-адрес, который был первоначально запрошен после успешной проверки подлинности с нашим поставщиком идентификации SAML (IdP). Я использую последние версии паспорт-saml, паспорт и экспресс.
Например, скажем, что клиент изначально запросил /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) {
...
});
Этот вызов перенаправит браузер на мою страницу входа в систему 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?...');
});
Пример в паспорт-saml readme просто показывает жестко закодированное перенаправление на корневой ресурс, но я бы хотел перенаправить обратно в первоначально запрошенную URL (/foo/bar
).
Могу ли я отправить URL-адрес или какое-либо другое значение в IdP, который будет возвращаться в ответ на ответ SAML? И если да, то как я могу получить доступ к нему в моем маршруте /login/callback
?
Или есть ли какой-то лучший способ выразить/паспорт, чтобы я этого не делал?
Любая помощь, которую вы можете предоставить, будет оценена по достоинству!
Ответы
Ответ 1
Могу ли я отправить URL-адрес или другое значение в IdP, который получит с округлением и возвратом в ответ SAML? И если да, то как можно Я обращаюсь к нему в моем/логином/обратном вызове?
Для округления значения через IdP вам нужно использовать RelayState
. Это значение, которое вы можете отправить в IdP, и если вы это сделаете, они обязаны отправить его обратно без изменений.
Вот что спецификации SAML должно сказать:
3.1.1 Использование RelayState
Некоторые привязки определяют механизм RelayState для сохранения и передачу информации о состоянии. Когда такой механизм используется в передачу сообщения запроса в качестве начального шага протокола SAML, оно устанавливает требования к выбору и использованию привязки впоследствии используется для передачи ответа. А именно, если запрос SAML сообщение сопровождается данными RelayState, затем ответчик SAML ДОЛЖЕН вернуть ответ протокола SAML, используя привязку, которая также поддерживает механизм RelayState, и он ДОЛЖЕН разместить точное Данные RelayState, полученные с запросом, в соответствующий Параметр RelayState в ответе.
Чтобы использовать это с паспорт-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
Приведенный выше код представляет собой реализацию multi-tenant saml, поэтому я отправляю свой tenant.key
в качестве параметра RelayState
. Затем я извлекаю это значение из тела POSTed return из IdP и использую его для восстановления всего состояния, в котором я нуждаюсь.
getTenantKey: (req, next) ->
key = req.body?.RelayState ? routes.match(req.path).params.tenentKey
next null, key
Ваше дело может быть проще. Вероятно, вы захотите сохранить URL-адрес конечного адресата в кеше, ограниченном временем, а затем отправить ключ кэша в качестве параметра RelayState
.
Для чего вы можете избежать использования RelayState
вообще, если вы просто используете исходный идентификатор запроса SAML в качестве ключа кеша. Это значение всегда отправляется вам через поле InResponseTo
.
Ответ 2
Если вы хотите указать RelayState для каждого запроса, вы могли бы подумать, что можете сделать:
passport.authenticate('saml', { additionalParams: { RelayState: "foo" } })
Но это не работает. Если вы посмотрите на реализацию: https://github.com/bergie/passport-saml/blob/6d1215bf96e9e352c25e92d282cba513ed8e876c/lib/passport-saml/saml.js#L326, вы увидите, что дополненияParams взяты из исходных параметров конфигурации ИЛИ из объекта req ( но не из параметров для запроса).
Но, к счастью, стратегия Passport SAML делает этот запрос:
var RelayState = req.query && req.query.RelayState || req.body && req.body.RelayState;
Затем он просматривает параметры конфигурации для дополнительных (глобальных) дополнительных параметров.
Итак, если вы хотите указать RelayParams для каждого запроса, вам нужно загрузить их в req до вызова авторизации. В моем случае я делаю что-то подобное внутри обработчика маршрута:
req.query.RelayState = req.params.redirect_to;
passport.authenticate('saml')(req, res, next);
Затем, когда перенаправленный запрос возвращается из SAML IdP, вы должны иметь возможность использовать req.body.RelayParams для доступа к вашему состоянию для каждого запроса.
Обратите внимание, что если ваше значение RelayParams является объектом, вам может потребоваться использовать JSON.stringify для его кодирования в RelayParams и JSON.parse для его декодирования (я должен был сделать это в моем случае).