Azure Cosmos DB ドキュメント データベースと Xamarin.Forms でユーザーを認証する

Download Sampleサンプルのダウンロード

Azure Cosmos DB ドキュメント データベースでは、複数のサーバーとパーティションにまたがることができるパーティション分割されたコレクションがサポートされ、同時に無制限のストレージとスループットがサポートされます。 この記事では、ユーザーが Xamarin.Forms アプリケーション内の自分のドキュメントにのみアクセスできるように、アクセスの制御とパーティション分割されたコレクションを組み合わせる方法について説明します。

概要

パーティション分割されたコレクションを作成するときはパーティション キーを指定する必要があり、同じパーティション キーを持つドキュメントは同じパーティションに格納されます。 そのため、ユーザーの ID をパーティション キーとして指定すると、作成されるパーティション分割されたコレクションには、そのユーザーのドキュメントのみが格納されます。 これにより、ユーザーと項目の数が増えるにつれて、Azure Cosmos DB ドキュメント データベースのスケーリングも行われます。

どのコレクションに対してもアクセスを許可する必要があり、Azure Cosmos DB for NoSQL のアクセス制御モデルでは、2 種類のアクセス コンストラクトが定義されています。

  • マスター キーは、Azure Cosmos DB アカウント内のすべてのリソースへの完全な管理アクセスを可能にし、Azure Cosmos DB アカウントの作成時に作成されます。
  • リソース トークンは、 データベースのユーザーと、コレクションやドキュメントなどの特定の Azure Cosmos DB リソースに対してユーザーが持っているアクセス許可の間の関係をキャプチャします。

マスター キーを公開すると、Azure Cosmos DB アカウントが悪意や過失によって使用される可能性が生じます。 一方、Azure Cosmos DB リソース トークンでは、付与されたアクセス許可に従って、Azure Cosmos DB アカウント内の特定のリソースの読み取り、書き込み、削除をクライアントに許可する安全なメカニズムが提供されます。

リソース トークンを要求および生成してモバイル アプリケーションに配信する一般的な方法は、リソース トークン ブローカーを使うことです。 次の図は、サンプル アプリケーションがリソース トークン ブローカーを使ってドキュメント データベース データへのアクセスを管理する方法の概要を示したものです。

Document Database Authentication Process

リソース トークン ブローカーは中間層の Web API サービスであり、Azure Cosmos DB アカウントのマスター キーを所有している Azure App Service でホストされます。 サンプル アプリケーションでは、リソース トークン ブローカーを使って、ドキュメント データベース データへのアクセスを次のように管理します。

  1. ログイン時に、Xamarin.Forms アプリケーションは Azure App Service にアクセスして認証フローを開始します。
  2. Azure App Service は、Facebook との OAuth 認証フローを実行します。 認証フローが完了すると、Xamarin.Forms アプリケーションはアクセス トークンを受け取ります。
  3. Xamarin.Forms アプリケーションはアクセス トークンを使って、リソース トークン ブローカーにリソース トークンを要求します。
  4. リソース トークン ブローカーは、アクセス トークンを使って Facebook にユーザーの ID を要求します。 その後、ユーザーの ID を使って Azure Cosmos DB にリソース トークンが要求され、それを使って認証されたユーザーのパーティション分割されたコレクションへの読み取り/書き込みアクセスが許可されます。
  5. Xamarin.Forms アプリケーションは、リソース トークンを使い、リソース トークンによって定義されているアクセス許可で Azure Cosmos DB リソースに直接アクセスします。

Note

リソース トークンの有効期限が切れると、それ以降のドキュメント データベース要求は 401 不認可例外を受け取ります。 この時点で、Xamarin.Forms アプリケーションは ID を再確立して、新しいリソース トークンを要求する必要があります。

Azure Cosmos DB のパーティション分割について詳しくは、Azure Cosmos DB でパーティション分割とスケーリングを行う方法に関する記事をご覧ください。 Azure Cosmos DB のアクセスの制御について詳しくは、「 Azure Cosmos DB のデータへのアクセスをセキュリティで保護する」と、Azure Cosmos DB for NoSQL でのアクセスの制御に関する記事をご覧ください。

段取り

