MSAL4J と Azure Active Directory B2C を使用して Java Tomcat アプリのサインインを有効にする

この記事では、 Microsoft Authentication Library for Java (MSAL4J) を使用してユーザーを Azure Active Directory B2C (Azure AD B2C) に対して認証する Java Tomcat アプリケーションについて説明します。

次の図は、アプリのトポロジを示しています。

アプリのトポロジを示す図。

このアプリでは MSAL4J を使用してユーザーのサインインを行い、Azure AD B2C から ID トークンを取得します。 ID トークンにより、ユーザーが Azure AD B2C テナントに対して認証されていることが証明されます。

前提条件

推奨事項

  • Java / Jakarta Servlets に関するある程度の知識。
  • Linux/OSX ターミナルに関するある程度の知識。
  • トークンの検査に必要な jwt.ms
  • ネットワークの活動監視とトラブルシューティングに必要な Fiddler
  • 開発に関する最新の情報について、Microsoft Entra ID ブログを確認してください。

サンプルのセットアップ

次のセクションでは、サンプル アプリケーションを設定する方法を示します。

サンプル リポジトリを複製またはダウンロードする

サンプルを複製するには、Bash ウィンドウを開き、次のコマンドを使用します。

git clone https://github.com/Azure-Samples/ms-identity-msal-java-samples.git
cd 3-java-servlet-web-app/1-Authentication/sign-in-b2c

または、ms-identity-msal-java-samples リポジトリに移動し、.zip ファイルでダウンロードして、ハード ドライブに展開します。

重要

Windows でファイル パスの長さが制限を超える場合は、ハード ドライブのルート近くのディレクトリにリポジトリを複製または展開してください。

サンプル アプリケーションを Azure AD B2C テナントに登録する

このサンプルには、テストのために事前に登録されたアプリケーションが含まれています。 自分の Azure AD B2C テナントとアプリケーションを使用する場合は、次のセクションに記載された手順に従い、アプリケーションを Azure portal で登録して構成します。 そうしない場合は、「サンプルを実行する」の手順に進みます。

アプリケーションを作成するための Azure AD B2C テナントを選択する

テナントを選択するには、次の手順に従います。

  1. Azure portal にサインインします。

  2. ご利用のアカウントが複数の Azure AD B2C テナントに存在する場合は、Azure portal の画面の隅にあるプロファイルを選択し、ディレクトリの切り替えを選択して、ポータルのセッションを目的の Azure AD B2C テナントに変更します。

ユーザー フローとカスタム ポリシーの作成

サインアップ、サインイン、プロファイルの編集、パスワードのリセットなど、一般的なユーザー フローを作成するには、「チュートリアル: Azure Active Directory B2C 内にユーザー フローを作成する」を参照してください。

Azure Active Directory B2C でもカスタム ポリシーを作成することをお勧めしますが、それについては、このチュートリアルでは説明しません。

外部 ID プロバイダーを追加する

チュートリアル:Azure Active Directory B2C でアプリケーションに ID プロバイダーを追加する」を参照してください。

アプリを登録する (ms-identity-b2c-java-servlet-webapp-authentication)

アプリを登録するには、次の手順に従います。

  1. Azure portal に移動して、Azure AD B2C を選びます。

  2. ナビゲーション ペインでアプリの登録を選択し、新しい登録を選択します。

  3. 表示される アプリケーションの登録ページで、アプリケーションの登録情報を入力します。

    • 名前セクションに、アプリのユーザーに表示されるわかりやすいアプリケーション名を入力します (例: ms-identity-b2c-java-servlet-webapp-authentication)。
    • [サポートされているアカウントの種類] で、[任意の組織のディレクトリ内のアカウントと、個人用の Microsoft アカウント (Skype、Xbox、Outlook.com など)] を選択します。
    • [リダイレクト URI (省略可能)] セクションで、コンボボックスの [Web] を選択し、リダイレクト URI 「http://localhost:8080/ms-identity-b2c-java-servlet-webapp-authentication/auth_redirect」を入力します。
  4. [登録] を選択して、アプリケーションを作成します。

  5. アプリの登録ページで、アプリケーション (クライアント) ID の値を見つけてメモします。 この値は、後ほどアプリの構成ファイルで使用します。

  6. [保存] を選択して変更を保存します。

  7. アプリの登録ページで、ナビゲーション ペインにある 証明書とシークレットを選択してページを開き、シークレットの生成と証明書のアップロードを行います。

  8. [クライアント シークレット] セクションで、 [新しいクライアント シークレット] を選択します。

  9. キーの説明 (例: アプリのシークレット) を入力します。

  10. 1 年2 年無期限のいずれかの期間を選びます。

  11. [追加] を選択します。 生成された値が表示されます。

  12. 生成した値をコピーしてから保存します。 この値は後ほど、コードの構成ファイルに使用します。 この値は二度と表示されず、他の方法でも取得はできません。 そのため、必ず Azure portal から保存した後に、他の画面やペインに移動してください。

