機雷がなんだ! 全速前進!

SEというかプログラマというか、日々のエンジニア生活の中で体験したことなどを中心に書き残しています。

Microsoft Entra ID (旧 Azure AD) をIdPとしてOIDC認証する際の注意点(バックエンド)

前回記事では主にMicrosoft Entra ID (旧 Azure AD)側の設定とフロントエンド(SPA)を中心にした内容でしたが、今回はバックエンド側の注意点について記載します。

本記事では、バックエンドはJava(Spring Boot 3.5.8)で実装したWebAPIを想定しています。

変更前IdP:Amazon Cognito

Amazon CognitoをIdPとして以下のようなエンドポイントを指定していました。

バックエンドの設定

以下のように設定ファイル(application.yml)でエンドポイントを指定しました。

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_xxxxxxxxx

この設定で特に問題なくOIDC認証ができていました。

変更後IdP:Microsoft Entra ID (旧 Azure AD)

前提

  • Microsoft Entra ID (旧 Azure AD)側でIdP設定済
  • フロントエンドはReactで実装したSPAを想定

今回前提とする設定内容は前回記事の【最終版】と同様です。(詳細はそちらをご覧ください)

問題

Microsoft Entra ID (旧 Azure AD)をIdPに変更したところ、フロントエンドからバックエンドAPI呼び出しで以下の401エラーが発生してしまいました。

Www-Authenticate:

Bearer error="invalid_token", 
error_description="The aud claim is not valid", 
error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"

これは、Spring SecurityのOAuth2リソースサーバが、受信したJWTトークン(アクセストークン)のaudクレームを検証した際、想定する値と一致しないため認証が失敗たことを示しています。

手順①:バックエンドの設定

以下のように設定ファイル(application.yml)でエンドポイントを指定していました。

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://login.microsoftonline.com/4647xxxx-xxxx-xxxx-xxxx-xxxxxx394ecb/v2.0
          audiences:
            - api://4f82xxxx-xxxx-xxxx-xxxx-xxxxxx5fa07b

手順②:実行結果

アクセストークンを確認してみるとiss(issuer)は一致しているようです。(以下✅のところ) しかし、aud(audience)が一完全には致していないことが分かりました。(以下❌のところ)

{
  "aud": "4f82xxxx-xxxx-xxxx-xxxx-xxxxxx5fa07b", ← ❌ 完全一致してない(設定ファイルには「api://」がある)
  "iss": "https://login.microsoftonline.com/4647xxxx-xxxx-xxxx-xxxx-xxxxxx394ecb/v2.0", ← ✅ 完全一致している
:
(以下略)

解決方法

前述の設定ファイルとアクセストークンの差異を修正します。

手順①:バックエンドの設定

以下のように設定ファイル(application.yml)でエンドポイントを修正しました。

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://login.microsoftonline.com/4647xxxx-xxxx-xxxx-xxxx-xxxxxx394ecb/v2.0
          audiences:
            - 4f82xxxx-xxxx-xxxx-xxxx-xxxxxx5fa07b

手順②:実行結果

アクセストークンを確認してみるとiss(issuer)とaud(audience)が一致していることが確認できました。(以下✅のところ)

{
  "aud": "4f82xxxx-xxxx-xxxx-xxxx-xxxxxx5fa07b", ← ✅ 完全一致している
  "iss": "https://login.microsoftonline.com/4647xxxx-xxxx-xxxx-xxxx-xxxxxx394ecb/v2.0", ← ✅ 完全一致している
:
(以下略)

今回のポイント

前回記事ではフロントエンドの設定でaud(audience)を指定する際、以下のように「api://」というプレフィックスを記述していました。

VITE_OIDC_SCOPE=openid profile email api://4f82xxxx-xxxx-xxxx-xxxx-xxxxxx5fa07b/.default

しかしバックエンドの設定ファイル(application.yml)では

          audiences:
            - api://4f82xxxx-xxxx-xxxx-xxxx-xxxxxx5fa07b ← ❌ 「api://」は不要

ではなく、以下のようにプレフィックスなしで記述する必要があるので注意が必要です。

          audiences:
            - 4f82xxxx-xxxx-xxxx-xxxx-xxxxxx5fa07b ← ✅ 「api://」なしが正解

また、aud(audience)は複数形の「audiences」でありリスト型式で記述します。(以下参照)

          audiences:
            - my-api-audience
            - another-allowed-audience

アクセストークンに含まれる'aud'クレームがこのリストのいずれかと一致するか検証されるという仕組みになっているようです。

参考