おでんはじめました。

required ちくわぶ and 巾着,optional はんぺん.

.NET MAUI で Azure Notification Hubs でのプッシュ通知を実装する(iOS編)

表題の通り .NET MAUI の iOS で Azure Notification Hubs(以下 Notification Hubs) でのプッシュ通知を実装してみます。

残念ながら Xamarin.Forms の iOS で使用していたクライアント用の Xamarin.Azure.NotificationHubs.iOS パッケージは MAUI 用にアップデートされていないので、.NET MAUI の issue にもある shiny パッケージを使用します。このパッケージはプレビュー版なのでご注意ください(2023年1月15日現在)。

Maui+iOS での Notification Hubs でのプッシュ通知は現時点でこのパッケージ一択のようです。

www.nuget.org

github.com

github.com

プッシュ通知に関する設定に関しては Xamarin.Forms と基本的に同じなので、このドキュメントに倣って進めていきます。クライアントの実装部分が shiny パッケージを使うこと以外は基本的に Xamarin.Forms と同じになります。

learn.microsoft.com

1. 証明書の署名要求ファイルを生成する

Apple Push Notification Service (APNs) では、証明書を使用してプッシュ通知を認証します。 次の手順に従って、通知を送受信するために必要なプッシュ証明書を作成します。

まずはじめに、証明書署名要求 (CSR) ファイルを生成します。 これは、Apple が署名済みのプッシュ証明書を生成するために使用します。

キーチェーンアクセス を起動します。メニューから、「キーチェーンアクセス>証明書アシスタント>認証局に証明書を要求」を選択します。

「ユーザのメールアドレス」は適当で構いません。名前も適当に入力して「ディスクに保存」を選択して、「続ける」を選択して保存します。

2. アプリケーションをプッシュ通知に登録する

次に、アプリを Apple Developer にて登録をおこないプッシュ通知を有効にします。その後、先ほどエクスポートした CSR をアップロードしてプッシュ証明書を作成します。

Apple Developer ポータルのアカウントから ID(英語) に移動します (最近デザインが変わった感じ)。

Identifiers の横にある+をクリックします。

「App IDs」を選択して「Continue」をクリックします。

「Register a new identifier」で「App」を選択して「Continue」をクリックします。

Bundle IDアプリディストリビューションガイドに従い、 <組織 ID>.<製品名> の形式のバンドル ID を入力します。この Bundle ID は MAUI プロジェクトで使用します。

App ID Prefix は複数ある場合は(組織か個人かなど)適切な方を選択します。 Description は任意で構いません。その下にある Push Notifications を選択して「Continue」をクリックします。

登録の確認で「Register」を忘れずにクリックします。

登録が完了すると、新しいアプリ ID が Certificates, Identifiers & Profiles ページに項目として表示されます。

3. Notification Hubs の証明書を作成する

Notification Hubs を APNs と連携させるには、証明書が必要です。これには、次の 2 とおりの方法があります。

  1. Notification Hubs に直接アップロードできる .p12 を作成する方法
  2. トークンベースの認証に使用できる .p8 を作成する方法

ここでは新しい方法である後者の方で進めていきます。

Apple Developer の Certificates, Identifiers & Profiles から Keys の横の+マークをクリックします。

Apple Push Notification services(APNs) にチェックを入れて、任意の名前で Key Name を入力します。

Register で登録します。

Download をクリックして .p8 ファイル(①)をダウンロードします。また、Key ID(②)をコピーしておきます。 あわせて、チーム ID(③、右上にある 10 桁の英数字)、バンドル ID(④)もあわせてコピーしておきます。

この後出てくる Notification Hubs と紐付ける際にこの4つの情報が必要になります。

4. アプリケーションのプロビジョニング プロファイルを作成する

Apple Developr の「Profiles」の「+」をクリックします。「iOS App Development」を選択して「Continue」をクリックします。

作成した App ID(Bundle ID) を選択して「Continue」をクリックします。

対象のユーザーを選択して「Continue」をクリックします。

使用するデバイスを選択して「Continue」をクリックします。

名前を決めて証明書を生成します。

生成した証明書をダウンロードします。

ダウンロードしたファイルをビルドする対象の Mac でダブルクリックすると Xcode 起動してインストールされます。

5. iOS プッシュ通知向けに Notification Hubs を構成する

Notification Hub の Settings > Apple(APNS)を選択します。

トークンベースの認証なので Token を選択します。以下の情報をコピペします。 - Key ID: 上記でコピーした② - Bundle ID: 上記の④ - Team ID: 上記の③ - Token: ダウンロードした .p8 ファイル(①)をエディタで開いてコピペ

Application Mode は開発用なので Sandbox を選択します。

6. MAUI アプリケーションの実装をおこなう

ここからやっと本題に入ることができます。

ここでは MAUI アプリケーションを作成して、shiny パッケージを使用して Notification Hubs のメッセージを受信するところまでの実装をおこないます。

MAUI アプリケーションを作成後、プロジェクトのプロパティ→MAUI 共有→アプリケーション ID に Bundle ID をペーストとします。

Entitlements.plist というファイルを作成して以下の内容をペースとして、Platforms > iOS にコピーします。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>aps-environment</key>
    <string>development</string>
</dict>
</plist>

.csproj を開いて以下を追加します。「パッケージの追加」からおこなう場合は、「プレリリースをチェックします」にチェックを入れてください。バージョンは 2023年1月15日現在のものです。

<ItemGroup>
  <PackageReference Include="Shiny.Extensions.Configuration" Version="3.0.0-alpha-0495" />
  <PackageReference Include="Shiny.Hosting.Maui" Version="3.0.0-alpha-0495" />
  <PackageReference Include="Shiny.Push.AzureNotificationHubs" Version="3.0.0-alpha-0495" />
  <PackageReference Include="Shiny.Push" Version="3.0.0-alpha-0495" />
</ItemGroup>

プロジェクトに appsettings.apple.jsonappsettings.json を追加します。プロパティのビルドアクションをそれぞれ MauiAsset にします。

appsettings.apple.json は下記をコピペします(または空でも良いです)。アプリケーションでこの情報は実際には使いませんがビルドにはこのファイルが必要になります。

