Xamarin.iOS アプリに認証を追加する

このチュートリアルでは、Azure Active Directory を使用して、TodoApp プロジェクトに Microsoft 認証を追加します。 このチュートリアルを完了する前に、プロジェクトを作成し、バックエンドをデプロイしていることを確認してください。

注意

iOS アプリにはキーチェーン アクセスが必要であるため、iOS プロビジョニング プロファイルを設定する必要があります。 プロビジョニング プロファイルには、実際の iOS デバイスまたは有料の Apple Developer アカウント (シミュレーターを使用している場合) のいずれかが必要です。 この制限により認証を使用できない場合は、このチュートリアルをスキップし、アプリへのオフライン アクセスの追加に進むことができます。

ヒント

ここでは認証に Azure Active Directory を使用していますが、Azure Mobile Apps では任意の認証ライブラリを使用できます。

バックエンド サービスに認証を追加する

バックエンド サービスは標準の ASP.NET 6 サービスです。 ASP.NET 6 サービスの認証を有効にする方法を説明しているチュートリアルは、Azure Mobile Apps の場合にも当てはまります。

バックエンド サービスで Azure Active Directory 認証を有効にするには、次の手順を実行する必要があります。

  • アプリケーションを Azure Active Directory に登録します。
  • ASP.NET 6 バックエンド プロジェクトに認証チェックを追加します。

アプリケーションを登録する

まず、次の手順に従って、Azure AD テナントに Web API を登録し、スコープを追加します。

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

  2. 複数のテナントにアクセスできる場合は、トップ メニューの [ディレクトリとサブスクリプション] フィルターを使用して、アプリケーションを登録するテナントに切り替えます。

  3. Azure Active Directory を検索して選択します。

  4. [管理][アプリの登録]>[新規登録] の順に選択します。

    • 名前: 登録するアプリケーションの名前を入力します。たとえば、TodoApp Quickstart など。 このアプリのユーザーには、この名前が表示されます。 これは後で変更できます。
    • サポートされているアカウントの種類: 任意の組織のディレクトリ (Azure AD ディレクトリ - マルチテナント) 内のアカウントと、個人用の Microsoft アカウント (Skype、Xbox など)
  5. [登録] を選択します。

  6. [管理] で、 [API の公開]>[スコープの追加] の順に選択します。

  7. [アプリケーション ID URI] は既定値のままにして [保存して続行] を選択します。

  8. 次のように設定します。

    • スコープ名: access_as_user
    • 同意できるのはだれですか? : 管理者とユーザー
    • 管理者の同意の表示名: Access TodoApp
    • 管理者の同意の説明: Allows the app to access TodoApp as the signed-in user.
    • ユーザーの同意の表示名: Access TodoApp
    • ユーザーの同意の説明: Allow the app to access TodoApp on your behalf.
    • [状態] :有効
  9. [スコープの追加] を選択してスコープの追加を完了します。

  10. スコープの値をメモします。 それは、api://<client-id>/access_as_user となります。 クライアントを構成するときに、スコープが必要になります。 これを "Web API スコープ" と呼びます。

  11. [概要] を選択します。

  12. Essentials セクションのアプリケーション (クライアント) ID をメモしてください。後でバックエンド サービスを構成するために、この値が必要になります。 これを "Web API アプリケーション ID" と呼びます。

Visual Studio を開いて、TodoAppService.NET6 プロジェクトを選択します。

  1. TodoAppService.NET6 プロジェクトを右クリックし、[NuGet パッケージの管理] を選択します。

  2. 新しいタブで [参照] を選択し、検索ボックスに「Microsoft.Identity.Web」と入力します。

    Screenshot of adding the M S A L NuGet in Visual Studio.

  3. Microsoft.Identity.Web パッケージを選択し、[インストール] を押します。

  4. 指示に従ってパッケージのインストールを完了します。

  5. Program.csを開きます。 using ステートメントの一覧に以下を追加します。

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
  1. builder.Services.AddDbContext() の呼び出しのすぐ上に次のコードを追加します。
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddMicrosoftIdentityWebApi(builder.Configuration);
builder.Services.AddAuthorization();
  1. app.MapControllers() の呼び出しのすぐ上に次のコードを追加します。