リソース トークン ブローカーを Xamarin.Forms アプリケーションに統合するプロセスは次のとおりです。

  1. アクセスの制御を使う Azure Cosmos DB アカウントを作成します。 詳しくは、「Azure Cosmos DB の構成」をご覧ください。
  2. リソース トークン ブローカーをホストする Azure App Service を作成します。 詳しくは、「Azure App Service の構成」をご覧ください。
  3. 認証を実行する Facebook アプリを作成します。 詳しくは、「Facebook アプリの構成」をご覧ください。
  4. Facebook で簡単な認証を実行できるように Azure App Service を構成します。 詳しくは、「Azure App Service の認証の構成」をご覧ください。
  5. Azure App Service および Azure Cosmos DB と通信するように、Xamarin.Forms サンプル アプリケーションを構成します。 詳しくは、「Xamarin.Forms アプリケーションの構成」をご覧ください。

Note

Azure サブスクリプションをお持ちでない場合は、開始する前に無料アカウントを作成してください。

Azure Cosmos DB の構成

アクセスの制御を使う Azure Cosmos DB アカウントを作成するプロセスは次のとおりです。

  1. Azure Cosmos DB アカウントを作成する。 詳細については、Azure Cosmos DB アカウントの作成に関する記事を参照してください。
  2. Azure Cosmos DB アカウントで、/userid のパーティション キーを指定して、UserItems という名前の新しいコレクションを作成します。

Azure App Service の構成

Azure App Service でリソース トークン ブローカーをホストするプロセスは次のとおりです。

  1. Azure portal で、新しい App Service Web アプリを作成します。 詳しくは、App Service Environment での Web アプリの作成に関する記事をご覧ください。

  2. Azure portal で、Web アプリの [アプリの設定] ブレードを開き、次の設定を追加します。

    • accountUrl – 値は、Azure Cosmos DB アカウントの [キー] ブレードの Azure Cosmos DB アカウント URL である必要があります。
    • accountKey – 値は、Azure Cosmos DB アカウントの [キー] ブレードの Azure Cosmos DB マスター キー (プライマリまたはセカンダリ) である必要があります。
    • databaseId – 値は、Azure Cosmos DB データベースの名前である必要があります。
    • collectionId – 値は、Azure Cosmos DB コレクションの名前である必要があります (この場合は UserItems)。
    • hostUrl – 値は、App Service アカウントの [概要] ブレードにある Web アプリの URL である必要があります。

    次のスクリーンショットでは、この構成の例を示します。

    App Service Web App Settings

  3. リソース トークン ブローカー ソリューションを Azure App Service Web アプリに発行します。

Facebook アプリの構成

認証を実行するための Facebook アプリを作成するプロセスは次のとおりです。

  1. Facebook アプリを作成します。 詳しくは、Facebook デベロッパー センターで「アプリを登録して構成する」をご覧ください。
  2. Facebook ログイン製品をアプリに追加します。 詳しくは、Facebook デベロッパー センターで「アプリまたは Web サイトに Facebook ログインを追加する」をご覧ください。
  3. Facebook ログインを次のように構成します。
    • クライアント OAuth ログインを有効にします。
    • Web OAuth ログインを有効にします。
    • 有効な OAuth リダイレクト URI を、App Service Web アプリの URI に /.auth/login/facebook/callback を追加したものに設定します。

次のスクリーンショットでは、この構成の例を示します。

Facebook Login OAuth Settings

詳しくは、「Facebook にアプリケーションを登録する」をご覧ください。

Azure App Service の認証の構成

App Service の簡単な認証を構成するプロセスは次のとおりです。

  1. Azure portal で、App Service Web アプリに移動します。

  2. Azure portal で [認証/承認] ブレードを開き、次の構成を行います。

    • App Service 認証を有効にする必要があります。
    • 要求が認証されていない場合に実行するアクションは、[Facebook アカウントでログインする] に設定されている必要があります。

    次のスクリーンショットでは、この構成の例を示します。

    App Service Web App Authentication Settings

認証フローを有効にするには、Facebook アプリと通信するように App Service Web アプリを構成する必要もあります。 これを実現するには、Facebook ID プロバイダーを選び、Facebook デベロッパー センターでの Facebook アプリの設定から [アプリ ID][アプリ シークレット] の値を入力します。 詳しくは、「Facebook の情報をアプリケーションに追加する」をご覧ください。

Xamarin.Forms アプリケーションの構成

Xamarin.Forms サンプル アプリケーションを構成するプロセスは次のとおりです。

  1. Xamarin.Forms ソリューションを開きます。
  2. Constants.cs を開き、次の定数の値を更新します。
    • EndpointUri – 値は、Azure Cosmos DB アカウントの [キー] ブレードの Azure Cosmos DB アカウント URL である必要があります。
    • DatabaseName – 値は、ドキュメント データベースの名前である必要があります。
    • CollectionName– 値は、ドキュメント データベース コレクションの名前である必要があります (この場合は UserItems)。
    • ResourceTokenBrokerUrl – 値は、App Service アカウントの [概要] ブレードにあるリソース トークン ブローカー Web アプリの URL である必要があります。