{
    "Firebase": {
        "ProjectId": "",
        "AppId": "",
        "SenderId": "",
        "ApiKey": ""
    }
}

appsettings.json は以下の形式で各値を Azure Portal からコピペします。ListenerConnectionStringAccess Policies → DefaultListenSharedAccessSignature から、HubNameOverViewName からそれぞれコピペします。

{
  "AzureNotificationHubs": {
    "ListenerConnectionString": "接続文字列(DefaultListenSharedAccessSignature)",
    "HubName": "ハブ名"
  }
}

MauiProgram.cs を以下のように修正します。
①はshiny 全体の機能を追加します。この中にプッシュ通知の機能も含まれています。
②は json を読み込むようにする機能を追加しています。
③で appsettings.json の値を取得して、④で取得した値を Notification Hubs へ設定しています。

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .UseShiny()  // 追加①
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            })
            .Configuration.AddJsonPlatformBundle(false);    // 追加②

  // 追加③
        var cfg = builder.Configuration.GetSection("AzureNotificationHubs");

  // 追加④
        builder.Services.AddPush<MyPushDelegate>();
        var service = builder.Services.AddPushAzureNotificationHubs<MyPushDelegate>(
            cfg["ListenerConnectionString"],
            cfg["HubName"]
        );
        
        return builder.Build();
    }
}

AddPushAzureNotificationHubs メソッドで渡している <MyPushDelegate クラスは(shiny のサンプルでは SQlite への書き込みを実装していますが)最低限の実装の場合は下記のような感じになります。ここにメッセージの受信をした場合などプッシュ通知に関するイベントが流れてくることになります。

public class MyPushDelegate : IPushDelegate
{
    readonly IPushManager pushManager;

    public MyPushDelegate(IPushManager pushManager)
    {
        this.pushManager = pushManager;
    }

    public Task OnEntry(PushNotification push)
        => this.Insert("PUSH ENTRY");

    public Task OnReceived(PushNotification push)
        => this.Insert("PUSH RECEIVED");

    public Task OnTokenRefreshed(string token)
        => this.Insert("PUSH TOKEN REFRESH");

    Task Insert(string info)
    {
        Console.WriteLine(info);
        return Task.CompletedTask;
    } 
}

Platforms > iOS > AppDelegate.cs を下記のように修正します。デバイスが登録された場合は RegisteredForRemoteNotifications メソッドが、メッセージを受信した場合は DidReceiveRemoteNotification メソッドが呼び出されます。

[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
    protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
// 以下を追加
    [Export("application:didRegisterForRemoteNotificationsWithDeviceToken:")]
    public void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
        => global::Shiny.Hosting.Host.Current.Lifecycle().OnRegisteredForRemoteNotifications(deviceToken);

    [Export("application:didFailToRegisterForRemoteNotificationsWithError:")]
    public void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
        => global::Shiny.Hosting.Host.Current.Lifecycle().OnFailedToRegisterForRemoteNotifications(error);

    [Export("application:didReceiveRemoteNotification:fetchCompletionHandler:")]
    public void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo,
        Action<UIBackgroundFetchResult> completionHandler)
    {
        global::Shiny.Hosting.Host.Current.Lifecycle().OnDidReceiveRemoveNotification(userInfo, completionHandler);
    }
}

ViewModel のクラスを追加して、DI で登録された IPushManager クラスを使えるようにします。IPushManager クラスにある RequestAccess メソッドを使ってデバイスを Notification Hubs へ登録することになります。ここでは最低限の BaseViewModel クラスを作成してそれを継承しています。

public class MainPageViewModel : BaseViewModel
{
    private readonly IPushManager _pushManager;

    public MainPageViewModel(IPushManager pushManager)
    {
        _pushManager = pushManager;
    }

    public async Task<PushAccessState> RequestAccess()
    {
        // iOS デバイスを Notification Hub に登録する
        var result = await _pushManager.RequestAccess();

        var accessState = result.Status;

        // 発行されたデバイストークンが返ってくる(iOSのデバイスIDではないので注意)
        var regToken = _pushManager.RegistrationToken;

        return result;
    }
}
public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

MainPage クラスを以下のように修正します。OnAppearing イベントでデバイスをプッシュ通知へ登録(RequestAccess)してますが、これでアプリケーション起動時に(最初に MainPage が表示される時に)実行されることになります。

public partial class MainPage : ContentPage
{
    int count = 0;
    MainPageViewModel viewModel => BindingContext as MainPageViewModel;

    //    }
    public MainPage(MainPageViewModel vm)
    {
        InitializeComponent();
        BindingContext = vm;
    }

    protected override async void OnAppearing()
    {
        base.OnAppearing();
        await viewModel.RequestAccess();
    }
}

これで実装は完了したので、実機デバイス(シミュレータではプッシュ通知のテストはできません)でデバッグをします。

7. プッシュ通知のメッセージを送信する

Azure Portal の Notification Hub の Test Send からメッセージを送ることができます。PlatformApple を選択して送信しますが、このときペイロードcontent-available: 1 が必要なので注意してください。これが無いとアプリケーションがアクティブなときにメッセージを受信することができません。

{"aps":{
  "alert":"Notification Hub test notification",
  "content-available": 1
}}

.NET MAUI でプッシュ通知を実装する(Android 編)

表題の通り .NET MAUI でプッシュ通知を実装してみます。

(案の定というか).NET MAUI でのプッシュ通知に関するドキュメントが見つからないということもあり、Xamarin.Forms のドキュメントを参考にしながらやってみます。

learn.microsoft.com

やるべきこととしては以下のようになります。

1. Firebase プロジェクトを作成し、Firebase Cloud Messaging を有効にする

Firebase プロジェクトを作成します。ここでのプロジェクト名は任意で構いません。

Android パッケージ名を入力してアプリ登録をおこないます。

ここで入力した Android パッケージ名は .NET MAUI でも使用しますのでメモしておきます(後述)。

google-services.json をダウンロードします。このファイルは .NET MAUI で使用します(後述)。

以降は Java での実装なのでここでの作業はこれで終わりです。

プロジェクトの先頭に戻り、「プロジェクトの設定」(「プロジェクトの概要」の隣にあるタイヤアイコン)→「Cloud Messageing」と進みます。