アプリの登録を使用するようにアプリ (ms-identity-b2c-java-servlet-webapp-authentication) を構成する

アプリを構成するには、次の手順に従います。

Note

以降の手順では、ClientIDApplication ID または AppId と同じです。

  1. IDE でプロジェクトを開きます。

  2. ./src/main/resources/authentication.properties ファイルを開きます。

  3. aad.clientId プロパティを見つけて、既存の値をアプリケーション ID または Azure portal からコピーした ms-identity-b2c-java-servlet-webapp-authentication アプリケーションの clientId に変更します。

  4. aad.secret プロパティを見つけて、Azure portal の ms-identity-b2c-java-servlet-webapp-authentication アプリケーションの作成時に保存した値に既存の値を置き換えます。

  5. aad.scopes プロパティを見つけて、既存のアプリケーション clientId を、このセクションの手順 1 で aad.clientId に入力した値に置き換えます。

  6. aad.authority プロパティを探して、fabrikamb2c の最初のインスタンスを、Azure portal の ms-identity-b2c-java-servlet-webapp-authentication アプリケーションで作成した Azure AD B2C テナントの名前に置き換えます。

  7. aad.authority プロパティを探して、fabrikamb2c の 2 番目のインスタンスを、Azure portal の ms-identity-b2c-java-servlet-webapp-authentication アプリケーションで作成した Azure AD B2C テナントの名前に置き換えます。

  8. aad.signInPolicy プロパティを見つけて、Azure portal で ms-identity-b2c-java-servlet-webapp-authentication アプリケーションを作成した際に、Azure AD B2C テナント で作成したサインアップ/サインイン ユーザー フロー ポリシーの名前に置き換えます。

  9. aad.passwordResetPolicy プロパティを見つけて、Azure portal で ms-identity-b2c-java-servlet-webapp-authentication アプリケーションを作成した際に、Azure AD B2C テナント で作成したパスワード リセット ユーザー フロー ポリシーの名前に置き換えます。

  10. aad.editProfilePolicy プロパティを見つけて、Azure portal で ms-identity-b2c-java-servlet-webapp-authentication アプリケーションを作成した際に、Azure AD B2C テナント で作成したプロファイル編集ユーザー フロー ポリシーの名前に置き換えます。

サンプルをビルドする

Maven を使用してサンプルをビルドするには、サンプルの pom.xml ファイルが含まれているディレクトリに移動して、次のコマンドを実行します。

mvn clean package

このコマンドを実行すると、.war ファイルが作成されます。これはさまざまなアプリケーション サーバーで実行できます。

サンプルを実行する

以降のセクションでは、サンプルを Azure App Service にデプロイする方法を紹介します。

前提条件

Maven プラグインを構成する

Azure App Service に対するデプロイ プロセスでは、Azure CLI からの Azure 資格情報が自動的に使用されます。 Azure CLI がローカルにインストールされていない場合、Maven プラグインは OAuth またはデバイス ログインを使用して認証します。 詳細については、Maven プラグインによる認証に関するページを参照してください。

プラグインを構成するには、次の手順に従います。

  1. 次のコマンドを実行してデプロイを構成します。 このコマンドは、Azure App Service オペレーティング システム、Java バージョン、および Tomcat バージョンを設定するのに役立ちます。

    mvn com.microsoft.azure:azure-webapp-maven-plugin:2.12.0:config
    
  2. 新しい実行構成を作成するで、Y を押して、Enter を押します。

  3. Windows の場合は、OS の値を定義します1 を押し、Linux の場合は 2 を押してから、Enter を押します。

  4. Java 11 の場合は javaVersion の値を定義します2 を押し、Enter を押します。

  5. Tomcat 9.0 の場合は、webContainer の値を定義します4 を押し、Enter を押します。

  6. pricingTier の値を定義しますEnter を押し、既定値である P1v2 層を選択します。

  7. 確認Y を押し、Enter を押します。

デプロイ プロセスの出力例を次に示します。

