今回はTAI(Trust Association Interceptor)のカスタムプロパティを使って、SP-redirectedのSAML SSOシナリオを実践していきます。
TAIの設定追加
まずは、認証なしでサーブレットにアクセスしてきたときに、IdP-initiated SSOが発動するようにTAIのプロパティを追加します。
WASの管理コンソールから、「セキュリティー」ー「グローバルセキュリティー」ー「トラスト・アソシエーション」に入ります。
「インターセプター」に入ります。
TAIのカスタムプロパティに入ります。
認証エラー時にリダイレクトさせるURLの設定は、"sso_1.sp.login.error.page"
です。値にIdP-initiatedのときに使用したURL("https://www.alto.net/openam/idpssoinit?metaAlias=%2Fidp&spEntityID=https%3A%2F%2Fwww.docker.mognet.net%2Fsamlsps%2Facs&binding=urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Abindings%3AHTTP-POST&NameIDFormat=urn:oasis:names:tc:SAML:2.0:nameid-format:transient&RelayState=https%3A%2F%2Fwww.docker.mognet.net%2Fsnoop"
)を入れて「OK」します。
サーバを再起動します。
SP-redirected SSO確認
Firefoxを立ち上げ、"https://www.docker.mognet.net/snoop"
に直接アクセスします。"SAML tracer"
でキャプチャしておきます。
はい、無事にIdP-initiatedと同じフローになりました。SAML tracer
でキャプチャした内容を見てみます。
GET https://www.docker.mognet.net/snoop HTTP/1.1
Host: www.docker.mognet.net
HTTP/1.1 302 Found
Location: https://www.alto.net/openam/idpssoinit?metaAlias=%2Fidp&spEntityID=https%3A%2F%2Fwww.docker.mognet.net%2Fsamlsps%2Facs&binding=urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Abindings%3AHTTP-POST&NameIDFormat=urn:oasis:names:tc:SAML:2.0:nameid-format:transient&RelayState=https%3A%2F%2Fwww.docker.mognet.net%2Fsnoop
レスポンスコード302
とプロパティに設定したURLがLocation
として返されていることがわかります。
実験(ACSに直アクセスしてみる)
今度はビジネスアプリケーション(snoop
)(笑)ではなく、ACSに直接アクセスしてみます。認証されていないという点では同じなので、同様にIdP-initiated SSOが発動されるはずです。
Firefoxを立ち上げ、"https://www.docker.mognet.net/samlsps/acs"
に直接アクセスします。
結果、画面遷移は同じでしたが、トレース内容を見ると興味深い差異がありました。
ACSに対して2回SAMLアサーションをPOSTしています。1回目のレスポンスは未認証時のレスポンスと同じ"302 Found"
です。SAMLアサーションをTAIが拒否したようです。OpenAM
は、ログイン済みユーザがログインページにアクセスしてきたらそのまま次の画面へ自動遷移させるので、この辺の動きはユーザには見えません。
ログ追跡
トラブルシュートを有効にしてトレース情報を追ってみます1。
トレースで役立ちそうなキーワードは"ACSTrustAssoc"
と"TAIWrapper"
あたりになりそうです。
> createTAIErrorResult(req[com.ibm.ws.webcontainer.srt.SRTServletRequest], res[com.ibm.ws.webcontainer.srt.SRTServletResponse], msg[CWSML7033E: The Security Assertion Markup Language (SAML) Web single sign-on TAI is unable to perform a redirect to the requested target URL [https://www.docker.mognet.net/samlsps/acs].], before[true]) Entry
〜省略〜
3 Resolved error page [https://www.alto.net/openam/idpssoinit?metaAlias=%2Fidp&spEntityID=https%3A%2F%2Fwww.docker.mognet.net%2Fsamlsps%2Facs&binding=urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Abindings%3AHTTP-POST&NameIDFormat=urn:oasis:names:tc:SAML:2.0:nameid-format:transient&RelayState=https%3A%2F%2Fwww.docker.mognet.net%2Fsnoop]
3 Sending redirect
< createTAIErrorResult Exit
3 SAMLResponse could not be verified. Auto Re-login.
< invokeTAIbeforeSSO:null Exit
< negotiateValidateandEstablishTrust returns [not null] Exit
< negotiateAndValidateEstablishedTrust(): status code = 403 Exit
あ、ちゃんとエラーコード出てますね。。。30分くらいトレース追いかけてたアホは私です。
[1/27/18 8:44:35:154 JST] 000000bf ACSTrustAssoc E CWSML7033E: The Security Assertion Markup Language (SAML) Web single sign-on TAI is unable to perform a redirect to the requested target URL [https://www.docker.mognet.net/samlsps/acs].
[1/27/18 8:44:35:156 JST] 000000bf ACSTrustAssoc E CWSML7030E: The redirect target URL, [https://www.docker.mognet.net/samlsps/acs], matches the value for the assertion consumer service (ACS) URL configured for this service provider. You cannot redirect to the ACS URL. The ACS URL is configured on the [sso_<id>.sp.acsUrl] TAI custom property. CWSML7034I: The redirect target URL was retrieved from the WasSamlSpReqUrl cookie on the request.
SAML tracer
によるトレースから以下のことがわかります。
- 未認証のユーザがACSにアクセスしてきたら、TAIのカスタムプロパティ
"sso_1.sp.login.error.page"
に指定したURLをレスポンスヘッダLocation
にセットして、HTTP 302
レスポンスを返す - さらにこのときに特殊な
Cookie
を返す(WasSamlSpReqURL
) - この
WasSamlSpReqURL
の値はオリジナルのリクエストURL(このケースではhttps://www.docker.mognet.net/samlsps/acs
) - IdPで認証してACS URLへアクセスした際にクライアントは
WasSamlSpReqURL
クッキーをリクエストヘッダに付与する
また、SystemOut.log
から以下のことがわかります。
- リダイレクトターゲットのURLは
WasSamlSpReqUrl
クッキーの値で決まる - リダイレクトターゲットのURLはACSのURLと同じではダメ
1回目のSAMLアサーションのPOSTでエラー(302
)になったのはこういうわけだったようです。この時のレスポンスでは当該のクッキーを空にして再度リダイレクトレスポンスを返すので、2回目のPOST時にはWasSamlSpReqUrl
クッキーがリクエストヘッダに付与されず、通常のIdP-initiatedのリクエストと同様に処理されたと考えられます。
事象の再整理
SAML tracer
のトレースからこの現象を時系列に再整理します。
1. ACSへ直接クライアントから"GET"リクエスト
- レスポンスコード
302
-
Location
にsso_1.sp.login.error.page
のURLをセット -
WasSamlSpReqURL
クッキーに"GET"リクエストのリクエストURLをセット
2. ユーザ認証したクライアントがACSへ"POST"リクエスト(with SAMLアサーション&WasSamlSpReqURL
クッキー)
- TAILはリダイレクト先URL(
WasSamlSpReqURL
の値)が自分自身(ACS)なのでNGと判断しレスポンスコード302
- 再度
Location
にsso_1.sp.login.error.page
のURLをセット -
WasSamlSpReqURL
クッキーをクリア
3. 2度目のACSへの"POST"リクエスト(with SAMLアサーション、NO WasSamlSpReqURL
Cookie)
- まともなクリエストなので認証OKで
302
レスポンス -
Location
にRelayState
のURLをセット -
LtpaToken2
クッキーを発行
比較のため、snoop
へ未認証アクセスした際のトレースも同様に整理しておきます。
1. snoop
へ直接クライアントから"GET"リクエスト
- レスポンスコード
302
-
Location
にsso_1.sp.login.error.page
のURLをセット -
WasSamlSpReeqURL
クッキーに"GET"リクエストのリクエストURLをセット
2. ユーザ認証したクライアントがACSへ"POST"リクエスト(with SAMLアサーション&WasSamlSpReqURL
クッキー)
- まともなクリエストなので認証OKで
302
レスポンス -
Location
にRelayState
のURLをセット -
LtpaToken2
クッキーを発行
おもいがけず深い沼にはまったので今回はこれくらいにしておきます。次回こそはJavaの話題に入りたいところです。