「Cloud Messaging(レガシー)」の右側にある三点ボタンをクリックして、Cloud Messaging を有効にします。

(リロードすると Cloud Messaging が有効になるので)「サーバーキーを追加」をクリックします。ここでのサーバーキーは Notification Hubs の設定で使用しますのでメモしておきます。

2. 通知ハブ(Notification Hubs)を作成する

Azure ポータルから Notification Hub を検索して作成します。

ハブ名は .NET MAUI で使用しますのでメモしておきます。

Notification Hub の「Access Policies」から「DefaultListenSharedAccessSignature」(Permission=Listen)の値をメモしておきます。この後 .NET MAUI のプロジェクトで使用します。

3. FCM の設定を通知ハブ用に構成する

Notification Hubs で「Google (GCM/FCM)」 を選択します。

先ほど Firebase Console でメモしておいた API キーをペーストして保存します。

4. Visual Studio プロジェクトを作成し、NuGet パッケージを追加する

Visual Studio 2022 を起動して「新しいプロジェクトの作成」を選択します。

"maui" で検索するなどして、「.NET MAUI アプリ」を作成します。

プロジェクトのプロパティを選択して、「Andoird >マニフェスト」を選択します。

Firebase Console でメモしておいた「アプリケーションパッケージ名」をペーストします。アプリケーション名は任意です。

ここで設定した「アプリケーションパッケージ名」は Platforms/Android/AndroidManifest.xml へ反映されますが、このファイルも同時にプロジェクトで開いていると反映されないケースがあるようです。

パッケージを二つインストールします。バージョンは 2022年10月28日時点のものです。

<PackageReference Include="Xamarin.Azure.NotificationHubs.Android" Version="1.1.4.1" />
<PackageReference Include="Xamarin.Firebase.Messaging" Version="123.0.8" />

Android の Framework のバージョンを 30 にしておきます。

5. Google Services JSON ファイルを追加する

Google Firebase Console からダウンロードした google-services.json ファイルをプロジェクト フォルダーにコピーします。

ソリューション エクスプローラーで google-services.json を選択して、プロパティでビルド アクションを GoogleServicesJson に設定します。

パッケージが正常にインストールされていないと GoogleServicesJson が選択できない場合があります。Visual Studio を再起動してプロジェクトを再度開くと表示されることもあります。

6. プロジェクトで Notification Hubs を設定する

<application> 要素の後ろに(前でも良いですが)、以下のステートメントを追加します。

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>

プロジェクトで下記の Constants.cs クラスを作成します。プレースホルダーの値を実際の値に置き換えます。

ListenConnectionString は「Access Policies」の「DefaultListenSharedAccessSignature」で取得した値、NotificationHubName は Notification Hubs で作成したハブ名になります。

public static class Constants
{
    public const string ListenConnectionString = "<Listen connection string>";
    public const string NotificationHubName = "<hub name>";
}

下記の FirebaseService クラスを Platforms/Android フォルダに追加します。

OnNewToken メソッドはアプリの初回起動時に呼ばれます。ここで取得できるトークンをつかって Notification Hubs への登録をおこないます。

プッシュ通知を受信すると OnMessageReceived メソッドが呼ばれます。ここではアプリがバックグラウンドの状態の時のローカル通知用の設定(SendLocalNotification)と、アプリが起動している場合は MainPage へメッセージを追加する処理(SendMessageToMainPage)をおこなっています。

using Android.App;
using Android.Content;
using Android.Util;
using AndroidX.Core.App;
using Firebase.Messaging;
using WindowsAzure.Messaging;

namespace MauiPush.Platforms.Android;

