Azure Mobile Services の .NET バックエンドの更新: ログイン スコープのカスタマイズ、シングル サインオン、新しい ASP.NET Web API のサポート


このポストは、10 月 2 日に投稿された Custom Login Scopes, Single Sign-On, new ASP.NET Web API – updates to the Azure Mobile Services .NET backend の翻訳です。

このたびマイクロソフトは、Azure Mobile Services の .NET バックエンドを大幅に更新しました。ここではその更新の一部として下記の内容をご紹介します。

  • 認証プロバイダーからのスコープのカスタマイズが可能に
  • Windows ストア アプリケーションのシングル サインオンをサポート
  • 依存関係を Web API 5.2 に更新

Azure Mobile Services .NET についてあまり詳しくない方はこちらのブログ記事で概要をご理解のうえ、こちらのサイトで Azure Mobile Services の詳細についてご確認ください。

更新版の入手

最新の更新版は、これまでと同様に NuGet Package Explorer からダウンロードしてプロジェクトに適用できます。ソリューション エクスプローラーでお客様のプロジェクトのプロジェクト ノードまたはリファレンス ノードを右クリックし、[Manage NuGet Packages] オプションを選択します。

NuGet のダイアログの左側にある [Updates] オプションを選択し、検索ボックスに「mobileservices」と入力します。Mobile Services 関連の最新バージョンのパッケージ (1.0.402) が表示されるので、このパッケージを選択して [Update] ボタンをクリックします。

注: このタイミングでは一部の依存関係が更新されないため、[Update All] ボタンは使用しないでください。Mobile Services 関連のパッケージのみを更新すればすべての依存関係が自動的に更新されるため正常に動作します。

ソーシャル認証プロバイダー向けのスコープのカスタマイズ

このログイン スコープのカスタマイズは以前よりお客様からご要望をいただいていたもので、Node.js バックエンドでは既に可能になっています (英語)。.NET バックエンドにログインすると、認証プロバイダーと通信するためのトークンの発行をサーバーに依頼できます。既定では、このトークンではユーザーに関する基本情報のみにアクセスできますが、今回の更新により .NET バックエンドで追加のログイン スコープを要求できるようになりました。このため、サーバーから受け取るアクセス トークンで、より詳細な情報を認証プロバイダーから取得できるようになります。Node.js バックエンドと同様に、この機能は Facebook、Google、Microsoft の各アカウントに対応しています。また、Node.js バックエンドと同様に、ログイン スコープはアプリケーションの設定で定義できます。これを行う場合、ポータルの [configure] タブで MS_FacebookScopeMS_GoogleScope、および MS_MicrosoftScope (それぞれ Facebook、Google、Microsoft の各アカウントに対応) を設定します。

ではこのスコープの具体的な使用例について説明しましょう。ここでは上記のプロバイダーのうち Facebook と Microsoft の 2 つを使用するように .NET でモバイル サービスの認証をセットアップします。また、ログインしたユーザーの情報を取得する際にプロバイダーと通信するコントローラーを追加します。

    public class UserInfoController : ApiController
    {
        public ApiServices Services { get; set; }

        [AuthorizeLevel(AuthorizationLevel.User)]
        public async Task<JObject> GetUserInfo()
        {
            ServiceUser user = this.User as ServiceUser;
            if (user == null)
            {
                throw new InvalidOperationException("This can only be called by authenticated clients");
            }

            var identities = await user.GetIdentitiesAsync();
            var result = new JObject();
            var fb = identities.OfType<FacebookCredentials>().FirstOrDefault();
            if (fb != null)
            {
                var accessToken = fb.AccessToken;
                result.Add("facebook", await GetProviderInfo("https://graph.facebook.com/me?access_token=" + accessToken));
            }

            var ms = identities.OfType<MicrosoftAccountCredentials>().FirstOrDefault();
            if (ms != null)
            {
                var accessToken = ms.AccessToken;
                result.Add("microsoft", await GetProviderInfo("https://apis.live.net/v5.0/me/?method=GET&access_token=" + accessToken));
            }

            return result;
        }

        private async Task<JToken> GetProviderInfo(string url)
        {
            var c = new HttpClient();
            var resp = await c.GetAsync(url);
            resp.EnsureSuccessStatusCode();
            return JToken.Parse(await resp.Content.ReadAsStringAsync());
        }
    }