app.UseAuthentication();
app.UseAuthorization();

Program.cs は次のようになります。

using Microsoft.AspNetCore.Datasync;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
using TodoAppService.NET6.Db;
  
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
  
if (connectionString == null)
{
  throw new ApplicationException("DefaultConnection is not set");
}
  
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddMicrosoftIdentityWebApi(builder.Configuration);
builder.Services.AddAuthorization();
builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddDatasyncControllers();
  
var app = builder.Build();
  
// Initialize the database
using (var scope = app.Services.CreateScope())
{
  var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
  await context.InitializeDatabaseAsync().ConfigureAwait(false);
}
  
// Configure and run the web service.
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
  1. Controllers\TodoItemController.cs を編集します。 クラスに [Authorize] 属性を追加します。 クラスは次のようになります。
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Datasync;
using Microsoft.AspNetCore.Datasync.EFCore;
using Microsoft.AspNetCore.Mvc;
using TodoAppService.NET6.Db;

namespace TodoAppService.NET6.Controllers
{
  [Authorize]
  [Route("tables/todoitem")]
  public class TodoItemController : TableController<TodoItem>
  {
    public TodoItemController(AppDbContext context)
      : base(new EntityTableRepository<TodoItem>(context))
    {
    }
  }
}
  1. appsettings.json を編集します。 次のブロックを追加します。
  "AzureAD": {
    "Instance": "https://login.microsoftonline.com",
    "ClientId": "<client-id>",
    "TenantId": "common"
  },

<client-id> を、前に記録した "Web API アプリケーション ID" に置き換えます。 完了すると、次のようになります。