Please confirm webapp properties
AppName : msal4j-servlet-auth-1707209552268
ResourceGroup : msal4j-servlet-auth-1707209552268-rg
Region : centralus
PricingTier : P1v2
OS : Linux
Java Version: Java 11
Web server stack: Tomcat 9.0
Deploy to slot : false
Confirm (Y/N) [Y]: [INFO] Saving configuration to pom.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  37.112 s
[INFO] Finished at: 2024-02-06T08:53:02Z
[INFO] ------------------------------------------------------------------------

選択内容の確認後、プラグインによって、アプリを Azure App Service で実行するよう構成するのに必要なプラグイン要素および設定が、プロジェクトの pom.xml ファイルに追加されます。

pom.xml ファイルの関連部分は、次の例のようになります。

<build>
    <plugins>
        <plugin>
            <groupId>com.microsoft.azure</groupId>
            <artifactId>>azure-webapp-maven-plugin</artifactId>
            <version>x.xx.x</version>
            <configuration>
                <schemaVersion>v2</schemaVersion>
                <resourceGroup>your-resourcegroup-name</resourceGroup>
                <appName>your-app-name</appName>
            ...
            </configuration>
        </plugin>
    </plugins>
</build>

pom.xml 内で App Service の構成を直接変更できます。 一般的な構成をいくつか次の表に示します。

プロパティ Required 説明
subscriptionId false サブスクリプション ID です。
resourceGroup true アプリの Azure リソース グループ。
appName true アプリの名前。
region false アプリをホストするリージョン。 既定値は centralus です。 有効なリージョンについては、「サポートされているリージョン」を参照してください。
pricingTier false アプリの価格レベル。 運用環境のワークロードでは、既定値は P1v2 です。 Java の開発とテストに推奨される最小値は B2 です。 詳細については、「App Service の価格」を参照してください。
runtime false ランタイム環境の構成。 詳細については、「構成の詳細」を参照してください。
deployment false デプロイの構成。 詳細については、「構成の詳細」を参照してください。

構成の完全な一覧については、プラグインのリファレンス ドキュメントを参照してください。 すべての Azure Maven プラグインでは、一連の構成が共通しています。 これらの構成については、「共通の構成」を参照してください。 Azure App Service に固有の構成については、「Azure アプリ: 構成の詳細」を参照してください。

後で使用するために appNameresourceGroup の値を保存しておいてください。

アプリのデプロイを準備する

アプリケーションを App Service にデプロイすると、リダイレクト URL が、デプロイされたアプリ インスタンスのリダイレクト URL に変更されます。 これらの設定をプロパティー ファイルで変更しするには、次の手順を使用します。

  1. アプリの authentication.properties ファイルに移動し、app.homePage の値をデプロイされたアプリのドメイン名に変更します (次の例を参照)。 たとえば、前の手順でアプリ名に example-domain を選択している場合は、app.homePage の値に https://example-domain.azurewebsites.net を使用する必要があります。 プロトコルを http から https に変更することも必要です。

    # app.homePage is by default set to dev server address and app context path on the server
    # for apps deployed to azure, use https://your-sub-domain.azurewebsites.net
    app.homePage=https://<your-app-name>.azurewebsites.net
    
  2. このファイルを保存した後、次のコマンドを使用してアプリをリビルドします。

    mvn clean package
    

重要

この同じ authentication.properties ファイルに、aad.secret の設定があります。 この値を App Service にデプロイすることはお勧めしません。 また、この値をコードに残しておくことや、それを git リポジトリにプッシュすることは望ましくありません。 このシークレット値をコードから削除する方法の詳細な説明については、「App Service へのデプロイ - シークレットの削除」セクションを参照してください。 ここにはシークレット値を Key Vault にプッシュするための手順や、Key Vault 参照を使用するための手順が説明されています。

Microsoft Entra IDアプリの登録を更新する

リダイレクト URI は Azure App Service にデプロイされたアプリに変更されるため、Microsoft Entra ID アプリの登録でも、リダイレクト URI を変更する必要があります。 次の手順に従って、この変更を行います。

  1. 開発者用の Microsoft ID プラットフォームの [アプリの登録] ページに移動します。

  2. 検索ボックスを使用してアプリの登録を検索します (例: java-servlet-webapp-authentication)。

  3. 名前を選択して、アプリの登録を開きます。

  4. コマンドメニューから 認証 を選択します。

  5. Web - リダイレクト URI セクションで、URI の追加を選択します。

  6. アプリの URI を、/auth/redirect を追加して入力します。たとえば https://<your-app-name>.azurewebsites.net/auth/redirect のようになります。

  7. [保存] を選択します。