ログインの開始

サンプル アプリケーションは、次のコード例で示すように、ブラウザーを ID プロバイダーの URL にリダイレクトすることで、ログイン プロセスを開始します。

var auth = new Xamarin.Auth.WebRedirectAuthenticator(
  new Uri(Constants.ResourceTokenBrokerUrl + "/.auth/login/facebook"),
  new Uri(Constants.ResourceTokenBrokerUrl + "/.auth/login/done"));

これにより、Azure App Service と Facebook の間で OAuth 認証フローが開始され、Facebook のログイン ページが表示されます。

Facebook Login

iOS の [キャンセル] ボタンを押すか、Android の [戻る] ボタンを押して、ログインを取り消すことができます。その場合、ユーザーは認証されていない状態のままになり、ID プロバイダーのユーザー インターフェイスは画面から削除されます。

リソース トークンの取得

認証が成功すると、WebRedirectAuthenticator.Completed イベントが発生します。 次のコード例は、このイベントの処理を示したものです。

auth.Completed += async (sender, e) =>
{
  if (e.IsAuthenticated && e.Account.Properties.ContainsKey("token"))
  {
    var easyAuthResponseJson = JsonConvert.DeserializeObject<JObject>(e.Account.Properties["token"]);
    var easyAuthToken = easyAuthResponseJson.GetValue("authenticationToken").ToString();

    // Call the ResourceBroker to get the resource token
    using (var httpClient = new HttpClient())
    {
      httpClient.DefaultRequestHeaders.Add("x-zumo-auth", easyAuthToken);
      var response = await httpClient.GetAsync(Constants.ResourceTokenBrokerUrl + "/api/resourcetoken/");
      var jsonString = await response.Content.ReadAsStringAsync();
      var tokenJson = JsonConvert.DeserializeObject<JObject>(jsonString);
      resourceToken = tokenJson.GetValue("token").ToString();
      UserId = tokenJson.GetValue("userid").ToString();

      if (!string.IsNullOrWhiteSpace(resourceToken))
      {
        client = new DocumentClient(new Uri(Constants.EndpointUri), resourceToken);
        ...
      }
      ...
    }
  }
};

認証が成功するとアクセス トークンが取得され、AuthenticatorCompletedEventArgs.Account プロパティで使用できます。 抽出したアクセス トークンは、リソース トークン ブローカーの resourcetoken API に対する GET 要求で使います。

resourcetoken API は、アクセス トークンを使って Facebook にユーザーの ID を要求します。次にそれを使って、Azure Cosmos DB にリソース トークンを要求します。 ドキュメント データベースにユーザーの有効なアクセス許可ドキュメントが既に存在する場合は、それが取得されて、リソース トークンを含む JSON ドキュメントが Xamarin.Forms アプリケーションに返されます。 ユーザーの有効なアクセス許可ドキュメントが存在しない場合は、ユーザーとアクセス許可がドキュメント データベースに作成され、リソース トークンがアクセス許可ドキュメントから抽出されて、JSON ドキュメントで Xamarin.Forms アプリケーションに返されます。

Note

ドキュメント データベース ユーザーはドキュメント データベースに関連付けられたリソースであり、各データベースは 0 人以上のユーザーを含むことができます。 ドキュメント データベースのアクセス許可は、ドキュメント データベース ユーザーに関連付けられたリソースであり、各ユーザーは 0 個以上のアクセス許可を含むことができます アクセス許可リソースは、ユーザーがドキュメントなどのリソースにアクセスしようとするときに必要なセキュリティ トークンへのアクセスを提供します。

resourcetoken API は、正常に完了すると、リソース トークンを含む JSON ドキュメントと共に、応答で HTTP 状態コード 200 (OK) を送信します。 次に示す JSON データは、一般的な成功応答メッセージです。

