困ったこと
「チュートリアル:Azure Active Directory 用の Spring Boot Starter を使用して Java Web アプリをセキュリティで保護する」を見てその通りにしてみるのだけど、いざAzure App Service (Web Apps) にデプロイして動かしてみると、こんな感じで「The reply URL specified in the request does not match the reply URLs configured for the application」というエラーになる。
メッセージにある通り、これは「reply URL」の問題である。そしてそれは、Azure ADの「アプリ登録」→「認証」→「プラットフォーム構成]→「Web」にある、「リダイレクト URI」のことだ。
チュートリアルには http://localhost:8080/login/oauth2/code/
を設定せよとあるが、もちろんこれはローカルマシンでデバッグ実行するときの設定であり、App Serviceに配置する場合は適切なURIを設定しないといけない。幸い、ここには複数のURIを登録できる。
こんな感じで、http://localhost:8080
の部分を https://your-azure-web-app-name.azurewebsites.net
に置き換えたURIを追加しておけばよさそうだ。
それなのに、まだ「The reply URL specified in the request does not match the reply URLs configured for the application」というエラーが解消されない。
何が原因で、どうすればいいのか?
とりあえず、ブラウザー開発者ツールで通信をキャプチャしてみると……
最初のリダイレクト。httpsにアクセスしたはずがhttpにリダイレクトされる。これはひとまず無視する1。
GET / HTTP/1.1
Host: your-azure-web-app-name.azurewebsites.net
HTTP/1.1 302
Location: http://your-azure-web-app-name.azurewebsites.net/oauth2/authorization/azure
次のリダイレクトでAzure ADのログイン画面に飛ぶのだが、その時に渡しているURLパラメーターが……(見やすさのために空白と改行を追加)
GET /oauth2/authorization/azure HTTP/1.1
Host: your-azure-web-app-name.azurewebsites.net
HTTP/1.1 302
Location: https://login.microsoftonline.com/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/oauth2/v2.0/authorize
?response_type=code
&client_id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
&scope=openid%20profile%20https://graph.microsoft.com/User.Read%20https://graph.microsoft.com/Directory.Read.All
&state=XXXXXXXXX-XXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXX
&redirect_uri=http://your-azure-web-app-name.azurewebsites.net/login/oauth2/code/
&nonce=XX-XXXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXX
redirect_uri
が http://
になってる。
「それなら、Azure ADのリダイレクトURIをhttp://
にしたらいい」と思ったら残念。http://
で登録できるのはlocalhost
のみという仕様である。
解決方法 (おすすめ)
X-Forwarded-Proto をどっかが見ていないのが原因なのだろうな https://t.co/VVDTtMU4Jm
— Tatsuro Shibamura (@shibayan) August 13, 2021
あらためて調べたら……こんなのがあるじゃないですか。
Spring Boot 「使い方」ガイド - リファレンスドキュメント
3.12. フロントエンドプロキシサーバーの背後で実行する
プロキシが一般的に使用される
X-Forwarded-For
およびX-Forwarded-Proto
ヘッダーを追加する場合、server.forward-headers-strategy
をNATIVE
に設定するだけでサポートできます。このオプションを使用すると、Web サーバー自体がこの機能をネイティブでサポートします。特定のドキュメントを確認して、特定の動作について学ぶことができます。
早速やってみたらら、これで一発解決。
application.yml
なら
server:
forward-headers-strategy: NATIVE
application.properties
なら
server.forward-headers-strategy=NATIVE
環境変数で指定するなら(App Serviceなら「構成」→「アプリケーション設定」)
SERVER_FORWARD_HEADERS_STRATEGY=NATIVE
設定結果はこちら。最初のリダイレクトの方もきちんとHTTPSを保つようになった。
GET / HTTP/1.1
Host: your-azure-web-app-name.azurewebsites.net
HTTP/1.1 302
Location: https://your-azure-web-app-name.azurewebsites.net/oauth2/authorization/azure
GET /oauth2/authorization/azure HTTP/1.1
Host: your-azure-web-app-name.azurewebsites.net
HTTP/1.1 302
Location: https://login.microsoftonline.com/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/oauth2/v2.0/authorize
?response_type=code
&client_id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
&scope=openid%20profile%20https://graph.microsoft.com/User.Read%20https://graph.microsoft.com/Directory.Read.All
&state=XXXXXXXXX-XXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXX
&redirect_uri=https://your-azure-web-app-name.azurewebsites.net/login/oauth2/code/
&nonce=XX-XXXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXX
ちなみに、上記のドキュメントには
ヒント
Tomcat を使用していて、プロキシで SSL を終了している場合は、server.tomcat.redirect-context-root
をfalse
に設定する必要があります。これにより、リダイレクトが実行される前にX-Forwarded-Proto
ヘッダーを受け入れることができます。
ともありますが、Spring BootのデフォルトであるEmbedded Tomcatではこの設定は不要だった。
おまけ:別の方法(最初に調べたのはこちらですが、おすすめしません)
redirect-uri-template
の設定を追加して、そこに https://your-azure-web-app-name.azurewebsites.net/login/oauth2/code/
と書いてしまえばいい。
ドキュメントはこちら。
Azure AD Spring Boot Starter client library for Java | Microsoft Docs
application.yml
なら
azure:
activedirectory:
redirect-uri-template: https://your-azure-web-app-name.azurewebsites.net/login/oauth2/code/
application.properties
なら
azure.activedirectory.redirect-uri-template=https://your-azure-web-app-name.azurewebsites.net/login/oauth2/code/
だけど、たぶん環境変数で指定するほうがいいだろう。(App Serviceなら「構成」→「アプリケーション設定」)
AZURE_ACTIVEDIRECTORY_REDIRECT_URI_TEMPLATE=https://your-azure-web-app-name.azurewebsites.net/login/oauth2/code/
-
無視しないほうが解決の早道だったことが後でわかる。 ↩