次に、アプリケーションでユーザーの情報を認証し取得できるようにします。この例では、上記の各認証プロバイダーのボタンを持つ簡単なアプリケーションを使用します。

    public sealed partial class MainPage : Page
    {
         public static MobileServiceClient MobileService = new MobileServiceClient(
              "https://blog20141002.azure-mobile.net/",
              "yourapplicationkeyshouldbehere00"
        );

        public MainPage()
        {
            this.InitializeComponent();
        }

        private async void btnFacebook_Click(object sender, RoutedEventArgs e)
        {
            await LoginAndGetUserInfo(MobileServiceAuthenticationProvider.Facebook);
        }

        private async void btnMicrosoft_Click(object sender, RoutedEventArgs e)
        {
            await LoginAndGetUserInfo(MobileServiceAuthenticationProvider.MicrosoftAccount);
        }

        private async Task LoginAndGetUserInfo(MobileServiceAuthenticationProvider provider)
        {
            try
            {
                var user = await MobileService.LoginAsync(provider);
                Debug("Logged in as {0}", user.UserId);
                var userInfo = await MobileService.InvokeApiAsync("userInfo", HttpMethod.Get, null);
                Debug("User info: {0}", userInfo);
                MobileService.Logout();
                Debug("Logged out");
                Debug("");
            }
            catch (Exception ex)
            {
                Debug("Error: {0}", ex);
            }
        }

        private void Debug(string text, params object[] args)
        {
            if (args != null && args.Length > 0) text = string.Format(text, args);
            this.txtDebug.Text = this.txtDebug.Text + text + Environment.NewLine;
        }
    }

このアプリケーションを実行し 3 つのプロバイダーのいずれかを使用してログインする場合、ユーザーの基本情報を取得する必要があります。次に示すのは、私の資格情報を取得するコードの例です。

Logged in as Facebook:xxxxxxxxxxxxx9805
User info: {
  "facebook": {
    "id": "xxxxxxxxxxxxx9805",
    "first_name": "Carlos",
    "gender": "male",
    "last_name": "Figueira",
    "link": "https://www.facebook.com/app_scoped_user_id/xxxxxxxxxxxxx9805/",
    "locale": "en_US",
    "name": "Carlos Figueira",
    "timezone": -7,
    "updated_time": "2013-12-12T04:09:57Z",
    "verified": true
  }
}
Logged out

Logged in as MicrosoftAccount:xxxxxxxxxxxxd789
User info: {
  "microsoft": {
    "id": "xxxxxxxxxxxxd789",
    "name": "Carlos Figueira",
    "first_name": "Carlos",
    "last_name": "Figueira",
    "link": "https://profile.live.com/",
    "gender": null,
    "locale": "en_US",
    "updated_time": "2014-09-30T09:38:42Z"
  }
}
Logged out

基本情報は上記のコードで取得できますが、アプリケーションでユーザーの電子メール アドレスなど他の情報も必要な場合、サービスにログインすることで付与されたアクセス トークンではこれらの情報にアクセスできません。しかし、アプリケーション設定の MS_FacebookScope および MS_MicrosoftScope でログイン中に追加のスコープを要求すると、必要な追加情報を取得することができます。

再度クライアント アプリケーションを実行し、ユーザーが追加情報へのアプリケーション アクセスを付与された後に、新しく要求した情報を取得します。

Logged in as Facebook:xxxxxxxxxxxxx9805
User info: {
  "facebook": {
    "id": "xxxxxxxxxxxxx9805",
    "birthday": "xx/yy/zzzz",
    "email": "xxxxxxxxxxxxxxxxxxx@hotmail.com",
    "first_name": "Carlos",
    "gender": "male",
    "last_name": "Figueira",
    "link": "https://www.facebook.com/app_scoped_user_id/xxxxxxxxxxxxx9805/",
    "locale": "en_US",
    "name": "Carlos Figueira",
    "timezone": -7,
    "updated_time": "2013-12-12T04:09:57Z",
    "verified": true
  }
}
Logged out