アプリケーションのデプロイ

以上で、アプリを Azure App Service にデプロイする準備ができました。 次のコマンドを使用して、デプロイを実行するために Azure 環境にサインインしていることを確認します。

az login

pom.xml ファイルにすべての構成が準備されています。これで、次のコマンドを使用して Java アプリを Azure にデプロイできます。

mvn package azure-webapp:deploy

デプロイが完了すると、アプリケーションを http://<your-app-name>.azurewebsites.net/ で起動できます。 ローカル Web ブラウザーで URL を開くと、msal4j-servlet-auth アプリケーションのスタート ページが表示されます。

サンプルの確認

次の手順に従ってサンプルを操作します。

  1. サインインまたはサインアウトの状態が、画面の中央に表示されます。
  2. 画面の隅にある状況依存ボタンを選択します。 このボタンは、アプリを最初に実行するときにサインインと表示します。
  3. 次のページに移動し、指示に従って、選択した ID プロバイダーのアカウントでサインインします。
  4. 状況依存ボタンの表示がサインアウトに変わり、ユーザー名が表示されます。
  5. ID トークンの詳細を選択すると、ID トークンのデコードされた要求の一部が表示されます。
  6. プロファイルを編集することもできます。 リンクを選択して、表示名、居住地、職業などの詳細を編集します。
  7. 画面隅のボタンを使用してサインアウトします。
  8. サインアウトした後、トークンの詳細ページの次の URL に移動します: http://localhost:8080/ms-identity-b2c-java-servlet-webapp-authentication/auth_token_details。 ここで、アプリが ID トークン要求ではなく 401: unauthorized エラーを表示することを確認できます。

コードについて

このサンプルでは、MSAL4J を使用して Azure AD B2C テナントへのユーザーのサインインを行う方法を示します。

Contents

次の表に、サンプル プロジェクト フォルダーの内容を示します。

ファイル/フォルダー 説明
AuthHelper.java 認証に使用するヘルパー関数。
Config.java 起動時に実行され、プロパティ リーダーとロガーを構成します。
authentication.properties Microsoft Entra ID とプログラム構成。
AuthenticationFilter.java 認証されていない要求を、保護されたリソースの 401 ページにリダイレクトします。
MsalAuthSession HttpSession を使用してインスタンス化されます。 すべての MSAL 関連セッション属性をセッション属性に格納します。
____Servlet.java 使用可能なすべてのエンドポイントは、末尾が ____Servlet.java である .java クラスに定義されます。
CHANGELOG.md サンプルに対する変更の一覧。
CONTRIBUTING.md サンプルに貢献するためのガイドライン。
ライセンス サンプルのライセンス。

ConfidentialClientApplication

ConfidentialClientApplication インスタンスが AuthHelper.java ファイルに作成されます (次の例を参照)。 このオブジェクトにより Azure AD B2C 認証 URL の作成、および認証トークンとアクセス トークンの交換が行われます。

IClientSecret secret = ClientCredentialFactory.createFromSecret(SECRET);
confClientInstance = ConfidentialClientApplication
                     .builder(CLIENT_ID, secret)
                     .b2cAuthority(AUTHORITY + policy)
                     .build();

インスタンス化には次のパラメーターが使用されます。

  • アプリのクライアント ID。
  • クライアント シークレット。機密クライアント アプリケーションに必要です。
  • Azure AD B2C の認証機関が UserFlowPolicy と連結され、サインアップ、サインイン、プロファイルの編集、またはパスワード リセットが行われます。

上記の例では、Config.java ファイルにあるプロパティ リーダ0を使用して、値が authentication.properties ファイルから読み取られます。

ステップバイステップのチュートリアル