{
  "AzureAD": {
    "Instance": "https://login.microsoftonline.com",
    "ClientId": "<client-id>",
    "TenantId": "common"
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=TodoApp;Trusted_Connection=True"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

サービスを Azure に再発行します。

  1. TodoAppService.NET6 プロジェクトを右クリックし、[パブリッシュ] を選択します。
  2. タブの右上隅にある [パブリッシュ] ボタンを選択します。

https://yoursite.azurewebsites.net/tables/todoitem?ZUMO-API-VERSION=3.0.0 を指定してブラウザーを開きます。 ここで、このサービスは 401 の応答を返すことに注意して下さい。これは、認証が必要であることを示しています。

Screenshot of the browser showing an error.

Visual Studio を開いて、TodoAppService.NET6 プロジェクトを選択します。

  1. TodoAppService.NET6 プロジェクトを右クリックし、[NuGet パッケージの管理] を選択します。

  2. [参照] タブを選択し、検索ボックスに「Microsoft.Identity.Web」と入力します。

    Screenshot of adding the M S A L NuGet in Visual Studio.

  3. Microsoft.Identity.Web パッケージを選択し、[パッケージの追加] を押します。

  4. ライセンスに同意し、パッケージのインストールを完了します。

  5. Program.csを開きます。 using ステートメントの一覧に以下を追加します。

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
  1. builder.Services.AddDbContext() の呼び出しのすぐ上に次のコードを追加します。
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddMicrosoftIdentityWebApi(builder.Configuration);
builder.Services.AddAuthorization();
  1. app.MapControllers() の呼び出しのすぐ上に次のコードを追加します。
app.UseAuthentication();
app.UseAuthorization();

Program.cs は次のようになります。

using Microsoft.AspNetCore.Datasync;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
using TodoAppService.NET6.Db;
  
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
  
if (connectionString == null)
{
  throw new ApplicationException("DefaultConnection is not set");
}
  
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddMicrosoftIdentityWebApi(builder.Configuration);
builder.Services.AddAuthorization();
builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddDatasyncControllers();
  
var app = builder.Build();
  
// Initialize the database
using (var scope = app.Services.CreateScope())
{
  var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
  await context.InitializeDatabaseAsync().ConfigureAwait(false);
}
  
// Configure and run the web service.
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
  1. Controllers\TodoItemController.cs を編集します。 クラスに [Authorize] 属性を追加します。 クラスは次のようになります。
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Datasync;
using Microsoft.AspNetCore.Datasync.EFCore;
using Microsoft.AspNetCore.Mvc;
using TodoAppService.NET6.Db;

namespace TodoAppService.NET6.Controllers
{
  [Authorize]
  [Route("tables/todoitem")]
  public class TodoItemController : TableController<TodoItem>
  {
    public TodoItemController(AppDbContext context)
      : base(new EntityTableRepository<TodoItem>(context))
    {
    }
  }
}
  1. appsettings.json を編集します。 次のブロックを追加します。
  "AzureAD": {
    "Instance": "https://login.microsoftonline.com",
    "ClientId": "<client-id>",
    "TenantId": "common"
  },

完了すると、次のようになります。

{
  "AzureAD": {
    "Instance": "https://login.microsoftonline.com",
    "ClientId": "<client-id>",
    "TenantId": "common"
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=TodoApp;Trusted_Connection=True"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

<client-id> を、前に記録しておいたアプリケーション (クライアント) ID に置き換えます。

完了したら、再びサービスをパブリッシュできます。

  1. TodoAppService.NET6 プロジェクトを右クリックし、[パブリッシュ][Azure にパブリッシュ] の順に選択します。
  2. バックエンド サービスを選択し、[パブリッシュ] を選択し、更新されたサイトを Azure にパブリッシュします。

https://yoursite.azurewebsites.net/tables/todoitem?ZUMO-API-VERSION=3.0.0 を指定してブラウザーを開きます。 ここで、このサービスは 401 の応答を返すことに注意して下さい。これは、認証が必要であることを示しています。

Screenshot of the browser showing an error.

アプリケーションに認証を追加する

Microsoft Datasync Framework には、HTTP トランザクションのヘッダー内で JSON Web Token (JWT) を使用するすべての認証プロバイダーのサポートが組み込まれています。 このアプリケーションは、Microsoft Authentication Library (MSAL) を使用して、前述のトークンを要求し、サインインしているユーザーのバックエンド サービスへの認証を行います。

ネイティブ クライアント アプリケーションを構成する

Microsoft ID ライブラリ (MSAL) などのクライアント ライブラリを使用してアプリでホストされている Web API への認証を許可するように、ネイティブ クライアントを登録できます。

  1. Azure portal で、 [Active Directory][アプリの登録][新規登録] の順に選択します。

  2. [アプリケーションの登録] ページで、次のようにします。

    • アプリケーション登録用の[名前]を入力します。 バックエンド サービスで使用される名前と区別するために、名前は native-quickstart の使用をお勧めします。
    • 任意の組織のディレクトリ (Azure AD ディレクトリ - マルチテナント) 内のアカウントと、個人用の Microsoft アカウント (Skype、Xbox など) を選択します。
    • [リダイレクト URI] は次のようにします。
      • [パブリック クライアント (モバイル & デスクトップ)] を選択します。
      • URL quickstart://auth を入力します。
  3. [登録] を選択します。

  4. [API のアクセス許可][アクセス許可の追加][自分の API] の順に選択します。

  5. 以前にバックエンド サービス用に作成したアプリ登録を選択します。 アプリ登録が表示されない場合は、access_as_user スコープを追加しているかを確認します。

    Screenshot of the scope registration in the Azure portal.

  6. [アクセス許可の選択] で [access_as_user] を選択してから、[アクセス許可の追加] を選択します。

  7. [認証] > [モバイル アプリケーションとデスクトップ アプリケーション] の順に選択します。

  8. https://login.microsoftonline.com/common/oauth2/nativeclient の横にあるチェック ボックスをオンにします。

  9. msal{client-id}://auth の横にあるチェック ボックスをオンにします ({client-id} をアプリケーション ID に置き換える)。

  10. [URI の追加] を選択し、追加 URI のフィールドに http://localhost を追加します。

  11. ページの下部にある [保存] を選択します。

  12. [概要] を選択します。 アプリケーション (クライアント) ID をメモします。この情報は後で必要になります。 これを "ネイティブ クライアント アプリケーション ID" と呼びます。

次の 3 つのリダイレクト URL を定義しました。

  • http://localhost は WPF アプリケーションで使用されます。
  • https://login.microsoftonline.com/common/oauth2/nativeclient は UWP アプリケーションで使用されます。
  • msal{client-id}://auth は、モバイル (Android および iOS) アプリケーションで使用されます。

TodoApp.sln ソリューションを Visual Studio で開き、TodoApp.iOS プロジェクトをスタートアップ プロジェクトとして設定します。 Microsoft Identity Library (MSAL)TodoApp.iOS プロジェクトに追加します。

Microsoft Identity Library (MSAL) をプラットフォーム プロジェクトに追加します。

  1. プロジェクトを右クリックし、 [NuGet パッケージの管理] を選択します。

  2. [参照] タブを選択します。

  3. 検索ボックスに「Microsoft.Identity.Client」と入力し、Enter キーを押します。

  4. Microsoft.Identity.Client の結果を選択し、 [インストール] をクリックします。

    Screenshot of selecting the M S A L NuGet in Visual Studio.

  5. 使用許諾契約書に同意してインストールを続行します。

ネイティブ クライアント ID とバックエンド スコープを構成に追加します。

TodoApp.Data プロジェクトを開き、Constants.cs ファイルを編集します。 ApplicationIdScopes の定数を追加します。

  public static class Constants
  {
      /// <summary>
      /// The base URI for the Datasync service.
      /// </summary>
      public static string ServiceUri = "https://demo-datasync-quickstart.azurewebsites.net";

      /// <summary>
      /// The application (client) ID for the native app within Azure Active Directory
      /// </summary>
      public static string ApplicationId = "<client-id>";

      /// <summary>
      /// The list of scopes to request
      /// </summary>
      public static string[] Scopes = new[]
      {
          "<scope>"
      };
  }

<client-id> を、Azure Active Directory にクライアント アプリケーションを登録するときに取得した "ネイティブ クライアント アプリケーション ID" に置き換え、<scope> を、サービス アプリケーション登録時に [API の公開] を使用したときにコピーした "Web API スコープ" に置き換えます。

  1. プラットフォーム プロジェクトを右クリックし、[NuGet パッケージの管理] を選択します。

  2. [参照] タブを選択します。

  3. 検索ボックスに「Microsoft.Identity.Client」と入力し、Enter キーを押します。

  4. Microsoft.Identity.Client の結果を選択し、 [インストール] をクリックします。

    Screenshot of selecting the M S A L NuGet in Visual Studio.

  5. 使用許諾契約書に同意してインストールを続行します。

ネイティブ クライアント ID とバックエンド スコープを構成に追加します。

TodoApp.Data プロジェクトを開き、Constants.cs ファイルを編集します。 ApplicationIdScopes の定数を追加します。

  public static class Constants
  {
      /// <summary>
      /// The base URI for the Datasync service.
      /// </summary>
      public static string ServiceUri = "https://demo-datasync-quickstart.azurewebsites.net";

      /// <summary>
      /// The application (client) ID for the native app within Azure Active Directory
      /// </summary>
      public static string ApplicationId = "<client-id>";

      /// <summary>
      /// The list of scopes to request
      /// </summary>
      public static string[] Scopes = new[]
      {
          "<scope>"
      };
  }

<client-id> を、Azure Active Directory にクライアント アプリケーションを登録するときに取得した値に置き換え、<scope> を、サービス アプリケーション登録時に [API の公開] を使用したときにコピーしたスコープに置き換えます。

TodoApp.iOS プロジェクトで ViewControllers\HomeViewController.cs を開きます。 次の using ステートメントを追加します。

using Microsoft.Datasync.Client;
using Microsoft.Identity.Client;
using System.Diagnostics;
using System.Linq;

HomeViewController クラスで、新しいプロパティを追加します。

public IPublicClientApplication IdentityClient { get; set; }

読み取るコンストラクターを調整します。

public HomeViewController() {
  Title = "Todo Items";
  TodoService = new RemoteTodoService(GetAuthenticationToken);
  TodoService.TodoItemsUpdated += OnTodoItemsUpdated;
}

GetAuthenticationToken メソッドをクラスに追加します。

public async Task<AuthenticationToken> GetAuthenticationToken()
{
    if (IdentityClient == null)
    {
        IdentityClient = PublicClientApplicationBuilder.Create(Constants.ApplicationId)
            .WithAuthority(AzureCloudInstance.AzurePublic, "common")
            .WithRedirectUri($"msal{Constants.ApplicationId}://auth")
            .WithIosKeychainSecurityGroup("com.microsoft.adalcache")
            .Build();
    }

    var accounts = await IdentityClient.GetAccountsAsync();
    AuthenticationResult result = null;
    bool tryInteractiveLogin = false;

    try
    {
        result = await IdentityClient
            .AcquireTokenSilent(Constants.Scopes, accounts.FirstOrDefault())
            .ExecuteAsync();
    }
    catch (MsalUiRequiredException)
    {
        tryInteractiveLogin = true;
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"MSAL Silent Error: {ex.Message}");
    }

    if (tryInteractiveLogin)
    {
        try
        {
            result = await IdentityClient
                .AcquireTokenInteractive(Constants.Scopes)
                .ExecuteAsync()
                .ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"MSAL Interactive Error: {ex.Message}");
        }
    }

    return new AuthenticationToken
    {
        DisplayName = result?.Account?.Username ?? "",
        ExpiresOn = result?.ExpiresOn ?? DateTimeOffset.MinValue,
        Token = result?.AccessToken ?? "",
        UserId = result?.Account?.Username ?? ""
    };
}

GetAuthenticationToken() メソッドは Microsoft Identity Library (MSAL) と連携して、サインインしているユーザーのバックエンド サービスへの認証に適したアクセス トークンを取得します。 この関数は、クライアント作成のために RemoteTodoService に渡されます。 認証が成功すると、各要求の認証に必要なデータと共に AuthenticationToken が生成されます。 成功しなかった場合は、代わりに期限切れの無効なトークンが生成されます。

AppDelegate クラスの末尾に次のコードを追加します。

[Export("application:openURL:options:")]
public bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
    AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(url);
    return true;
}

キーチェーン アクセスを Entitlements.plist に追加する:

  1. Entitlements.plist ファイルを開きます。

  2. [キーチェーン] を選択します。

  3. キーチェーン グループで [新規追加] を選択します。

  4. 値として「com.microsoft.adalcache」を入力します。

    Screenshot showing the i O S entitlements.

カスタム エンタイトルメントをプロジェクトに追加します。

  1. TodoApp.iOS プロジェクトを右クリックし、[プロパティ] を選択します。

  2. [iOS バンドル署名] を選択します。

  3. [カスタム エンタイトルメント] フィールドの横にある [...] ボタンを選択します。

  4. Entitlements を選択し、[開く] を選択します。

  5. Ctrl + S キーを押して、プロジェクトを保存します。

    Screenshot showing the i O S bundle signing properties.

キーチェーン アクセスを Entitlements.plist に追加する:

  1. Entitlements.plist ファイルを開きます。

  2. 必要に応じて、[ソース] ビューから [エンタイトルメント] ビューに切り替えます。 セレクターはウィンドウの右上隅にあります。

  3. 下にスクロールして [キーチェーン] パネルを見つけます。

  4. [キーチェーン] スイッチをオンにします。

  5. 緑の + アイコンを選択します。

  6. ボックスに「com.microsoft.adalcache」と入力し (既に入っていたものは上書きします)、Enter を押します。

    Screenshot showing the i O S keychain properties on macOS.

カスタム エンタイトルメントをプロジェクトに追加します。

  1. TodoApp.iOS プロジェクトを右クリックし、[オプション] を選択します。

  2. [iOS バンドル署名] を選択します。

  3. [カスタム エンタイトルメント] フィールドの横にある [...] ボタンを選択します。

  4. TodoApp.iOS>Entitlements.plist を選択し、[開く] を選択します。

  5. [OK] を選択します。

    Screenshot showing the i O S bundle signing properties.

アプリをテストする

注意

iOS アプリにはキーチェーン アクセスが必要であるため、プロビジョニング プロファイルを設定する必要があります。 プロビジョニング プロファイルには、実際のデバイスまたは有料の Apple Developer アカウント (シミュレーターを使用している場合) のいずれかが必要です。

TodoApp.iOS をスタートアップ プロジェクトとして設定し、アプリを実行します。 アプリが起動すると、アプリにサインインするように求められます。 最初の実行時には、アプリへの同意も求められます。 認証が完了すると、アプリは通常どおりに実行されます。

次のステップ

次に、オフライン ストアを実装して、オフラインで動作するようにアプリケーションを構成します。

参考資料