チュートリアル: 外部テナントを使用して .NET MAUI アプリでユーザーをサインインさせる

このチュートリアルは、.NET マルチプラットフォーム アプリ UI (.NET MAUI) シェルを構築する方法を示すシリーズの最後の部分であり、サインインとサインアウトのコードを追加し、そのアプリを Windows プラットフォームで実行する方法について説明します。 このシリーズのパート 2 で、.NET MAUI シェル アプリの作成、MSAL ヘルパー クラスを使用した MSAL SDK のサポートの追加、必要なライブラリのインストール、画像リソースの追加を行いました。 この最後の手順では、サインインとサインアウトのコードを .NET MAUI シェルに追加し、そのアプリを Windows プラットフォームで実行する方法を示します。

このチュートリアルでは、次の作業を行う方法について説明します。

  • サインインとサインアウト コードを追加します。
  • アプリ シェルを変更します。
  • プラットフォーム固有のコードを追加します。
  • アプリ設定を追加します。
  • .NET MAUI シェル アプリを実行してテストします。

前提条件

サインインとサインアウト コードを追加する

.NET MAUI アプリのユーザー インターフェイス (UI) は、各ターゲット プラットフォームのネイティブ コントロールにマップされるオブジェクトで構成されています。 .NET MAUI アプリの UI の作成に使用される主なコントロール グループは、ページ、レイアウト、ビューです。

メイン ビュー ページを追加する

次の手順では、main view が定義されるようにコードを組みます。

  1. プロジェクトから MainPage.xamlMainPage.xaml.cs を削除します。これらはもう必要ありません。 [ソリューション エクスプローラー] ペインで、[MainPage.xaml] のエントリを見つけて、右クリックして [削除] を選択します。

  2. [SignInMaui] プロジェクトで右クリックし、[追加]>[新しいフォルダー] を選択します。 フォルダーの名前を [Views] にします。

  3. [Views] で右クリックします。

  4. [追加]>[新しい項目...] を選択します。

  5. テンプレートの一覧で [.NET MAUI] を選択します。

  6. [.NET MAUI ContentPage (XAML)] テンプレートを選択します。 ファイルの名前を [MainView.xaml] にします。

  7. [追加] を選択します。

  8. MainView.xaml ファイルが新しいドキュメント タブで開き、ページの UI を表すすべての XAML マークアップが表示されます。 XAML マークアップを次のマークアップで置き換えます。

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="SignInMaui.Views.MainView"
                 Title="Microsoft Entra External ID"
                 >
        <Shell.BackButtonBehavior>
            <BackButtonBehavior IsVisible="False" IsEnabled="False" />
        </Shell.BackButtonBehavior>
    
        <ScrollView>
            <VerticalStackLayout 
                Spacing="25" 
                Padding="30,0" 
                VerticalOptions="Center">
    
                <Image
                    Source="external_id.png"
                    SemanticProperties.Description="External ID"
                    HeightRequest="200"
                    HorizontalOptions="Center" />
    
                <Label 
                    Text="CIAM"
                    SemanticProperties.HeadingLevel="Level1"
                    FontSize="26"
                    HorizontalOptions="Center" />
    
                <Label 
                    Text="MAUI sample"
                    SemanticProperties.HeadingLevel="Level1"
                    FontSize="26"
                    HorizontalOptions="Center" />
    
                <Button 
                    x:Name="SignInButton"
                    Text="Sign In"
                    SemanticProperties.Hint="Sign In"
                    Clicked="OnSignInClicked"
                    HorizontalOptions="Center"
                    IsEnabled="False"/>
    
            </VerticalStackLayout>
        </ScrollView>
     
    </ContentPage>
    
  9. ファイルを保存します。

    以下でページに配置された XAML コントロールの主要なパーツを理解しましょう。

    • <ContentPage> は MainView クラスのルート オブジェクトです。
    • <VerticalStackLayout> は ContentPage の子オブジェクトです。 このレイアウト コントロールは、子要素を垂直方向に順番に配置します。
    • <Image> は画像を表示します。この場合は、前にダウンロードした azureactive_directory.png_ を使用しています。
    • <Label> コントロールはテキストを表示します。
    • <Button> はユーザーが押すことができ、これによって Clicked イベントが発生します。 Clicked イベントに応じてコードを実行できます。
    • Clicked="OnSignInClicked" ボタンの Clicked イベントは OnSignInClicked イベント ハンドラーに割り当てられ、これは分離コード ファイルで定義されます。 このコードを次の手順で作成します。