{
  "id": "John Smithpermission",
  "token": "type=resource&ver=1&sig=zx6k2zzxqktzvuzuku4b7y==;a74aukk99qtwk8v5rxfrfz7ay7zzqfkbfkremrwtaapvavw2mrvia4umbi/7iiwkrrq+buqqrzkaq4pp15y6bki1u//zf7p9x/aefbvqvq3tjjqiffurfx+vexa1xarxkkv9rbua9ypfzr47xpp5vmxuvzbekkwq6txme0xxxbjhzaxbkvzaji+iru3xqjp05amvq1r1q2k+qrarurhmjzah/ha0evixazkve2xk1zu9u/jpyf1xrwbkxqpzebvqwma+hyyaazemr6qx9uz9be==;",
  "expires": 4035948,
  "userid": "John Smith"
}

WebRedirectAuthenticator.Completed イベント ハンドラーは、resourcetoken API から応答を読み取って、リソース トークンとユーザー ID を抽出します。その後、リソース トークンは、Azure Cosmos DB アカウントへのアクセスに使われるエンドポイント、資格情報、接続ポリシーをカプセル化している DocumentClient コンストラクターに引数として渡され、Azure Cosmos DB に対する要求の構成と実行に使われます。 リソース トークンは、リソースに直接アクセスするための各要求と共に送信され、認証されたユーザーのパーティション分割されたコレクションへの読み取り/書き込みアクセスが許可されていることを示します。

ドキュメントの取得

認証されたユーザーにのみ属するドキュメントの取得は、ユーザーの ID をパーティション キーとして含むドキュメント クエリを作成することで実現できます。次のコード例はそれを示しています。

var query = client.CreateDocumentQuery<TodoItem>(collectionLink,
                        new FeedOptions
                        {
                          MaxItemCount = -1,
                          PartitionKey = new PartitionKey(UserId)
                        })
          .Where(item => !item.Id.Contains("permission"))
          .AsDocumentQuery();
while (query.HasMoreResults)
{
  Items.AddRange(await query.ExecuteNextAsync<TodoItem>());
}

クエリは、指定されたコレクションから、認証されたユーザーに属するすべてのドキュメントを非同期に取得し、表示のためにそれを List<TodoItem> コレクションに格納します。

CreateDocumentQuery<T> メソッドでは、ドキュメントのクエリを実行する必要があるコレクションを表す Uri 引数と、FeedOptions オブジェクトを指定します。 FeedOptions オブジェクトでは、無制限の数の項目をクエリで返すことができることと、パーティション キーとしてのユーザーの ID を指定します。 これにより、ユーザーのパーティション分割されたコレクション内のドキュメントのみが結果で返されます。

Note

リソース トークン ブローカーによって作成されたアクセス許可ドキュメントは、Xamarin.Forms アプリケーションによって作成されたドキュメントと同じドキュメント コレクションに格納されることに注意してください。 そのため、ドキュメント クエリには、ドキュメント コレクションに対するクエリにフィルター述語を適用する Where 句が含まれています。 この句により、アクセス許可ドキュメントはドキュメント コレクションから返されなくなります。

ドキュメント コレクションからのドキュメントの取得について詳しくは、「ドキュメント コレクションのドキュメントの取得」をご覧ください。

ドキュメントの挿入

ドキュメント コレクションにドキュメントを挿入する前に、次のコード例で示すように、パーティション キーとして使われる値で TodoItem.UserId プロパティを更新する必要があります。

item.UserId = UserId;
await client.CreateDocumentAsync(collectionLink, item);

これにより、ドキュメントがユーザーのパーティション分割されたコレクションに挿入されるようになります。

ドキュメント コレクションへのドキュメントの挿入について詳しくは、「ドキュメント コレクションにドキュメントを挿入する」をご覧ください。

ドキュメントの削除

次のコード例で示すように、パーティション分割されたコレクションからドキュメントを削除するときは、パーティション キーの値を指定する必要があります。

await client.DeleteDocumentAsync(UriFactory.CreateDocumentUri(Constants.DatabaseName, Constants.CollectionName, id),
                 new RequestOptions
                 {
                   PartitionKey = new PartitionKey(UserId)
                 });

これにより、ドキュメントを削除するパーティション分割されたコレクションが、Azure Cosmos DB にわかるようになります。

ドキュメント コレクションからドキュメントを削除する方法について詳しくは、「ドキュメント コレクションからドキュメントを削除する」をご覧ください。

まとめ

この記事では、ユーザーが Xamarin.Forms アプリケーション内の自分のドキュメント データベース ドキュメントにのみアクセスできるように、アクセスの制御とパーティション分割されたコレクションを組み合わせる方法について説明しました。 パーティション キーとしてユーザーの ID を指定すると、パーティション分割されたコレクションがそのユーザーのドキュメントのみを格納できるようになります。