これが骨の折れる質問である場合は申し訳ありませんが、SAML IDプロバイダー(IdP)での認証が成功した後、クライアントブラウザーを最初に要求されたURLにリダイレクトする方法を理解するのに問題があります。パスポートの最新バージョン(saml、passport、express)を使用しています。
たとえば、クライアントが最初に別の保護されていないページのリンクから_/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は_/login/callback
_ルートにPOSTします。そのルートでは、再び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?...');
});
_
passport-saml readme の例は、ルートリソースへのハードコードされたリダイレクトを示していますが、最初に要求されたURL(_/foo/bar
_)にリダイレクトしたいと思います。
ラウンドトリップされて応答SAMLにPOSTされるIdPにURLまたはその他の値を送信できますか?もしそうなら、どうすれば私の_/login/callback
_ルートでそれにアクセスできますか?
それとも、私が見逃している、これを行うためのより良いエクスプレス/パスポートの方法はありますか?
あなたが提供できるどんな助けでも大歓迎です!
ラウンドトリップされて応答SAMLにPOSTされるIdPにURLまたはその他の値を送信できますか?もしそうなら、どうすれば私の/ 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
上記のコードはマルチテナントsaml実装からのものであるため、tenant.key
をRelayState
パラメーターとして送信しています。次に、IdPからのPOSTされたリターンの本体からこの値を取得し、それを使用して必要なすべての状態を再確立します。
getTenantKey: (req, next) ->
key = req.body?.RelayState ? routes.match(req.path).params.tenentKey
next null, key
あなたのケースはもっと単純かもしれません。最終宛先のURLを時間制限のあるキャッシュに保存してから、キャッシュキーをRelayState
パラメータとして送信することをお勧めします。
元のSAMLリクエストIDをキャッシュキーとして使用するだけであれば、RelayState
の使用を完全に回避できます。この値は常にInResponseTo
フィールドを介して返送されます。
リクエストごとにRelayStateを指定する場合は、次のことができると思います。
passport.authenticate('saml', { additionalParams: { RelayState: "foo" } })
しかし、それは機能しません。実装を見ると: https://github.com/bergie/passport-saml/blob/6d1215bf96e9e352c25e92d282cba513ed8e876c/lib/passport-saml/saml.js#L326 additionParamsが初期設定オプションから取得OR reqオブジェクトから(ただし、リクエストごとのオプションからではありません)。
しかし幸いなことに、PassportSAML戦略はリクエストごとにこれを実行します。
var RelayState = req.query && req.query.RelayState || req.body && req.body.RelayState;
次に、構成オプションでさらに(グローバルな)additionalParamsを探します。
したがって、リクエストごとにRelayParamsを指定する場合は、authorizeが呼び出される前にそれらをreqにロードする必要があります。私の場合、ルートハンドラーのinsideのようなことをします。
req.query.RelayState = req.params.redirect_to;
passport.authenticate('saml')(req, res, next);
次に、リダイレクトされたリクエストがSAML IdPから戻ってきたら、req.body.RelayParamsを使用してリクエストごとの状態にアクセスできるはずです。
RelayParams値がオブジェクトの場合、JSON.stringifyを使用してRelayParamsにエンコードし、JSON.parseを使用してデコードする必要がある場合があることに注意してください(私の場合はそれを行う必要がありました)。