OnSignInClicked イベントを処理する

次の手順は、ボタンの Clicked イベントのコードを追加することです。

  1. Visual Studio の [ソリューション エクスプローラー] ペインで、[MainView.xaml] ファイルを展開して、分離コード ファイル [MainView.xaml.cs] を表示します。 [MainView.xaml.cs] を開き、ファイルの内容を次のコードで置き換えます。

    // Copyright (c) Microsoft Corporation. All rights reserved.
    // Licensed under the MIT License.
    
    using SignInMaui.MSALClient;
    using Microsoft.Identity.Client;
    
    namespace SignInMaui.Views
    {
        public partial class MainView : ContentPage
        {
            public MainView()
            {
                InitializeComponent();
    
                IAccount cachedUserAccount = PublicClientSingleton.Instance.MSALClientHelper.FetchSignedInUserFromCache().Result;
    
                _ = Dispatcher.DispatchAsync(async () =>
                {
                    if (cachedUserAccount == null)
                    {
                        SignInButton.IsEnabled = true;
                    }
                    else
                    {
                        await Shell.Current.GoToAsync("claimsview");
                    }
                });
            }
    
            private async void OnSignInClicked(object sender, EventArgs e)
            {
                await PublicClientSingleton.Instance.AcquireTokenSilentAsync();
                await Shell.Current.GoToAsync("claimsview");
            }
            protected override bool OnBackButtonPressed() { return true; }
    
        }
    }
    

MainView クラスは、アプリのメイン ビューを表示するコンテンツ ページです。 コンストラクターでは、キャッシュされたユーザー アカウントが見つからない場合、PublicClientSingleton インスタンスからの MSALClientHelper を使用してキャッシュされたユーザー アカウントを取得しサインイン ボタンを有効にします。

サインイン ボタンがクリックされると、AcquireTokenSilentAsync メソッドを呼び出してトークンをサイレントで取得し、Shell.Current.GoToAsync メソッドを使用して claimsview ページに遷移します。 さらに、OnBackButtonPressed メソッドは true を返すようにオーバーライドされるので、このビューの戻るボタンは無効になります。

要求ビュー ページを追加する

次の手順では、ClaimsView ページが定義されるようにコードを組みます。 ページには、ID トークンで見つかったユーザーの要求が表示されます。

  1. Visual Studio の [ソリューション エクスプローラー] ペインで、[Views] を右クリックします。

  2. [追加]>[新しい項目...] を選択します。

  3. テンプレートの一覧で [.NET MAUI] を選択します。

  4. [.NET MAUI ContentPage (XAML)] テンプレートを選択します。 ファイルの名前を [ClaimsView.xaml] にします。

  5. [追加] を選択します。

  6. ClaimsView.xaml ファイルが新しいドキュメント タブで開き、ページの UI を表すすべての XAML マークアップが表示されます。 XAML マークアップを次のマークアップで置き換えます。

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="SignInMaui.Views.ClaimsView"
                 Title="ID Token View">
        <Shell.BackButtonBehavior>
            <BackButtonBehavior IsVisible="False" IsEnabled="False" />
        </Shell.BackButtonBehavior>
        <VerticalStackLayout>
            <Label 
                Text="CIAM"
                FontSize="26"
                HorizontalOptions="Center" />
            <Label 
                Text="MAUI sample"
                FontSize="26"
                Padding="0,0,0,20"
                HorizontalOptions="Center" />
    
            <Label 
                Padding="0,20,0,0"
                VerticalOptions="Center" 
                HorizontalOptions="Center"
                FontSize="18"
                Text="Claims found in ID token"
                />
            <ListView ItemsSource="{Binding IdTokenClaims}"
                      x:Name="Claims">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Grid Padding="0, 0, 0, 0">
                                <Label Grid.Column="1" 
                                       Text="{Binding}" 
                                       HorizontalOptions="Center" />
                            </Grid>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <Button
                x:Name="SignOutButton"
                Text="Sign Out"
                HorizontalOptions="Center"
                Clicked="SignOutButton_Clicked" />
        </VerticalStackLayout>
    </ContentPage>
    

    この XAML マークアップ コードは、.NET MAUI アプリの要求ビューの UI レイアウトを表します。 まず、ContentPage をタイトルで定義し、戻るボタンの動作を無効にします。

    VerticalStackLayout 内には、静的テキストを表示するいくつかの Label 要素があり、その後に Claims という名前の ListView が続き、ID トークンで見つかった要求を表示するための IdTokenClaims というコレクションにバインドされます。 各要求は、ViewCell 内で DataTemplate を使用してレンダリングされ、グリッド内で中央揃えの Label として表示されます。

    最後に、レイアウトの下部の中央に Sign Out ボタンがあり、クリックされると SignOutButton_Clicked イベント ハンドラーがトリガーされます。

