web-dev-qa-db-ja.com

passport-samlで認証した後、最初に要求されたURLにリダイレクトするにはどうすればよいですか?

これが骨の折れる質問である場合は申し訳ありませんが、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_ルートでそれにアクセスできますか?

それとも、私が見逃している、これを行うためのより良いエクスプレス/パスポートの方法はありますか?

あなたが提供できるどんな助けでも大歓迎です!

23
Dave Stearns

ラウンドトリップされて応答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.keyRelayStateパラメーターとして送信しています。次に、IdPからのPOSTされたリターンの本体からこの値を取得し、それを使用して必要なすべての状態を再確立します。

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

あなたのケースはもっと単純かもしれません。最終宛先のURLを時間制限のあるキャッシュに保存してから、キャッシュキーをRelayStateパラメータとして送信することをお勧めします。

元のSAMLリクエストIDをキャッシュキーとして使用するだけであれば、RelayStateの使用を完全に回避できます。この値は常にInResponseToフィールドを介して返送されます。

22
biofractal

リクエストごとに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を使用してデコードする必要がある場合があることに注意してください(私の場合はそれを行う必要がありました)。

10
BobDickinson