チュートリアル: 外部テナントを使用して .NET MAUI シェル アプリでユーザーをサインインさせる
このチュートリアルは、.NET Multi-Platform App UI (.NET MAUI) シェル アプリを作成し、Microsoft Entra 管理センターを使用した認証のためにそれを準備する方法を見ていくシリーズの、最後のパートです。 このシリーズのパート 2 では、カスタム Microsoft Authentication Library (MSAL) クライアント ヘルパーを追加して MSAL SDK を初期化し、必要なライブラリをインストールして、画像リソースを含めました。 この最後のステップでは、.NET MAUI にサインインとサインアウトのコードを追加し、Android プラットフォームでシェル アプリを実行する方法を見ていきます。
このチュートリアルでは、次のことについて説明します。
- サインインとサインアウト コードを追加します。
- アプリ シェルを変更します。
- プラットフォーム固有のコードを追加します。
- アプリ設定を追加します。
- .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 がターゲットにできるプラットフォームを表しています。 既定のアプリケーション クラスを補完する Android アプリケーション固有の動作を提供するには、次の手順に従います。
[ソリューション エクスプローラー] ペインで
Platforms/Android/AndroidManifest.xml
ファイルをダブルクリックして、XML エディターを開きます。 次のプロパティを更新します。- [アプリケーション名] を MAUI CIAM に設定します。
- [パッケージ名] を SignInMaui.Droid に設定します。
- [Android の最小バージョン] を "Android 5.0 (API レベル 21)" に設定します。
[ソリューション エクスプローラー] ペインで
Platforms/Android/MainActivity.cs
ファイルをダブルクリックして、CSharp エディターを開きます。 ファイルの内容を次のコードで置き換えます。// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using Android.App; using Android.Content; using Android.Content.PM; using Android.OS; using SignInMaui.MSALClient; using Microsoft.Identity.Client; namespace SignInMaui; [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] public class MainActivity : MauiAppCompatActivity { protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); // configure platform specific params PlatformConfig.Instance.RedirectUri = $"msal{PublicClientSingleton.Instance.MSALClientHelper.AzureAdConfig.ClientId}://auth"; PlatformConfig.Instance.ParentWindow = this; // Initialize MSAL and platformConfig is set _ = Task.Run(async () => await PublicClientSingleton.Instance.MSALClientHelper.InitializePublicClientAppAsync()).Result; } protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) { base.OnActivityResult(requestCode, resultCode, data); AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(requestCode, resultCode, data); } }
以下で追加したコードの主要な部分を理解しましょう。
- 必要な
using
ステートメントが先頭に含まれています。 MainActivity
クラスはMauiAppCompatActivity
を継承して定義されていて、これは .NET MAUI の Android プラットフォームの基本クラスです。- [Activity] 属性が
MainActivity
クラスに適用され、Android アクティビティのさまざまな設定を指定しています。Theme = "@style/Maui.SplashTheme"
は、アクティビティのスプラッシュ テーマを設定します。MainLauncher = true
は、このアクティビティをアプリケーションのメイン エントリ ポイントとして指定します。ConfigurationChanges
はアクティビティが処理できる構成変更 ("画面サイズ"、"向き"、"UI モード"、"画面レイアウト"、"最小画面サイズ"、"密度" など) を指定します。
OnCreate
メソッドは、アクティビティの作成時にカスタム ロジックを提供するためにオーバーライドされます。base.OnCreate(savedInstanceState)
は、メソッドの基本実装を呼び出します。PlatformConfig.Instance.RedirectUri
は、PublicClientSingleton.Instance.MSALClientHelper.AzureAdConfig.ClientId
に基づいて動的に生成される値に設定されます。 MSAL クライアントのリダイレクト URI を構成します。PlatformConfig.Instance.ParentWindow
は、認証関連の操作の親ウィンドウを指定する現在のアクティビティ インスタンスに設定されます。PublicClientSingleton.Instance.MSALClientHelper.InitializePublicClientAppAsync()
は、MSALClientHelper
という名前のシングルトン インスタンスのヘルパー メソッドを使用して、MSAL クライアント アプリを非同期的に初期化します。Task.Run
は、バックグラウンド スレッドで初期化を実行するために使用され、.Result
はタスクが完了するまで同期的に待機するために使用されます。
OnActivityResult
メソッドは、現在のアクティビティによって起動されたアクティビティの結果を処理するためにオーバーライドされます。base.OnActivityResult(requestCode, resultCode, data)
は、メソッドの基本実装を呼び出します。AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(requestCode, resultCode, data)
は、受信した要求コード、結果コード、および意図データに基づいて認証継続イベント引数を設定します。 これは、外部アクティビティが結果を返した後に認証フローを続行するために使用されます。
- 必要な
Visual Studio の [ソリューション エクスプローラー] ペインで、[プラットフォーム] を選択します。
[Android] フォルダーで右クリック>[追加]>[新しい項目...]。
[C# 項目]>[クラス] を選択します。 そのファイルに
MsalActivity.cs
という名前を付けます。MsalActivity.cs
ファイルの内容を次のコードで置き換えます。// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; using System.Collections.Generic; using System.Linq; using System.Text; using Android.App; using Android.Content; using Android.OS; using Android.Runtime; using Android.Views; using Android.Widget; using Microsoft.Identity.Client; namespace MauiAppBasic.Platforms.Android.Resources { [Activity(Exported =true)] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataHost = "auth", DataScheme = "msalEnter_the_Application_Id_Here")] public class MsalActivity : BrowserTabActivity { } }
以下で追加したコードの主要な部分を理解しましょう。
MsalActivity
クラスはMauiAppBasic.Platforms.Android.Resources
名前空間内で宣言されています。 このクラスはBrowserTabActivity
クラスを継承しているので、その機能を拡張しています。- このクラスは
[Activity(Exported = true)]
属性で修飾されます。これは、アクティビティがエクスポートされ、他のメソッドからアクセスできることを示します。 - 意図フィルターは、"[IntentFilter(...)]" 属性を使用して指定します。
ActionView
意図をインターセプトするようにアクティビティを構成します。 - 意図フィルターは、指定された
DataScheme
(msalEnter_the_Application_Id_Here
) とDataHost
("auth") を使用してActionView
意図を処理するように設定されます。 この構成により、アクティビティはActionView
意図をインターセプトして処理することで、認証プロセスを処理できます。Enter_the_Application_Id_Here
を、前に登録したアプリのアプリケーション (クライアント) ID で置き換えます。
アプリ設定を追加する
設定を使用すると、アプリの動作を構成するデータをコードから分離できるため、アプリを再構築せずに動作を変更できます。 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 ツール バーの [デバッグ ターゲット] を、デバッグしてテストしたいデバイスに設定します。 次の手順は、[デバッグ ターゲット] を Android に設定する方法を示しています。
- [デバッグ ターゲット] ドロップダウン リストを選択します。
- [Android エミュレーター] を選択します。
- エミュレーター デバイスを選択します。
F5 キーを押すか、Visual Studio の上部にある "再生ボタン" を選択してアプリを実行します。
これでサンプルの .NET MAUI Android アプリをテストできるようになりました。 アプリを実行すると、エミュレーターに Android アプリ ウィンドウが表示されます。
表示された Android ウィンドウで、[サインイン] ボタンを選択します。 ブラウザー ウィンドウが開き、サインインが求められます。
サインイン プロセス中に、さまざまなアクセス許可を付与するように求められます (アプリケーションがデータにアクセスできるようにします)。 サインインと同意が成功すると、アプリケーション画面にメイン ページが表示されます。