ClaimsView データを処理する

次の手順は、ClaimsView データを処理するためのコードを追加することです。

  1. Visual Studio の [ソリューション エクスプローラー] ペインで、[ClaimsView.xaml] ファイルを展開して、分離コード ファイル [ClaimsView.xaml.cs] を表示します。 [ClaimsView.xaml.cs] を開き、ファイルの内容を次のコードで置き換えます。

    using SignInMaui.MSALClient;
    using Microsoft.Identity.Client;
    
    namespace SignInMaui.Views;
    
    public partial class ClaimsView : ContentPage
    {
        public IEnumerable<string> IdTokenClaims { get; set; } = new string[] {"No claims found in ID token"};
        public ClaimsView()
        {
            BindingContext = this;
            InitializeComponent();
    
            _ = SetViewDataAsync();
        }
    
        private async Task SetViewDataAsync()
        {
            try
            {
                _ = await PublicClientSingleton.Instance.AcquireTokenSilentAsync();
    
                IdTokenClaims = PublicClientSingleton.Instance.MSALClientHelper.AuthResult.ClaimsPrincipal.Claims.Select(c => c.Value);
    
                Claims.ItemsSource = IdTokenClaims;
            }
    
            catch (MsalUiRequiredException)
            {
                await Shell.Current.GoToAsync("claimsview");
            }
        }
    
        protected override bool OnBackButtonPressed() { return true; }
    
        private async void SignOutButton_Clicked(object sender, EventArgs e)
        {
            await PublicClientSingleton.Instance.SignOutAsync().ContinueWith((t) =>
            {
                return Task.CompletedTask;
            });
    
            await Shell.Current.GoToAsync("mainview");
        }
    }
    

    ClaimsView.xaml.cs コードは、.NET MAUI アプリの要求ビューの分離コードを表します。 まず、必要な名前空間をインポートし、ContentPage を拡張する ClaimsView クラスを定義します。 IdTokenClaims プロパティは文字列の Enumerable であり、最初はクレームが見つからないことを示す 1 つの文字列に設定されています。

    ClaimsView コンストラクターは、バインディング コンテキストを現在のインスタンスに設定し、ビュー コンポーネントを初期化し、SetViewDataAsync メソッドを非同期的に呼び出します。 SetViewDataAsync メソッドは、トークンのサイレントな取得を試み、認証結果から要求を取得し、それらを Claims という名前の ListView に表示するために IdTokenClaims プロパティを設定します。 認証にユーザー操作が必要であることを示す MsalUiRequiredException が発生した場合、アプリは要求ビューに遷移します。

    OnBackButtonPressed メソッドは、戻るボタンの動作を常に true を返すようにオーバーライドして、ユーザーがこのビューから戻る動作をできないようにします。 SignOutButton_Clicked イベント ハンドラーは、PublicClientSingleton インスタンスを使用してユーザーをサインアウトし、完了すると main view に遷移します。

アプリ シェルを変更する