次の手順で、アプリの機能をチュートリアルで紹介します。

  1. サインイン プロセスの最初のステップでは、Azure Active Directory B2C テナントの /authorize エンドポイントに要求を送信します。 MSAL4J ConfidentialClientApplication インスタンスを使用して承認要求 URL を作成すると、アプリがブラウザーをこの URL にリダイレクトします (次の例を参照)。

    final ConfidentialClientApplication client = getConfidentialClientInstance(policy);
    final AuthorizationRequestUrlParameters parameters = AuthorizationRequestUrlParameters
        .builder(REDIRECT_URI, Collections.singleton(SCOPES)).responseMode(ResponseMode.QUERY)
        .prompt(Prompt.SELECT_ACCOUNT).state(state).nonce(nonce).build();
    
    final String redirectUrl = client.getAuthorizationRequestUrl(parameters).toString();
    Config.logger.log(Level.INFO, "Redirecting user to {0}", redirectUrl);
    resp.setStatus(302);
    resp.sendRedirect(redirectUrl);
    

    このコードの機能を次の一覧で示します。

    • AuthorizationRequestUrlParameters: AuthorizationRequestUrl を作成するために設定する必要があるパラメーター。

    • REDIRECT_URI: Azure AD B2C がユーザー資格情報を収集した後、認証コードと共にブラウザーをリダイレクトする場所。

    • SCOPES: スコープはアプリケーションで要求されるアクセス許可です。

      通常、ID トークンの応答を受信するには、openid profile offline_access の 3 つのスコープがあれば十分です。 ただし、MSAL4J では、Azure AD B2C からのすべての応答にもアクセス トークンが含まれている必要があります。

      Azure AD B2C でアクセス トークンと ID トークンを分配するには、要求に追加のリソース スコープを含める必要があります。 このアプリは実際には外部リソース スコープを必要としないため、アクセス トークンを受け取るために、自分のクライアント ID が 4 番目のスコープとして追加されます。

      authentication.properties ファイルに、アプリで要求されるすべてのスコープの一覧が入力されています。

    • ResponseMode.QUERY: Azure AD B2C は、HTTP POST 要求のフォーム パラメーターとして、または HTTP GET 要求のクエリ文字列パラメーターとして応答を返すことができます。

    • Prompt.SELECT_ACCOUNT: Azure AD B2C は、認証対象のアカウントを選択するようユーザーに求める必要があります。

    • state: アプリで各トークン要求のセッションに設定され、対応する Azure AD B2C リダイレクト コールバックを受信した後に破棄される一意の変数。 状態変数を使用すると、Azure AD B2C 承認要求から /auth_redirect endpoint への Azure AD B2C 要求がこのアプリとこのセッションから確実に送信され、CSRF 攻撃が防止されます。 これは、AADRedirectServlet.java ファイルで行われます。

    • nonce: アプリによって各トークン要求のセッションに設定され、対応するトークンを受信した後に破棄される一意の変数。 この nonce は、Azure AD B2C に分配された結果のトークンに変換され、トークンリプレイ攻撃の発生を防ぎます。

  2. Azure Active Directory B2C により、ユーザーにサインイン プロンプトが表示されます。 サインインが成功すると、ユーザーのブラウザーはアプリのリダイレクト エンドポイントにリダイレクトされます。 このエンドポイントへの有効な要求には、、認証コードが含まれています。

  3. その後、ConfidentialClientApplication インスタンスは、この承認コードを Azure Active Directory B2C の ID トークンとアクセス トークンとで交換します (次の例を参照)。

    final AuthorizationCodeParameters authParams = AuthorizationCodeParameters
                        .builder(authCode, new URI(REDIRECT_URI))
                        .scopes(Collections.singleton(SCOPES)).build();
    
    final ConfidentialClientApplication client = AuthHelper
            .getConfidentialClientInstance(policy);
    final Future<IAuthenticationResult> future = client.acquireToken(authParams);
    final IAuthenticationResult result = future.get();
    

    このコードの機能を次の一覧で示します。

    • AuthorizationCodeParameters: ID トークンやアクセス トークンと承認コードを交換するために設定する必要があるパラメーター。
    • authCode: リダイレクト エンドポイントで受信された承認コード。
    • REDIRECT_URI: 前のステップで使用したリダイレクト URI を、再度渡す必要があります。
    • SCOPES: 前のステップで使用したスコープを、再度渡す必要があります。
  4. acquireToken が成功した場合、トークン要求が抽出され、セッションに格納されている nonce に対して nonce 要求が検証されます (次の例を参照)。

    parseJWTClaimsSetAndStoreResultInSession(msalAuth, result, serializedTokenCache);
    validateNonce(msalAuth)
    processSuccessfulAuthentication(msalAuth);
    
  5. nonce が正常に検証されると、MsalAuthSession クラスによって公開されるメソッドを利用して、認証状態がサーバー側セッションに入ります (次の例を参照)。

    msalAuth.setAuthenticated(true);
    msalAuth.setUsername(msalAuth.getIdTokenClaims().get("name"));
    

詳細

このシナリオおよびその他のシナリオでの OAuth 2.0 プロトコルの動作の詳細については、「Microsoft Entra ID の認証シナリオ」を参照してください。