Logged in as MicrosoftAccount:xxxxxxxxxxxxd789
User info: {
  "microsoft": {
    "id": "xxxxxxxxxxxxd789",
    "name": "Carlos Figueira",
    "first_name": "Carlos",
    "last_name": "Figueira",
    "link": "https://profile.live.com/",
    "gender": null,
    "emails": {
      "preferred": "xxxxxxxxxxxxxxxxxxx@hotmail.com",
      "account": "xxxxxxxxxxxxxxxxxxx@hotmail.com",
      "personal": null,
      "business": null
    },
    "locale": "en_US",
    "updated_time": "2014-09-30T09:38:42Z"
  }
}
Logged out

スコープの追加要求に関する最後の注意点として、要求するユーザー情報は、必要最小限のみにすることを推奨します。多くのユーザーは使用するアプリケーションに大量の情報を提供することを快く思わないため、ログイン時に要求される情報量が多いと、それを理由にアプリケーションの使用を諦めてしまう可能性があります。

Windows ストア アプリケーションのシングル サインオンのサポート

Windows ストア アプリケーションで Mobile Services SDK を使用する場合、アプリケーションが MobileServiceClient で認証プロバイダーを渡す際、LoginAsync メソッドを呼び出すたびに認証ウィンドウが表示され、ユーザーはこのページで資格情報を入力して [sign in] ボタンをクリックする必要があります。これは、ユーザーがプロバイダーのログイン ページで [remember me] ボタンをクリックした場合でも同様です (Windows は再入力をしなくて済むように資格情報をキャッシュしていますが、それでもログイン時にはボタンをクリックする必要があります)。これは、既定では認証セッションからの Cookie が保持されていないため、プロバイダーのページが再度表示されたときに、以前に使用したユーザーの認証情報を保持した Cookie が存在しないためです。追加のフラグを取得する際には LoginAsync でオーバーロードが発生します (英語)。これは認証セッションでクライアントが Cookie をキャッシュすることを示しているため、次回 LoginAsync を呼び出す際には、ユーザー エクスペリエンス改善のために、認証ダイアログは短時間表示された後で自動的に消去されます。

前のセクションで表示したクライアントで必要なことは、追加のオーバーロードを使用して、LoginAsync の 2 番目のパラメーターに true を渡すことだけです。

    private async Task LoginAndGetUserInfo(MobileServiceAuthenticationProvider provider)
    {
        try
        {
            var user = await MobileService.LoginAsync(provider, true);
            Debug("Logged in as {0}", user.UserId);
            var userInfo = await MobileService.InvokeApiAsync("userInfo", HttpMethod.Get, null);
            Debug("User info: {0}", userInfo);
            MobileService.Logout();
            Debug("Logged out");
            Debug("");
        }
        catch (Exception ex)
        {
            Debug("Error: {0}", ex);
        }
    }

サーバー側でも、実施済みでない場合は 1 点だけ変更が必要です。このシナリオを有効にするには、このアプリケーションのパッケージ SID (アプリケーション ID の 1 つ) がサービス内に格納されている必要があるため、アプリケーションが Windows ストアのアプリケーションと関連付けられている必要があります。Windows ストア ダッシュボードでアプリケーションを作成するとパッケージ SID を取得できます。値を確認する方法については、アプリケーション パッケージを Microsoft 認証に登録する方法を説明したチュートリアルを参照してください。お客様のアプリケーションで Microsoft 認証を使用しない場合 (Facebook や Twitter を使用する場合) は、クライアント ID とクライアント シークレットをコピーする必要はありませんが、ポータルの [identity] タブの [microsoft account settings] でパッケージ SID をコピーする必要があります。

ASP.NET Web API 2.2 のサポート

今回の更新により、.NET バックエンドで ASP.NET Web API 2.2 (または Microsoft.AspNet.WebApi.Owin NuGet パッケージ (英語) のバージョン 5.2.x) がサポートされました。これにより、属性ルーティングの改良や OData v4 など、このリリースの新機能や不具合の修正がすべて使用可能になります。変更の完全なリストは「ASP.NET Web API 2.2 の新機能 (英語)」でご確認いただけます。

最後に

今回の記事が皆様のお役に立ちますと幸いです。いつものお願いではありますが、マイクロソフトでは皆様からのご意見やご感想をお待ちしております。この記事のコメント欄、Twitter アカウント (@AzureMobile)、またはMSDN のフォーラム (英語) までお気軽にお寄せください。

Comments (0)

Skip to main content