AppShell クラスは、アプリのビジュアル階層、つまりアプリの UI の作成に使用される XAML マークアップを定義します。 AppShellViews について知ることができるように、これを更新します。

  1. [ソリューション エクスプローラー] ペインで AppShell.xaml ファイルをダブルクリックして、XAML エディターを開きます。 XAML マークアップを次のコードで置き換えます。

    <?xml version="1.0" encoding="UTF-8" ?>
    <Shell
        x:Class="SignInMaui.AppShell"
        xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:local="clr-namespace:SignInMaui.Views"
        Shell.FlyoutBehavior="Disabled">
    
        <ShellContent
            Title="Home"
            ContentTemplate="{DataTemplate local:MainView}"
            Route="MainPage" />
    </Shell>
    

    XAML コードは、ポップアップ動作を無効にするクラスをAppShell定義し、メインコンテンツを、 クラスをShellContent指すタイトルHomeとコンテンツ テンプレートを持つ要素にMainView設定します。

  2. Visual Studio の [ソリューション エクスプローラー] ペインで、[AppShell.xaml] ファイルを展開して、分離コード ファイル [AppShell.xaml.cs] を表示します。 [AppShell.xaml.cs] を開き、ファイルの内容を次のコードで置き換えます。

    // Copyright (c) Microsoft Corporation. All rights reserved.
    // Licensed under the MIT License.
    using SignInMaui.Views;
    
    namespace SignInMaui;
    
    public partial class AppShell : Shell
    {
        public AppShell()
        {
            InitializeComponent();
            Routing.RegisterRoute("mainview", typeof(MainView));
            Routing.RegisterRoute("claimsview", typeof(ClaimsView));
        }
    }
    

    AppShell.xaml.cs ファイルを更新して、MainViewClaimsView に必要なルート登録を含めます。 InitializeComponent() メソッドを呼び出すことで、AppShell クラスの初期化が確実に行われるようにします。 RegisterRoute() メソッドは、mainviewclaimsview ルートをそれぞれのビューの種類 MainViewClaimsView に関連付けます。

プラットフォーム固有のコードを追加する

.NET MAUI アプリ プロジェクトには Platforms フォルダーが含まれており、各子フォルダーは、.NET MAUI がターゲットにできるプラットフォームを表しています。 既定の Application クラスを補完するアプリケーション固有の動作を提供するには、Platforms/Windows/App.xaml.cs を変更します。

ファイルの内容を次のコードで置き換えます。

using SignInMaui.MSALClient;
using Microsoft.Identity.Client;
using Microsoft.UI.Xaml;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

namespace SignInMaui.WinUI;

/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : MauiWinUIApplication
{
    /// <summary>
    /// Initializes the singleton application object.  This is the first line of authored code
    /// executed, and as such is the logical equivalent of main() or WinMain().
    /// </summary>
    public App()
    {
        this.InitializeComponent();

        // configure redirect URI for your application
        PlatformConfig.Instance.RedirectUri = $"msal{PublicClientSingleton.Instance.MSALClientHelper.AzureAdConfig.ClientId}://auth";

        // Initialize MSAL
        IAccount existinguser = Task.Run(async () => await PublicClientSingleton.Instance.MSALClientHelper.InitializePublicClientAppAsync()).Result;

    }

    protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();

    protected override void OnLaunched(LaunchActivatedEventArgs args)
    {
        base.OnLaunched(args);

        var app = SignInMaui.App.Current;
        PlatformConfig.Instance.ParentWindow = ((MauiWinUIWindow)app.Windows[0].Handler.PlatformView).WindowHandle;
    }
}

コードでは、アプリケーションのリダイレクト URI を構成し、MSAL を初期化してから、アプリケーションの親ウィンドウを設定します。 さらに、OnLaunched メソッドを オーバーライドして起動イベントを処理し、親ウィンドウ ハンドルを取得します。

アプリ設定を追加する

設定を使用すると、アプリの動作を構成するデータをコードから分離できるため、アプリを再構築せずに動作を変更できます。 MauiAppBuilder は、.NET MAUI アプリで設定を構成するための ConfigurationManager を提供します。 appsettings.json ファイルを EmbeddedResource として追加しましょう。

