先日よりAmazon Cognitoを使ってSpring Bootアプリに対して認証機能を実装しています。
今回はログアウト機能の実装中に少しハマったことがあったのでそのことについて記載します。
陥った現象
今回陥った現象は、正しく実装したログアウト処理を試そうとすると「Invalid Request」というメッセージが表示されるというものです。画面としては以下の画面が表示されます。

新しいAmazon CognitoではQuick Startとしてそれぞれの言語ごとに実装例が表示されるので、それを真似たらできるのですが今回私の場合では動かず。何度もコピペミスをしていないか確認しましたがどうも問題なさそうです。
もう少し深掘りしてみると、以下の状況になっていました。
- ログアウト自身はうまくいっている感じ
- 再度ログインしようとすると認証情報の入力が求められるため
- 上記の画面のURLはAmazon Cognitoが指定しているURL +
/logoutではなく、/loginになっている
このことから、ログアウト自身はうまくいっているがその後の移動処理でなんらかの不具合が発生しているように見えます。
原因
Amazon Cognitoの設定項目である「許可されているサインアウトURL」にパラメータlogout_uriとして指定しているURIに完全一致していないのが原因です。Amazon Cognitoの画面は以下のもの。

AWSの以下のドキュメントを見て気がつきました。
この中で、以下の記述があります。
logout_uri パラメータを使用して、ユーザーをカスタムサインアウトページにリダイレクトします。その値を、サインアウトURL後にユーザーをリダイレクトするアプリケーションクライアントサインアウトに設定します。
ずっとパラメータの名前や値を確認していて、Amazon Cognito自身の設定ができていなかったのは個人的には盲点でした。
注意点
パラメータlogout_uriとAmazon Cognitoでの「許可されているサインアウトURL」は完全一致する必要があります。例えばhttp://localhost:8080とhttp://localhost:8080/は違うものと扱われ「Invalid Request」というメッセージが表示されることになります。
実装の注意点と改善策
新しいAmazon CognitoではQuick Startとしてそれぞれの言語ごとに実装例が表示されるのですが、client_idの値がソースコード上に直接書かれておりあまり褒められたものではないです。個人的にはapplication.propertiesに書かれている値を読み取る形としたいです。具体的な実装としては以下の通り。
package com.github.miyohide.sb_with_cognito; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; import org.springframework.web.util.UriComponentsBuilder; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.ResourceBundle; public class CognitoLogoutHandler extends SimpleUrlLogoutSuccessHandler { @Override protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { ResourceBundle resourceBundle = ResourceBundle.getBundle("application"); String domain = resourceBundle.getString("app.cognito.domain"); String logoutRedirectUrl = resourceBundle.getString("app.cognito.logout_uri"); String userPoolClientId = resourceBundle.getString("spring.security.oauth2.client.registration.cognito.client-id"); return UriComponentsBuilder .fromUri(URI.create(domain + "/logout")) .queryParam("client_id", userPoolClientId) .queryParam("logout_uri", logoutRedirectUrl) .encode(StandardCharsets.UTF_8) .build() .toUriString(); } }
最初はSpring Bootアプリなので@Valueを使おうとしたのですが、CognitoLogoutHandlerに@Componentなどを付与していないので、設定することができませんでした。そこで、ResourceBundlerを使った実装としました。