チュートリアル: 外部テナントを使用して .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
が定義されるようにコードを組みます。
プロジェクトから MainPage.xaml と MainPage.xaml.cs を削除します。これらはもう必要ありません。 [ソリューション エクスプローラー] ペインで、[MainPage.xaml] のエントリを見つけて、右クリックして [削除] を選択します。
[SignInMaui] プロジェクトで右クリックし、[追加]>[新しいフォルダー] を選択します。 フォルダーの名前を [Views] にします。
[Views] で右クリックします。
[追加]>[新しい項目...] を選択します。
テンプレートの一覧で [.NET MAUI] を選択します。
[.NET MAUI ContentPage (XAML)] テンプレートを選択します。 ファイルの名前を [MainView.xaml] にします。
[追加] を選択します。
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>
ファイルを保存します。
以下でページに配置された XAML コントロールの主要なパーツを理解しましょう。
<ContentPage>
は MainView クラスのルート オブジェクトです。<VerticalStackLayout>
は ContentPage の子オブジェクトです。 このレイアウト コントロールは、子要素を垂直方向に順番に配置します。<Image>
は画像を表示します。この場合は、前にダウンロードした azureactive_directory.png_ を使用しています。<Label>
コントロールはテキストを表示します。<Button>
はユーザーが押すことができ、これによってClicked
イベントが発生します。Clicked
イベントに応じてコードを実行できます。Clicked="OnSignInClicked"
ボタンのClicked
イベントはOnSignInClicked
イベント ハンドラーに割り当てられ、これは分離コード ファイルで定義されます。 このコードを次の手順で作成します。
OnSignInClicked イベントを処理する
次の手順は、ボタンの Clicked
イベントのコードを追加することです。
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 トークンで見つかったユーザーの要求が表示されます。
Visual Studio の [ソリューション エクスプローラー] ペインで、[Views] を右クリックします。
[追加]>[新しい項目...] を選択します。
テンプレートの一覧で [.NET MAUI] を選択します。
[.NET MAUI ContentPage (XAML)] テンプレートを選択します。 ファイルの名前を [ClaimsView.xaml] にします。
[追加] を選択します。
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
データを処理するためのコードを追加することです。
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 マークアップを定義します。 AppShell
が Views
について知ることができるように、これを更新します。
[ソリューション エクスプローラー] ペインで
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
設定します。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
ファイルを更新して、MainView
とClaimsView
に必要なルート登録を含めます。InitializeComponent()
メソッドを呼び出すことで、AppShell
クラスの初期化が確実に行われるようにします。RegisterRoute()
メソッドは、mainview
とclaimsview
ルートをそれぞれのビューの種類MainView
とClaimsView
に関連付けます。
プラットフォーム固有のコードを追加する
.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
を作成するには、次の手順に従います。
Visual Studio の [ソリューション エクスプローラー] ペインで、[SignInMaui] プロジェクトで右クリック>[追加]>[新しい項目...]。
[Web]>[JavaScript JSON 構成ファイル] を選択します。 そのファイルに
appsettings.json
という名前を付けます。[追加] を選択します。
[appsettings.json] を選択します
[プロパティ] ペインで、[ビルド アクション] を [埋め込みリソース] に設定します。
[プロパティ] ペインで、[出力ディレクトリにコピー] を [常にコピー] に設定します。
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" } }
appsettings.json
内で次のプレースホルダーを見つけます。Enter_the_Tenant_Subdomain_Here
を、ディレクトリ (テナント) サブドメインに置き換えます。 たとえば、テナントのプライマリ ドメインがcontoso.onmicrosoft.com
の場合は、contoso
を使用します。 テナント名がない場合は、テナントの詳細を読み取る方法を確認してください。Enter_the_Application_Id_Here
を、前に登録したアプリのアプリケーション (クライアント) ID に置き換えます。
カスタム URL ドメインを使用する (省略可能)
カスタム ドメインを使用して、認証 URL を完全にブランド化します。 ユーザーの視点から見ると、認証プロセスの間、ユーザーは ciamlogin.com ドメイン名にリダイレクトされず、あなたのドメインにとどまります。
カスタム ドメインを使用するには、次の手順に従います。
「外部テナントのアプリに対してカスタム URL ドメインを有効にする」の手順を実行して、外部テナントに対してカスタム URL ドメインを有効にします。
appsettings.json ファイルを開きます。
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 がわからない場合は、テナントの詳細を読み取る方法を確認してください。- [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 に設定する方法を示しています。
- [デバッグ ターゲット] ドロップダウン リストを選択します。
- フレームワークを選択します
- [net7.0-windows...] を選択します
F5 キーを押すか、Visual Studio の上部にある "再生ボタン" を選択してアプリを実行します。
これでサンプルの .NET MAUI デスクトップ アプリケーションをテストできるようになりました。 アプリケーションを実行すると、デスクトップ アプリケーション ウィンドウが自動的に表示されます。
表示されたデスクトップ ウィンドウで、[サインイン] ボタンを選択します。 ブラウザー ウィンドウが開き、サインインが求められます。
サインイン プロセス中に、さまざまなアクセス許可を付与するように求められます (アプリケーションがデータにアクセスできるようにします)。 サインインと同意が成功すると、アプリケーション画面にメイン ページが表示されます。