appsettings.json を作成するには、次の手順に従います。

  1. Visual Studio の [ソリューション エクスプローラー] ペインで、[SignInMaui] プロジェクトで右クリック>[追加]>[新しい項目...]

  2. [Web]>[JavaScript JSON 構成ファイル] を選択します。 そのファイルに appsettings.json という名前を付けます。

  3. [追加] を選択します。

  4. [appsettings.json] を選択します

  5. [プロパティ] ペインで、[ビルド アクション][埋め込みリソース] に設定します。

  6. [プロパティ] ペインで、[出力ディレクトリにコピー][常にコピー] に設定します。

  7. appsettings.json ファイルの内容を次のコードで置き換えます。

    {
      "AzureAd": {
        "Authority": "https://Enter_the_Tenant_Subdomain_Here.ciamlogin.com/",
        "ClientId": "Enter_the_Application_Id_Here",
        "CacheFileName": "msal_cache.txt",
        "CacheDir": "C:/temp"
      },
      "DownstreamApi": {
        "Scopes": "openid offline_access"
      }
    }
    
  8. appsettings.json 内で次のプレースホルダーを見つけます。

    1. Enter_the_Tenant_Subdomain_Here を、ディレクトリ (テナント) サブドメインに置き換えます。 たとえば、テナントのプライマリ ドメインが contoso.onmicrosoft.com の場合は、contoso を使用します。 テナント名がない場合は、テナントの詳細を読み取る方法を確認してください。
    2. Enter_the_Application_Id_Here を、前に登録したアプリのアプリケーション (クライアント) ID に置き換えます。

カスタム URL ドメインを使用する (省略可能)

カスタム ドメインを使用して、認証 URL を完全にブランド化します。 ユーザーの視点から見ると、認証プロセスの間、ユーザーは ciamlogin.com ドメイン名にリダイレクトされず、あなたのドメインにとどまります。

カスタム ドメインを使用するには、次の手順に従います。

  1. 外部テナントのアプリに対してカスタム URL ドメインを有効にする」の手順を実行して、外部テナントに対してカスタム URL ドメインを有効にします。

  2. appsettings.json ファイルを開きます。

    1. Authority プロパティの値を https://Enter_the_Custom_Domain_Here/Enter_the_Tenant_ID_Here に更新します。 Enter_the_Custom_Domain_Here を実際のカスタム URL ドメインに、Enter_the_Tenant_ID_Here を実際のテナント ID に置き換えます。 テナント ID がわからない場合は、テナントの詳細を読み取る方法を確認してください。
    2. [Enter_the_Custom_Domain_Here] という値を持つ knownAuthorities プロパティを追加します。

カスタム URL ドメインが login.contoso.com、テナント ID が aaaabbbb-0000-cccc-1111-dddd2222eeee の場合、appsettings.json ファイルに変更を加えた後には、ファイルは次のスニペットのようになるはずです。

{
  "AzureAd": {
    "Authority": "https://login.contoso.com/aaaabbbb-0000-cccc-1111-dddd2222eeee",
    "ClientId": "Enter_the_Application_Id_Here",
    "CacheFileName": "msal_cache.txt",
    "CacheDir": "C:/temp",
    "KnownAuthorities": ["login.contoso.com"]
  },
  "DownstreamApi": {
    "Scopes": "openid offline_access"
  }
}

.NET MAUI デスクトップ アプリを実行してテストする

.NET MAUI アプリは、複数のオペレーティング システムとデバイス上で実行できるように設計されています。 どのターゲットでアプリをテストしてデバッグしたいかを選択する必要があります。

Visual Studio ツール バーの [デバッグ ターゲット] を、デバッグしてテストしたいデバイスに設定します。 次の手順は、[デバッグ ターゲット]Windows に設定する方法を示しています。

  1. [デバッグ ターゲット] ドロップダウン リストを選択します。
  2. フレームワークを選択します
  3. [net7.0-windows...] を選択します

F5 キーを押すか、Visual Studio の上部にある "再生ボタン" を選択してアプリを実行します。

  1. これでサンプルの .NET MAUI デスクトップ アプリケーションをテストできるようになりました。 アプリケーションを実行すると、デスクトップ アプリケーション ウィンドウが自動的に表示されます。

    デスクトップ アプリケーションの [サインイン] ボタンのスクリーンショット

  2. 表示されたデスクトップ ウィンドウで、[サインイン] ボタンを選択します。 ブラウザー ウィンドウが開き、サインインが求められます。

    デスクトップ アプリケーションで資格情報を入力するためのユーザー プロンプトのスクリーンショット。

    サインイン プロセス中に、さまざまなアクセス許可を付与するように求められます (アプリケーションがデータにアクセスできるようにします)。 サインインと同意が成功すると、アプリケーション画面にメイン ページが表示されます。

    サインインした後に表示されるデスクトップ アプリケーションのメイン ページのスクリーンショット。

次の手順