[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class FirebaseService : FirebaseMessagingService
{
    public override void OnNewToken(string token)
    {
        SendRegistrationToServer(token);
    }

    public override void OnMessageReceived(RemoteMessage message)
    {
        base.OnMessageReceived(message);
        string messageBody = string.Empty;

        if (message.GetNotification() != null)
        {
            messageBody = message.GetNotification().Body;
        }
        else
        {
            messageBody = message.Data.Values.First();
        }

        // convert the incoming message to a local notification
        SendLocalNotification(messageBody);

        // send the incoming message directly to the MainPage
        SendMessageToMainPage(messageBody);
    }

    private void SendLocalNotification(string body)
    {
        try
        {
            var intent = new Intent(this, typeof(MainActivity));
            intent.AddFlags(ActivityFlags.ClearTop);
            intent.PutExtra("message", body);

            var requestCode = new Random().Next();
            var pendingIntent = PendingIntent.GetActivity(this, requestCode, intent, PendingIntentFlags.OneShot);

            var notificationBuilder = new NotificationCompat.Builder(this, Constants.NotificationChannelId)
                .SetContentTitle("IDEQ Alert")
                .SetSmallIcon(Resource.Drawable.ic_launcher)
                .SetContentText(body)
                .SetAutoCancel(true)
                .SetShowWhen(false)
                .SetContentIntent(pendingIntent);

            var notificationManager = NotificationManagerCompat.From(this);
            notificationManager.Notify(0, notificationBuilder.Build());
        }
        catch (Exception e)
        {
            Log.Error("firebase", e.ToString());
        }
    }

    void SendMessageToMainPage(string body)
    {
        var appShell = App.Current.MainPage as AppShell;
        var mainPage = appShell.CurrentPage as MainPage;

        mainPage.AddMessage(body);

        Log.Debug("firebase", $"Message:{body}");
    }

    void SendRegistrationToServer(string token)
    {
        try
        {
            NotificationHub hub = new NotificationHub(Constants.NotificationHubName, Constants.ListenConnectionString, this);

            // register device with Azure Notification Hub using the token from FCM
            Registration registration = hub.Register(token, Constants.SubscriptionTags);

        }
        catch (Exception e)
        {
            Log.Error("firebase", $"Error registering device: {e.Message}");
        }
    }
}

プッシュ通知を受信した時に表示するコントロールMainPage.xaml に追加します。今回は <Button> の下に追加しました。

<StackLayout x:Name="messageDisplay"
             HorizontalOptions="Center"
             VerticalOptions="Start">
</StackLayout>

これで実装ができたので実機を使ってデバッグします。

7. Azure Portal からテスト通知を送信する

アプリの通知の受信をテストするには、Azure Portal の「テスト送信(Test Send)」オプションを使用します。 プラットフォームで「Android」を選択して「Send」をクリックします。

Androidバイスが正常に登録されていれば、送信結果に登録された Registration Id が出力されます。

アプリが起動されていればプッシュ通知を受信する都度メッセージが追加されます。

アプリがバックグラウンドにいる場合はローカル通知で受信します。

次回は .NET MAUI の iOS でのプッシュ通知をやってみます。

コンソールアプリで Azure AD B2C の ROPC フローを試してみた

コンソールアプリを使って Azure AD B2C の ROPC フローを試してみました。

ROPC フローとは

Azure AD B2C は通常はブラウザや WebView を介してサインインをおこないますが、ROPC フローはユーザー名とパスワードを直接処理することでユーザーをサインインすることができるフローです。

また、ROPC フローは作成済みのユーザー情報を使ってサインイン、サインアウトをすることはできますが、新規のユーザーを作成することはできません。

docs.microsoft.com

ROPC フローはサインイン時にユーザー情報がむき出しになるなどの理由から非推奨となっているので注意が必要です。

ROPC フローは "使用しない" ことをお勧めします。 ほとんどのシナリオでは、より安全な代替手段を利用でき、推奨されます。 このフローでは、アプリケーションで非常に高い信頼度が要求されるため、他のフローには存在しないリスクが伴います。 他のもっと安全なフローを使用できない場合にのみ、このフローを使用する必要があります。

(補足)コンソールアプリではブラウザを使った認証を使用できない(ただし Graph API の場合使用可能)

Azure AD B2C のコンソールアプリは http://localhostURI へリダイレクトされるため、ブラウザや WebView を使用する(「サインアップとサインイン」などの)フローの認証を使用することができません。

アプリの登録時に、http://localhost をリダイレクト URI として構成します (B2C では現在サポートされていません)

docs.microsoft.com

同じ MSAL ライブラリを使う場合でも、Graph API の場合はブラウザによる認証を使ったコンソールアプリを作成することができます。

github.com

Azure AD B2C の環境を作成する

ということで、Azure AD B2C で ROPC フローの環境を作成していきます。

docs.microsoft.com

Azure AD B2C のテナントを作成する

(ドキュメントにある Azure AD B2C を先に作るより)リソースグループから Azure AD B2C テナントを作成すると、テナントへのリンクが減る分少し簡単になります。

docs.microsoft.com

アプリの登録をする

作成した Azure AD B2C テナントから「アプリの登録」→「新規登録」でアプリケーションを登録します。

「認証」を選択して、「アクセス トークン (暗黙的なフローに使用)」にチェック、「パブリック クライアント フローを許可する」で「はい」を選択します。

f:id:masatoru:20210306210431p:plain

ユーザーフローを作成する

テナントに戻り、「新しいユーザーフフロー」→「リソース所有者のパスワード資格情報 (ROPC) を使用してサインインする」→「作成」を選択します。

f:id:masatoru:20210306223322p:plain

ユーザーフロー名を入力して「作成」を選択します。

MSAL で使う場合は、( Scope にアプリケーションIDを指定する方法が機能しないので)API を公開してスコープに追加する必要があるのでさらに下記を実行します。

  • API の公開」→「Scope の追加」→「スコープ名」を api(任意)で「スコープの追加」を選択

  • API のアクセス許可」→「自分の API」→「アクセス許可の追加」

  • 「"組織"名に管理者の同意を与えます」→「はい」

C# で使用するときに)スコープ名に https://{テナント名}.onmicrosoft.com/{クライアントID}/{作成したスコープ名} を追加します。

The identifier URI of the backend App Registration and scope name are concatenated to obtain the scope used in the next step.

github.com

C# で動かしてみる

C# と MSAL ライブラリーを使用して、作成した ROPC フローでサインインをおこないます。 今回のサンプルはこちらに置いてあります。 github.com

入力画面に Sharpromt を使う

メールアドレスとパスワードの入力は @shibayanSharpromt を使ってます。Sharpromt を使うと簡単にコンソールでの入力画面を作成できます。

var username = Sharprompt.Prompt.Input<string>("Input username.");
var password = Sharprompt.Prompt.Password("Input password.");

実行するとこんな感じです。

f:id:masatoru:20210306193920p:plain

github.com

MSAL を使ってサインインを実装する

MSAL ライブラリーを使ってサインインをする場合、

  1. Azure AD B2C の情報から IPublicClientApplication を作成する

  2. キャッシュがあればそっちでログインを試みる

  3. キャッシュがなければ(例外が発生するので)通常でログインする

  4. 成功すれば、AuthenticationResult から id token を取得する

という流れになります。

App = PublicClientApplicationBuilder.Create(ClientID)
    .WithB2CAuthority(Authority)
    .Build();

AuthenticationResult result = null;
var accounts = await App.GetAccountsAsync();
var account = accounts.FirstOrDefault();

try
{
    result = await App.AcquireTokenSilent(Scopes, account)
        .ExecuteAsync();
}
catch (MsalUiRequiredException)
{
    try
    {
        result = await App.AcquireTokenByUsernamePassword(Scopes, username, passwordText)
            .ExecuteAsync();
    }
    catch (MsalUiRequiredException ex)
    {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine(ex.Message);
        Console.ResetColor();
    }
    catch (MsalUiRequiredException ex)
    {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine(ex.Message);
        Console.ResetColor();
    }
    // AADB2C90225: The username or password provided in the request are invalid.
    catch (MsalServiceException ex)
    {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine(ex.Message);
        Console.ResetColor();
    }
}

// Cache empty or no token for account in the cache, attempt by username/password
if (result != null)
{
    Console.WriteLine($"IdToken: {result.IdToken}");
    Console.WriteLine($"AccessToken: {result.AccessToken}");
}

実行結果です。

f:id:masatoru:20210306211311p:plain

ちなみに、サンプルを実行して Fiddler で覗いて見るとがっつりパスワードが見えちゃってます(のでくれぐれもご注意ください)。

f:id:masatoru:20210306202504p:plain

最後に

ROPC フローはあくまでも非推奨となっていますが、この仕様を理解した上で手軽に社内ツールを作成する場合などは有効な方法かなと思います。

それでは、Happy B2C Life!!

決済サイトが簡単に作れちゃったのでそのご報告

ハッカソンに参加してきました

Hack Azure! #1 - Azure Serverless でオンライン決済基盤をつくる というハッカソンに参加してきました。決済サイトの構築なんて未知の世界過ぎて自分には縁がないと思ってたのが、本当に短時間で作れてしまったので感動して(珍しく)ブログ書いてます。

事前にチームのメンバーに Teams や Azure や Stripe や GitHub のアカウントが割り当ててあるので、僕らは Teams でコミュニケーションを取りながら黙々とコーディングをすればよいという感じになっています(この辺の運営スタッフの事前準備が素晴らしかったです)。

msdevjp.connpass.com

Stripe って何?

Stripe とはクレジットカードの支払い方法や商品ごとの金額設定やサブスクリプション方式での支払いなど、決済に関する面倒くさいところをおまかせにできちゃう大変便利なサービスです。

しかも Stripe は API が充実しているので、今回は .NETAPI を使い倒して自前の API を作ろうという主旨のハッカソンでした(今回は時間不足と Stripe への理解不足で使い倒すところまではたどりつきませんでしたが)。

stripe.com

何を作ろう

ramen チームということでラーメンをクリックすると Stripe の決済画面に遷移して購入することができるサイトを作ることに決定。

チーム内に Stripe への理解が深い @myfinder さん主導のもと、僕らが Stripe への基本的な理解を深めるにはやっぱり見た目がある方がいいよねということで、ASP .Net Core Web アプリケーションを選択しました。

最終的に完成したのが下の画面で、各ボタンをクリックすると購入画面に遷移する感じです。

f:id:masatoru:20200627190139p:plain

商品を登録する

まずは商品としてのラーメンを揃えようということで、 Stripe のダッシュボードの商品ページからポチポチ商品とイメージを追加してきます。

f:id:masatoru:20200627200653p:plain

商品にそれぞれ価格を設定していきます。1つの商品に複数の価格を設定することや、クーポンを使ってある期間は何パーセント引きというような設定も可能です。

f:id:masatoru:20200627200803p:plain

商品ごとに価格を設定すると price_xxxx という ID が発行されるので、これを実際のサイトに紐づけて Stripe のライブラリを呼び出す(それはまあなんとなくわかる)のですが、さてどうやって JavaScript を書くんだよと。

JavaScript は書かない!?

事前に Stripe の [ダッシュボード]→[設定]→[Checkout の設定] で [ドメイン] と [Webhook] の設定をしておくと、先ほど登録した商品の価格ごとに [Checkout のコードスニペットを取得] を実行できます。

これをクリックするとコードスニペットジェネレータが <button> の HTML と Stripe への購入画面に遷移する JavaScript を生成してくれるので、OK と NG の時の URL を指定して、これをコピペすればとりあえず実装できるという JavaScript が書けない僕にとって非常にありがたい機能です。

f:id:masatoru:20200627200857p:plain

実際にコピペしたページをクリックすると Stripe 側で事前に用意されている購入画面へ遷移します。

もちろん自前の決済ページに Stripe の機能を組み込むこともできます(みたいです)。クレジットカードはテスト用のクレジットカード番号を使用してテストをおこないます。

f:id:masatoru:20200627200943p:plain

ここまで、.NET 全然使ってないよねということで、時間が足りなかったので事前に登録した画像の URL を取得するところを .NET で書きましたが、これもドキュメントが API キー付きで生成してくれるのでコピペする感じでできちゃいます。

// Stripe の API キーはダッシュボードから取得
// 今回はそれを環境変数に保存しておいて使う
StripeConfiguration.ApiKey = Environment.GetEnvironmentVariable("StripeApiKey");
var options = new ProductListOptions
{
    Limit = 10,
};
var service = new ProductService();
StripeList<Product> products = service.List(
    options
);

// 時間が最後足りなかったので登録されている先頭の画像を表示
var imageUrl = products.Data[0].Images[0];
ViewData["imageUrl"] = imageUrl;

お買い上げありがとうございますと同時に画像を表示するようにしました。

f:id:masatoru:20200627202051p:plain

ということで、本当に決済サイトが完成しちゃいました。

今回の ramen チームのリポジトリですのでよければ参照してください。

最後に

本来のハッカソンの主旨である API を実装するところまでは事前知識が足りなかったので間に合いませんでしたが、もう一回この時間があればどんな API を作ろうかというところまで知見を得られたかなと感じています。

何より知らない人とチームを組んでオンラインでハッカソンをおこなうという新しくて楽しい体験ができたことにとても感謝しています。ありがとうございました。

ハッカソンはいいぞ!!

Xamarin.Forms で使える Azure AD 認証付きの WebAPI を実装するには

この記事は Xamarin Advent Calendar 2019 の 9 日目の記事です。

タイトルを実装するのにこれといったベストプラクティスが見つからずになかなかハマりました。

というテーマで、昨日登壇させていただいたのでその最後のベストプラクティスを手順にしてみました。

www.slideshare.net

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

Azure Portal から Azure AD のアプリケーションを登録します。

f:id:masatoru:20191201112114p:plain

  • 概要でアプリケーションID(クライアントID)とディレクトリ(テナントID)をコピペします(後で使います)

  • 認証→リダイレクトURImsal9999...://auth とあるところにチェックを入れて→保存をクリック これが Xamarin 用のリダイレクト URI になります。

f:id:masatoru:20191201113621p:plain

  • 証明書とシークレットで、新しいクライアントシークレットをクリック→任意の名前(secret など)→有効期限なし→追加 これをコピペします。

AppService に認証(EasyAuth)を設定する

AppService は作成済みとして、ポータルから AppService のサイトに移動します。

  • 認証/承認をクリック
  • App Service 認証を ON
  • Azure Active Directory をクリック
  • クライアント ID 、クライアントシークレット(上記で取得済み)をコピペ
  • 発行者の URL はテナント ID (上記で取得済み)を使って以下をコピペ https://login.microsoftonline.com/{テナントID}/v2.0/.well-known/openid-configuration
  • 許可されるトークン対象ユーザーは以下の形式でコピペ https://{Your AppService}.azurewebsites.net/.auth/login/aad/callback
  • OK をクリック
  • 要求が承認されない場合に実行するアクション→ Azure Active Directory でのログイン を選択(忘れやすいので注意!) → 保存をクリック

f:id:masatoru:20191210085138p:plain

Xamarin.Forms にログインを実装する

ログイン画面を実装して API で使用するアクセストークンを取得します。

比較的簡単に実装できる AppCenter Auth は Azure AD B2C のみ対応なので(2019年12月1日現在)使えない(ちなみにロードマップには Azure AD があるので近々対応されるはず)。

となると、Xamarin.Forms(以下 Xamarin ) の場合 MSAL(Microsoft Authentication Library)一択になるわけですが、最初はログイン画面に遷移して2回目以降はキャッシュを取得して画面遷移しないみたいな感じで書きます。この時、ログイン情報をキャッシュするとか有効期限を更新するとかはライブラリにお任せです。

MSAL は nuget から Microsoft.Identity.Client(バージョンは 4.1.0 )で取得します。

using Microsoft.Identity.Client;
// ログインしてアクセストークンを取得する
private async string SignInAsync()
{
  AuthenticationResult authResult = null;
  try
  {
    // キャッシュにあればログイン画面に遷移しない
    IAccount firstAccount = accounts.FirstOrDefault();
    authResult = await App.PCA.AcquireTokenSilent(App.Scopes, firstAccount)
                          .ExecuteAsync();
  }
  catch (MsalUiRequiredException ex)
  {
    try
    {
      // なければログイン画面に遷移する
      authResult = await App.PCA.AcquireTokenInteractive(App.Scopes)
                                  .WithParentActivityOrWindow(App.ParentWindow)
                                  .ExecuteAsync();
    }
    catch (Exception ex2)
    {
    }
  }
  return authResult?.AccessToken;
}

Xamarin で認証付き WebAPI をたたく

取得したアクセストークンを Bearer で渡して WebAPI を叩きます。

最近は HttpClientusing で囲うと犯罪者扱いされるみたいですが...(ry

using (var client = new HttpClient())
{
  // アクセストークンを使ってAPIをたたく
  client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
  var res = await client.GetAsync(apiUrl);

  if (!res.IsSuccessStatusCode)
  {
    throw new Exception(res.ToString());
  }
  // 結果のJSONを受け取る
  var resultText = await res.Content.ReadAsStringAsync();
...

Azure AD の認証付き WebAPI を実装する

VSCodeASP.Net Core で Azure AD 認証付きの Web API を作成します。

azure-v2-app というアプリを作る場合、コンソールから以下をたたきます。

mkdir azure-v2-app
cd  azure-v2-app
dotnet new webapi
code -r .

VSCode が起動するので、表示→ターミナルを開いて、

dotnet build
dotnet run

これで VSCode がローカルでサーバーを実行している状態になるので、ブラウザや POSTMAN などで、

https://localhost:5001/WeatherForecast

とたたくと JSON が返ってくるはずです。これを Azure の AppService にデプロイします。

  • VSCode拡張機能で Azure App Service を追加します(入れてない場合は)。
  • VSCode の左側の Azure のロゴをクリックして、デプロイする App Service を選択します
  • 右クリックで Deploy to Web App をクリックするとデプロイされます(初回はいくつか質問されますがそのまま OK をクリック)。

API を POSTMAN でテストする

デプロイした WebAPI は EasyAuth で認証されたサイトになっているので、そのままたたくと401( Unauthorized )が返ってきます。 これにトークンを設定して 200 が返ってくれば OK ということになります。

まずはブラウザを使って id_token を取得します。 - 作成したサイトが https://azuread-v2-app.azurewebsites.net であれば、https://azuread-v2-app.azurewebsites.net/WeatherForecast にブラウザでアクセスします - ログインされていなければ Azure AD のログイン画面に遷移します。ログイン済みであれば白い画面が表示されます - 同じ画面で、https://azuread-v2-app.azurewebsites.net/.auth/me とたたくとトークンが取得できるので id_token をコピーします

f:id:masatoru:20191210083212p:plain

次に POSTMAN を使って認証済みのサイトにアクセスしてみます。 - POSTMAN で URL に https://azuread-v2-app.azurewebsites.net/WeatherForecast をコピペ - Authorization → Bearer Token → id_token をコピペ - Send をクリックして ステータスが 200(OK) と JSON が返ってくればOKです。

取得したアクセストークンを調べる

取得した id_tokenaccess_tokenhttps://jwt.ms/ にコピペするとトークンの中身を確認することができます。

MSAL で Azure AD の認証を行う場合 V2 のエンドポイントである必要があるので、 isshttps://login.microsoftonline.com/{テナントID}/v2.0 であることを確認します。ちなみに、V1 のエンドポイントは https://sts.windows.net/{テナントID}/

Xamarin.Forms のソースコード

これで Xamarin.Forms から 認証付き WebAPI をたたけるようになったはずです。あとで Xamarin.Forms のサンプルを GitHub にあげます。

さいごに

401(Unauhtorized)なんて怖くない!より良い認証ライフを!!

App Center Data のオフライン同期を試してみた

App Center Data(現在プレビュー版)のオフライン同期が最近できるようになった(今回リストにも対応した)ので Xamarin.Forms で試してみました。

以下の記事を参考にしてサンプルアプリを作成しました。作成したサンプルの GitHub のリンク先も最後に掲載しておきます(現在修正中なので後日必ず)。

docs.microsoft.com

App Center Data とは

App Center Data を使用することで Cosmos DB とアプリを比較的少ないコード量で簡単につなげることができます。

docs.microsoft.com

さらに App Center Auth を組み合わせることでユーザー別のデータベースをアプリに実装することができます。App Center Data は App Center Auth 無しでも使用することはできますが、どちらかというと App Center Auth と組み合わせて使うことを前提としている感じです。

f:id:masatoru:20190803185115p:plain

オフライン同期とは

(英語ではただの offline ですが)「オフライン同期」とはデバイスがネットにつながっていない状態で編集(追加や削除)したデータをローカルにキャッシュしておき、ネットにつながった時にクラウド側のデータベース(今回の場合は Cosmos DB)と同期する機能です。App Center Data を使うとここら辺をいい感じでやってくれる(であろうことをここでは期待している)という便利機能です。

以下はデータベースの一覧を取得しているところですが、この1行だけで"オフライン同期"をいい感じでやってくれます(追加や削除などももちろん同様に)。

var result = await Data.ListAsync<TodoItem>(DefaultPartitions.UserDocuments);

オフライン同期を試す

ネットがつながっている状態で追加をおこなうと Cosmos DB と同期されます。試しに5件追加してみたところ、Azure ポータルの Data Explorer で確認をすると5件分のデータが確認できます。

f:id:masatoru:20190803225726g:plain

f:id:masatoru:20190804005110p:plain

これをデバイス側のネットを切断してデータを追加してみます。アプリ上では追加されましたが Cosmos DB の件数は5件のままです。

f:id:masatoru:20190804005654p:plain

f:id:masatoru:20190804005239p:plain

この状態からデバイスをネットをつなげると Cosmos DB 側と同期されました。

f:id:masatoru:20190804005506p:plain

正確には、

  1. OnAppearing(ページの再描画)が呼ばれる
  2. Data.ListAsync が呼ばれる
  3. (Data.ListAsync の内部で)キャッシュされたローカルのデータをクラウド(Cosmos DB)と同期する

という仕組みのようです。

ローカルの DB は何?

ローカルにキャッシュする時の DB のフォーマットは SQLite を使用しているみたいです。

The Data service supports both writing data offline with a simple store and forward model and reading information offline with a built-in SQLite cache.

devblogs.microsoft.com

最後に

わずかなコード量でオフライン同期が実装できるというのはそれだけで魅力ではあります(少なくともやってみようという気にさせてくれるという意味で)。

App Center Data に限らず Push や Auth など全般的に言えることなのですが、このようにコード量を極力少なく実装できるのが App Center SDK の特徴でもあります。このことは敷居が低いという一方で、細かいハンドリングができないとも言えますが...。

github.com

(おまけ)App Center の今後

App Center は今後多くの新機能が予定されています(以下に抜粋)。興味ある方はこの動画をぜひ見てみてください。

  • JWT Access (Auth)
  • Off-line List Support (Data) ... この記事で紹介
  • Third Party Identity Providers (Auth)
  • Enterprise Support (Auth)
  • View UserProfile Details (Auth)
  • Metrics / Browser / Free Tier (Data)
  • Notification Hubs V2 (Push)
  • Integrate Notification Hub (Push)
  • Full Offline Support (Data)
  • Realtime Updates/Syncing (Data)
  • User Collaboration (Data)
  • Partial Updates (Data)
  • File Storage (Application Services)
  • Compute (Application Services)

www.youtube.com

以上です。 App Center はいいぞ。

Xamarin.Forms で App Center のプッシュ通知を使ってみる

この記事は Xamarin Advent Calendar 2018 の 19 日目の記事です。

App Center の Push Notifications と Xamarin.Forms の Android を使用してプッシュ通知を送受信する方法を説明します。App Center の Push Notifications は Preview 版(2018.12.19 現在)ですのでご注意ください。

Xamarin.Forms でプッシュ通知を実装する方法

Xamarin.Forms でプッシュ通知を実装するには以下の方法があります。

プッシュ通知の仕組みは、プラットフォーム通知システム(以下 PNS: Platform Notification System )と呼ばれるプラットフォーム独自のインフラストラクチャを利用してプッシュ通知を配信します。 PNS は Android であれば Firebase Cloud Messaging (FCM)、iOS であれば Apple Push Notification Service (APNS) を使用することになります。

他にも Azure Notification Hubs を使用しないで直接各 PNS とプッシュ通知のやり取りをする方法や、プッシュ通知に似てるものとして Firebase In-App Messaging というのもあります。Firebase In-App Messaging はアプリに事前に URL スキームを埋め込んでおき、Firebase からその URL に対してメッセージを送信するとそのアプリがメッセージを受信するという仕組みです。

Azure Mobile Apps SDK を使用する方法は App Service の URL を指定するだけで各種の接続をまかなってくれて扱いやすいのですが、開発は止まってるぽいです。1

Azure Notification Hubs は ネイティブ側(iOS / Android)にコードを実装する必要があるのですが、App Center の Push Notifications は共通プロジェクトの App クラスにちょろちょろっと書くだけなのでずいぶん簡単になっています(それでも事前に設定することがいろいろありますが)。

Firebase Cloud Messaging の設定

今回は Android を使ってプッシュ通知をおこなうので、Firebase Cloud Messaging でのプロジェクトとアプリの登録が必要になります。

  1. Firebase にログインする
  2. 右上の「コンソールへ移動」をクリック
  3. プロジェクトを追加する
    f:id:masatoru:20181218183333p:plain
  4. 左上の設定をクリック
  5. 「プロジェクトの設定」をクリック
    f:id:masatoru:20181218183617p:plain
  6. Android アプリに Firebase を追加」をクリック f:id:masatoru:20181219122016p:plain
  7. 「アプリの登録」の「 Android パッケージ名」は Xamarin の Android プロジェクト→プロパティ→ Android マニフェスト→パッケージ名 をコピペします
    f:id:masatoru:20181219122502p:plain
  8. 「アプリを登録」をクリックすると google-services.json をダウンロードできるのでこれを保存しておきます
  9. JAVAに関するところは Xamarin には関係ないので「次へ」で無視

これでアプリ登録が完了しました。

サーバーキーの取得

設定→クラウドメッセージングの「サーバーキー」をコピペして保存しておきます。

App Center でアプリケーションを追加する

App Center にログインして All apps から右上の Add newAdd new app をクリックします。

「アプリケーション名」の入力と、OS は Android、Platform は Xamarin を指定します。

f:id:masatoru:20181219115000p:plain

作成したアプリケーションから Push → Notifications をクリックします。 ここに移行の実装手順が記載されています。

  1. Xamarin.Forms での実装手順
  2. (Firebase の設定と)Android プロジェクトへの記述
  3. Firebase のサーバーキーのコピペ

上記手順を以下進めていきます。

Xamarin.Forms にプッシュ通知を実装する

.NET Standard で Xamarin.Forms のプロジェクトを作成します。

パッケージマネージャーから Microsoft.AppCenter.Push をすべてのプロジェクトにインストールします。バージョンは 1.11.0(2018.12.15現在)です。

App.xaml.cs にコードを追加していきます。まずは using を追加します。

using Microsoft.AppCenter.Push

同様に App.xaml.csOnStart() メソッドに 以下を追加します。上記の Push → Notifications のページに記載されているのでそちらをコピペしてください。

protected override void OnStart()
{
  AppCenter.Start("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", typeof(Push));
  Push.PushNotificationReceived += this.Push_PushNotificationReceived;
}

プッシュ通知を受信したときに Push_PushNotificationReceived イベントが呼ばれるので、受信した内容を表示するようにします。

private async void Push_PushNotificationReceived(object sender, PushNotificationReceivedEventArgs e)
{
    await this.MainPage.DisplayAlert(e.Title, e.Message, "OK");
}

Android プロジェクトへ追記する

Android プロジェクトの PropertiesAndroidManifest.xml ファイルの &lt;application>...&lt;/application> タグ内に以下を追加します。

<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>

追加後は以下のようになります。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.company.PushAppCenterSample" android:installLocation="auto">
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="27" />
  <application android:label="PushAppCenterSample.Android">
    <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
    <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
      <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="${applicationId}" />
      </intent-filter>
    </receiver>
  </application>
</manifest>

Fireabase でダウンロードした google-service.jsonAndroid プロジェクトにコピーして、「ビルドアクション」を GoogleServicesJson に設定します。

選択できない場合は一度プロジェクトを保存して Visual Studio を終わらせて、再度プロジェクトを開くと表示されます。

f:id:masatoru:20181219123640p:plain

Firebase のサーバーキーのコピペ

上記で Firebase の設定で取得したサーバーキーをここにコピペします。

以上で App Center の設定 と Xamarin.Forms の実装は完了です。

プッシュ通知の送信テストをする

送信テストは App Center のサイトの Notifications ページにある Send Notification ボタンからおこないます。

f:id:masatoru:20181218160145p:plain

Compose で送信するメッセージを指定します。Cmapaign Name, Message が必須で、Title, Custom Data はオプションになっています。Custom data は Key/Valueで値を送信することができます(後述)。

f:id:masatoru:20181218161842p:plain

Target でどのデバイスに送信するかを決定します。

  • All registerd devices - 登録されているすべてのデバイスに送信します。
  • Custom device list - 指定したデバイスに最大20個まで一度の送信することができます。
  • Audience - 国や言語、デバイスのバージョンなどを指定して送信することができます。

f:id:masatoru:20181218161802p:plain

最後に Review で送信する内容を確認して Send notification クリックします。

f:id:masatoru:20181218162629p:plain

うまくいけばデバイスにプッシュ通知が到達しメッセージが表示されます。これまでの経験ではいずれも1秒かからずに到達する感じです。

f:id:masatoru:20181219215210p:plain

Swagger を使ってプッシュ通知を送信する

REST API を使用してプッシュ通知を送信することもできます。使用できる API が OpenAPI(旧Swagger) で定義されています。

API を使用するためには API トークンが必要になります。API トークンは(アプリの設定ではなく) App Center の設定(右上の自分のアイコン)→ Account Settings → API Tokens → New API Token で発行できます。

f:id:masatoru:20181219173514p:plain

Swagger UI を使ってプッシュ通知のメッセージを送信してみます。プッシュ通知の API一覧 の右上にある Authorize をクリックして取得した API トークンを APIToken(apiKey) にコピペします。

f:id:masatoru:20181219180146p:plain

プッシュ通知の API一覧 から POST を使用します。

f:id:masatoru:20181219193918p:plain

[Try it out] をクリックすると値を編集できるので、ここに値を入力していきます。 すべてのデバイスに送信する場合は、"notification_target": null になります。

Example value

{
  "notification_content": {
    "name": "プッシュ通知のテスト",
    "title": "プッシュ通知のタイトル",
    "body": "プッシュ通知のメッセージ"
  },
    "notification_target" : null
}

The name of the owner にオーナー名、The name of the application にアプリ名を入力して、Execute をクリックします。成功すれば notification_id と値が JSON が返ります。

特定のデバイスにプッシュ通知をおこなう

特定のデバイスにプッシュ通知をおこなう場合は、notification_target に対象のデバイス ID を指定します。最大で同時に 20 台まで送信できます。

Example value

{
  "notification_content": {
    "name": "プッシュ通知のテスト",
    "title": "プッシュ通知のタイトル",
    "body": "プッシュ通知のメッセージ"
  },
  "notification_target":  {
    "type": "devices_target",
    "devices": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"],
  }
}

バイス ID は、App Center の SDK を使用して AppCenter.GetInstallIdAsync(); で取得することができます。

Custom Data を使ってプッシュ通知をおこなう

送信する JSONcustom_data を付加すると Key/Value の値を送信することができます。

Example value

{
  "notification_content": {
    "name": "プッシュ通知のテスト",
    "title": "プッシュ通知のタイトル",
    "body": "プッシュ通知のメッセージ",
    "custom_data": {
      "dinner": "beer"
    },
  },
  "notification_target":  {
    "type": "devices_target",
    "devices": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"],
  }
}

バイス側で受け取る方法は、Push_PushNotificationReceived イベントの引数の PushNotificationReceivedEventArgs クラスの CustomData プロパティに Key/Value 値が入っているので、これを使って分岐処理などをおこなうことができます。

private async void Push_PushNotificationReceived(object sender, PushNotificationReceivedEventArgs e)
{
    if (e.CustomData != null && e.CustomData.ContainsKey("dinner"))
    {
        var action = e.CustomData["dinner"];
        switch (action)
        {
            case "beer":
                await this.MainPage.DisplayAlert("Sample", "また飲むのか!", "OK");
                break;
            case "beef":
                await this.MainPage.DisplayAlert("Sample", "焼肉いいね!", "OK");
                break;
        }
    }
}

まとめ

プッシュ通知は環境の設定や実機が必要などめんどくさいイメージがありますが、App Center の Push Notifications は(これまでに比べて)実装が簡単で、かつ安定してプッシュ通知を受信してくれるので、それほどストレスなく使えると思います。ぜひ一度お試しください。

明日は @yu_ka1984 さんです。よろしくお願いします!