Matthew Tse
Site Reliability Engineer

Database 全体を定期的にエクスポートするプライベート バックアップは、バックアップやスクリプティング、アナリティクスの収集のための優れたソリューションです。以前は、セットアップの際にユーザーと Firebase サポートの間でいくらかのやり取りが欠かせませんでしたが、現在は、Blaze ユーザー向けに Firebase コンソールでセルフサービス ユーザー インターフェースが提供されており、Firebase Database をシームレスに日次で Google Cloud Storage のバケットにバックアップできます。

この機能を使うには、Firebase コンソールの [Database] セクションから新しい [Backups] タブにアクセスします。すると、ウィザードが自動バックアップの設定をガイドしてくれます。


ストレージのコストを節約するために、デフォルトで Gzip 圧縮 が有効になっています。また、30 日以上古いバックアップを自動的に削除する 30 日ライフサイクル ポリシーを選べるようにもなっています。


セットアップ プロセスが完了すると、Google Cloud Storage バケットが使えるようになり、Database のアプリケーション データとルールが JSON 形式でバケットに毎日バックアップされます。すべてのバックアップのステータスや履歴は、Firebase コンソールから直接参照できます。また、即座に現在のデータベースとルールのバックアップをキューに入れる [Manual Backup] ボタンもついています。これは、特定の時間のスナップショットをとったり、コードの変更を行う前の安全策としてとても便利です。


ここで、今日から自動バックアップを使い始めるべき 3 つの理由をあげましょう。

オフライン スクリプティング

Automatic Database Backups では、 Database に接続しているユーザーのリアルタイム パフォーマンスには影響を与えずに、データに対してクロール、分析などの大規模なスクリプト アクションを実行しています。

履歴データ分析

Database のスナップショットの履歴があれば、データをさかのぼって分析やトレンドの検索を行ったり、過去のデータポイントに基づいて製品に関する意思決定を行ったりできます。

破損データやデータロストからのリカバリ

Automatic Database Backups では、データが破損したり失われた場合でも、簡単に復旧できます。Database を復旧するには、[Backups] タブからローカルマシンにバックアップ ファイルをダウンロード1し、[Data] タブの [Import JSON] ボタン2をクリックして、Database を以前にバックアップした際の状態に戻します。


私たちは、Firebase デベロッパーの皆様の作業を効率化することに取り組み続けています。この新しいリリースによって、今まで以上に細かくデータを管理できるようになる一方で、サポートのプロセスを経る必要性も減少します。



  1. Gzip が有効になっている場合、Firebase にインポートする前に Database のデータを解凍する必要があります。
  2. 大規模な Database では、Automatic Database Backups の JSON ファイルを使うよりも、サポートに連絡して Google の通常のバックアップから復旧する方が効率的です。


Posted by Yoshifumi Yamaguchi - Developer Relations Team



2. わかりやすさ

煩雑なサイトを見たいと思う人はいないでしょう。シンプルなアプリは、ユーザーにあらゆるオプションを試させて、時間を無駄にするようなことはしません。ユーザーは何が本当に必要かを意識しなくても使うことができます。画面をすっきりさせたり、短いキャッチコピーを書いたり、デザインを見やすくしたり、余白を多くしてワンタッチで操作できるボタンを作ったりすると、アプリをシンプルにすることができます。同じように、すっきりして美しく、ワンタッチで操作できる広告にすると、ユーザーを理解していることが伝わり、ユーザーの信頼を得ることができます。

3. トリックなし

はっきり言ってしまうと、ネイティブ広告も広告です。広告コンポーネントをゆがめたり、覆ったりするのはやめましょう(これ以上てっとり早くユーザーを不快にさせる方法はありません)。Google は、アプリ広告のエコシステムとの信頼を築き上げることを重視しています。たとえば、意図しないクリック(すばやいクリックや端のクリックなど)が発生しないよう、ネイティブ広告フォーマットの保護を拡張しています。これは、指を滑らせたユーザーが、広告主に実際以上の価値をもたらすことがないようにするためです。

4. 考え抜かれたデザイン

ユーザーに気を配っていることを伝えるために、アプリの細かい点まで考慮することが重要です。具体的には、精細なイメージ、選りすぐりのフォント、細かく調整された余白、短い読み込み時間がこれに当たります。ネイティブ広告のデザインに対しても同じレベルの細かい気配りや仕上げを行うことが重要です。シンプルかつ直感的で優れたデザインのネイティブ広告によって、貴重なユーザーベースに感謝を伝えることができます。



次の投稿までの間、AdMob が TwitterLinkedInGoogle+ でお届けする情報をご覧ください。




Posted by Rikako Katayama - AdMob Team


先日、Android 7.1 Nougat の Developer Preview がまもなく登場することをお知らせしました。SDK とツールをダウンロードすることにより、本日(*原文公開当時)よりこの新しいリリースが使えるようになっています。対応端末で 7.1 リリースを使うには、お使いの端末を Android ベータ版プログラムに登録してください。既に端末が登録されている場合は、自動的にアップデートを受信します。

Developer Preview の内容


Android 7.1 Developer Preview には、新しいプラットフォームでアプリのテストを実施し、ショートカットやイメージ キーボードのサポートなどの新機能を用いてアプリを拡張するために必要なものがすべて備わっています。具体的には、アップデートされた SDK、ビルドツール、ドキュメント、サンプル、さらにエミュレータや端末のシステム イメージなど、サポート対象の端末でアプリを実行するために必要なものが含まれています。

N やそれ以前のリリースで使用しているモデルは継続しますが、追加リリースである Android 7.1 には、特筆すべきいくつかの相違点があります。
  • 7.1 は既に Pixel に搭載されているため、Nexus ラインナップの端末向けの最初の Developer Preview はベータ版品質でお送りします。この目的は、端末固有の問題に対応することです。
  • 新しい API は、API レベル 25 として確定しています。
  • また、新しい API レベルを対象としたアプリの Google Play での公開を開始していますので、準備ができ次第、アプリをアップデートできます。

初期プレビュー リリースの次には、11 月にアップデートを提供する予定です。12 月には、Android Open Source Project(AOSP)への最終パブリック リリースを行います。まずは Nexus 5X、Nexus 6P、Pixel C 端末が対象になりますが、11 月には Developer Preview の対象端末が増える予定です。

Android 7.1 リリースに向けたアプリの準備


対応を始めるには、Android Studio 2.2.2 にアップデートし、API レベル 25 プラットフォームとエミュレータのシステム イメージやツールをダウンロードします。API レベル 25 に対応した最終版の SDK は、Android Studio の SDK Manager を使ってダウンロードできます。

API レベル 25 SDK をインストールすると、プロジェクトの compileSdkVersion を 25 に更新して新しい API のビルドやテストができるようになります。互換性テストを行う場合は、アプリの targetSdkVersion を API 25 に更新し、互換動作を無効にしてアプリをテストすることをおすすめします。API レベル 25 SDK を使ってアプリをセットアップする方法の詳細については、プレビューのセットアップに関する記事をご覧ください。

アプリのショートカットやサークル ランチャー アイコンをご自分のアプリに追加する場合は、Android Studio のビルトイン Image Asset Studio を使うと、マテリアル デザイン ガイドラインを満たす異なるサイズのアイコンを短時間で作成できます。

Android API レベル 25 SDK 対応の Google API のエミュレータ システム イメージには、円形アイコンや新しい Google Pixel ランチャーのサポートが含まれています。Google API システム イメージを使うと、円形アイコンをサポートしている端末でアプリの円形アイコンがどのように見えるかをテストできます。さらに、ライブ壁紙を開発している場合、新しいシステム イメージと Android Emulator を使うと、Android 7.1 で拡張されたプレビュー メタデータをテストできます。

イメージ キーボードのサポートを追加するには、Messenger と Google キーボード アプリを使ってテストします。これらのアプリは、プレビュー システム イメージに含まれており、新しい API をサポートしています。

API レベル 25 SDK とともに、Android Support Library も 25.0.0 にアップデートしました。新しいバージョンでは、API レベル 13 以降と互換性のあるイメージ キーボードのサポートを追加できます。また、マテリアル デザイン ガイドラインのボトム ナビゲーション パターンを実現する BottomNavigationView ウィジェットも追加されています。

API レベル 25 の詳細については、デベロッパー プレビュー サイトAPI の差分と最新の API リファレンスをご覧ください。
Nexus 6P でのイメージ キーボードのサポート
Android Studio で Android Emulator を使うと、ランチャーで円形アプリアイコンやショートカットのテストを行うことができます
Nexus 6P でのアプリ ショートカット
Image Asset ツールを使うと、短時間で円形アイコン アセットを作成できます。

Google Play のアルファ版、ベータ版、または製品版のチャンネルにアプリを公開する


Android 7.1 API は最終版なので、API 25 を使用しコンパイルした、またはオプションで API 25 をターゲットに指定したアップデートを、Google Play に公開できます。Google Play Developer Console では、API 25 を使用するアプリのアップデートをアルファ版、ベータ版、さらに製品版のチャンネルに公開できるようになっています。これによって、Pixel や Android ベータ版端末などの Android 7.1 が実行されているユーザーの端末にアプリのアップデートをプッシュできます。

対象端末での Android 7.1 Developer Preview の入手方法


既に Android ベータ版プログラムに登録している方には、すぐに対象端末に Android 7.1 Developer Preview のアップデートが配信されるはずです。ユーザー側の操作は必要ありません。まだ Android ベータ版プログラムに登録していない方は、次の方法で簡単に利用を開始できます。android.com/beta にアクセスして、対象の Android スマートフォンまたはタブレットをオプトインするだけで、すぐに今回(および次回以降)のプレビュー アップデートを OTA で受信できます。対象端末をお持ちで、アップデートを自動で受信したくない場合は、Android ベータ版プログラムで端末を登録解除してください。このアップデートを手動でダウンロードして書き込むこともできます。

12 月に予定している正式リリースに向けて努力してまいりますので、今後も Developer Preview Issue TrackerN Preview デベロッパー コミュニティ、または Android ベータ版コミュニティを通じてフィードバックをお寄せください。




Posted by Takeshi Hagikura - Developer Relations Team



2. 真の A/B テストを行うために、一度に 1 つのバリエーションのみをテストする


次の段階では、実験の準備に進みます。テスト段階では、オリジナルとなる現在のバージョンと、新しくデザインを変更したバージョンの 2 つのバリエーションのアプリ画面が必要です。A/B テスト プラットフォームを使うと、このような画面を簡単に作成でき、デザインやテストの実行、監視が容易になります。

3. 実験を行う


実験を行って結果を取得します。アプリを設定して、半分のユーザーには元々の設定(「コントロール グループ」)を、残りの 50% には 2 つ目のバージョン(「試験運用版グループ」)を無作為に表示するようにします。コントロール グループから、結果を比較する際に基準となるデータを収集します。それがないと、新しいデザインへの反応と、季節的な条件などのその他の変動要因を区別できません。

4. 決断する


実験を行ったら、データを分析し、最初の目標と仮説を再確認し、長期的に見て新しいバリエーションに変更する価値があるかどうかという、きわめて重要な判断を下します。ただし、急いで変更を行わないようにしましょう。重要な変更の場合は、季節を通して実験を行い、結果が季節的な要因やその他の変動要因によらないことを確認するとよいでしょう。

多くのテストを続ければ、たとえ便利な補助ツールがあったとしても、テストに多くの時間やリソースが必要になることがわかるでしょう。目標に大きく影響しない要素をテストして時間を無駄にする必要はありません。アプリのアナリティクス データ(https://firebase.google.com/docs/analytics/)を利用して、改善のチャンスや可能性が大きい場所を見つけます(トラフィックが多い画面、エンゲージメントが高い画面、ユーザーの離脱率が高い画面などを考えます)。専門チームを設け、その 25% の時間をアナリティクス データの監視に使い、広告を最適化するアイデアの検討やテストを行うのが理想的です。





次の投稿までの間、AdMob が TwitterLinkedInGoogle+ でお届けする情報をご覧ください。



Posted by Rikako Katayama - AdMob Team
Share on Twitter Share on Facebook


Pinar Ozlen
Software Engineer

本日(元記事執筆時点)は、Firebase Cloud Messaging(FCM)で JavaScript ライブラリがリリースされ、ウェブがサポートされたことをお知らせいたします。これによって、現在のブラウザのサポートが拡大され、実装プロセスも劇的にシンプルになります。また、トピック端末グループへのメッセージングなどの強力な機能をウェブでも使えるようになります。

通知は、デベロッパーが魅力的な仕組みを作る上で欠かせないツールの 1 つです。Chrome にこの仕組みが導入されてから、とても広い範囲で採用されるようになっており、日々 100 億件を超える通知がウェブサイトに送信されています。ただ、デベロッパーからは、この機能をウェブで実装するのは難易度が高い場合があり、ネイティブ通知で利用できるのと同じような FCM の高度な機能にアクセスしたいという声もよく聞かれました。

Firebase Cloud Messaging は、既に iOS アプリ、Android アプリ、Chrome へのメッセージ送信サポートしている強力なシステムです。本日(元記事執筆時点)より、デベロッパーの皆様は、FCM を使って Push API をサポートしているブラウザにメッセージを送信できるようになります。これによって、Chrome だけでなく、Firefox、Opera などにも通知を送れるようになります。

FCM JavaScript ライブラリによってウェブユーザーへの通知の送信も今まで以上に簡単になっています。FCM は、ペイロードの暗号化など複雑なサーバーサイドの機能や、Service Worker などのクライアントサイドの機能にも対応しています。

すぐに使い始めることができるように、デフォルトの Service Worker 実装が準備されています。これは、拡張したりオーバーライドしたりすることによって、簡単に使うことができます。さらに、FCM API を使用すると、サーバーでペイロードの暗号化を行うことができます。FCM ユーザーは、サーバー実装をまったく変更せずにこれを実現できます。

ただし、ウェブ通知の技術的な側面はまだ始まったばかりです。ウェブ通知を最大限に活用するためには、ユーザーに正しい方法で正しいコンテンツを使ってもらう必要があります。通知内容に関するベスト プラクティスについては、「よい通知とは」や「プッシュ通知許可 UX のベスト プラクティス」をご覧ください。ウェブユーザーとやり取りして通知を送信する許可を得るためのおすすめの方法が記載されています。

サポートされている FCM 機能


FCM JavaScript ライブラリは、簡単なクライアント実装の提供に留まらず、ウェブ向けの重要な FCM 機能も実現しています。

FCM JavaScript ライブラリを使うと、単一の端末、トピック、端末のグループに対してウェブでプッシュ通知を送信できます。ウェブでトピックがサポートされたことに加え、特定のトピックをオプトインしている Android、iOS、ウェブの各ユーザーにメッセージを送信できます。トピックや端末グループのメリットを活用するには、サーバーサイド API を使って トピックグループのサブスクリプションの管理を行う必要があります。

対象となるブラウザ


現在、FCM JavaScript ライブラリは、Push API をサポートしているブラウザで使用できます。具体的には、以下のブラウザになります。

Microsoft Edge は Push API をサポートする予定であることを発表しています。Samsung Browser も、メッセージ ペイロードをサポートすれば対象になります。今後、Service Worker をサポートするブラウザが増えれば、対象となるブラウザは増えてゆくでしょう。アップデートについては、リリースノートをご覧ください。

使ってみるには


Firecast の動画を確認し、スタートガイドに従ってください。



パートナーの実績


私たちは、最高のユーザーのウェブ通知エクスペリエンスを実現すべく、アーリー アダプターとともに、テストや微修正を行い、新しい FCM JavaScript ライブラリの力を解放してきました。以下で、いくつかの成功事例を紹介します。

「FCM を見つけるまで、効果的な通知ソリューションを探すことはできませんでした。……機能が豊富でパフォーマンスも安定しており、導入も簡単な FCM は最高のソリューションです」
Alibaba.com モバイル、ディレクター、Zou Yu 氏



外国の購入者と中国のサプライヤーをつなぐ主要小売りマーケットプレイスである Alibaba.com は、2 日でこのソリューションを実装し、ウェブ通知を受け取っているユーザーのエンゲージメントを直接ウェブサイトを訪れたユーザーの 4 倍に向上させましたAlibaba.com の事例紹介はこちらからご覧ください

「Firebase Cloud Messaging は私たちの要件に完璧に一致しました」
AliExpress、ディレクター、Lijun Chen 氏



グローバル小売りマーケットプレイスの AliExpress では、ウェブでの通知はアプリの通知に比べて開封率が 93.4% 高く、通知を受信していないモバイルサイトのユーザーに比べてコンバージョン率が 178% 高くなっています。AliExpress のチームは、現在もユースケースを展開中です。たとえば、11 月 11 日(「ダブル 11」とも言われます)は、主に若い独身の中国人が祝う世界最大級のオンライン ショッピング イベントです。この日の販売を促進するため、AliExpress はウェブサイト上で FCM を使って通知を送り、ユーザーが興味のある品を割引価格で購入できることをお知らせする予定です。AliExpress の事例紹介はこちらからご覧ください

「初期実装はとても簡単でした。実際に 1 日で終わってしまったほどです」
デベロッパー、Filip Procházka 氏



ユーザーの共同購入をトラッキングする成長著しい新興企業 Settle Up は、請求書に変更があった場合にユーザーに通知を送信したいと考えていました。実装は 1 日で終わりましたが、ウェブ通知を受信したユーザーのエンゲージメントが 37% 上昇するという成果が現れ始めています。さらに、Firebase Analytics、Crash Reporting、Hosting、Test Lab を利用している Settle Up は、統合ソリューションとしての Firebase を通してすべてのツールに簡単にアクセスすることができます。Settle Up の事例紹介はこちらからご覧ください

使ってみる


Firebase Cloud Messaging は、Firebase プラットフォームの一部であり、無償で使用できます。今回のリリースについてお知らせできることをうれしく思います。ぜひ感想をお寄せください。お待ちしています。


Posted by Eiji Kitamura - Developer Relations Team
Share on Twitter Share on Facebook


Nicolas Garnier
Developer Programs Engineer
Firebase Authentication は、4 つのすぐに使える ID プロバイダをサポートしているため、Google、Facebook、Twitter、GitHub で認証を行うのはとても簡単です。たとえば、ウェブアプリで Firebase ユーザーに Google でログインしてもらうために必要なのは次のコードだけです。

var google = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(google);

ただ、その他の ID プロバイダを使って独自の Firebase アプリにログインしてもらいたい場合もあるかもしれません。たとえば、Instagram API を使ってユーザーが Instagram の写真を共有できるようにするつもりなら、Instagram はすばらしい選択肢になるでしょう。

Firebase にビルトインでサポートされていない ID プロバイダを使うことも可能ですが、それには少しばかりのコードとサーバーが必要になります。ここでは、Instagram によるログインを Firebase ウェブアプリに組み込むために必要な手順について、順に説明します。Instagram はログインに OAuth 2.0 を使っています。そのため、本投稿は LinkedIn のようなその他の OAuth 2.0 ID プロバイダによるログイン機能を組み込む際にも役立つはずです。

設計の概要

Instagram は OAuth 2.0 をサポートしています。これは、アプリの認証や Instagram のユーザー ID を含むユーザーデータにアクセスするための主な方法となります。必要になるのは、ユーザーに OAuth 2.0 認証コードのフローを実行してもらい、アプリへのアクセス権を付与することです。OAuth 2.0 のフローは、次のような流れで実行されます。

まず、ユーザーを Instagram の認証エンドポイントにリダイレクトする必要があります。エンドポイントでは、初めてアプリにアクセス権を付与する際に、ユーザーに同意を求める画面が表示されます。これは、ポップアップ ウィンドウで実現されています。

アプリの認証が完了すると、ユーザーは認証コードとともに元のドメインにリダイレクトされます。Instagram アプリの認証情報を使うと、サーバー側で認証コードアクセス トークンと交換できます。Instagram では、認証コードを交換する過程でユーザー ID も返されます(LinkedIn などの他の OAuth 2.0 プロバイダでは、追加のリクエストが必要になる場合もあります)。

Instagram のユーザー情報を取得すると、サーバーで Firebase のカスタム認証トークンを作成できるようになります。ユーザーは、このトークンと signInWithCustomToken メソッドを使ってウェブアプリで Firebase にログインできます。

また、クライアント上で Firebase のプロフィールをアップデートできるように、表示名や写真の URL などの Instagram から取得したユーザー プロフィール情報も渡しています。 - 注: Instagram は、ユーザーのメールアドレスは提供していません。そのため、メールアドレスなしの Firebase アカウントができることになりますが、このこと自体には特に問題はありません。以上が完了すると、ポップアップが閉じます。これで、ユーザーは Instagram アカウントのプロフィール データつきで Firebase にログインできました。

構築に着手する

では、もう少し詳細に踏み込み、重要な部分の実装方法を見てみましょう。今回は、バックエンドを Node.js で記述します。

Instagram にアプリを登録する

ウェブアプリには、Instagram 認証フローを開始するボタンが必要になります。その前に、OAuth 2.0 に必要なアプリの認証情報を取得できるよう、まず Instagram デベロッパー コンソールにアプリケーションを登録する必要があります。

Instagram アプリの設定で、http://localhost:8080/instagram-callback(テスト用)と https:///instagram-callback(本番用ドメイン)を有効なリダイレクト URI としてホワイトリスト登録しておきます。次に、Instagram クライアント IDクライアント シークレットをメモします。後でこれが必要になります。

アプリを登録し、コールバック URL をホワイトリストに登録すると、クライアント ID とクライアント シークレットが返されます。この流れは、どの OAuth 2.0 プロバイダでも必要となる典型的な手順です。

Instagram の OAuth 2.0 を設定する

サーバーでは、OAuth 2.0 プロトコルの詳細を隠蔽してくれる simple-oauth2 パッケージを使います。これを設定するためには、Instagram クライアント ID とクライアント シークレット、Instagram の OAuth 2.0 トークンと認証エンドポイントなど、いくつかの値を設定する必要があります。Instagram に対して使う必要があるのは、次の値です。

// Instagram OAuth 2 の設定
const credentials = {
 client: {
   id:YOUR_INSTAGRAM_CLIENT_ID, // 要変更
   secret:YOUR_INSTAGRAM_CLIENT_SECRET, // 要変更
 },
 auth: {
   tokenHost: 'https://api.instagram.com',
   tokenPath: '/oauth/access_token'
 }
};
const oauth2 = require('simple-oauth2').create(credentials);

Instagram 認証フローを開始する

ユーザーを Instagram の同意画面にリダイレクトする URL ハンドラをサーバーに追加します。その際に、Instagram 認証フローを完了したときにユーザーがリダイレクトされて戻ってくる場所となるリダイレクト URI を提供する必要があります。今回は、/instagram-callback をコールバック ハンドラのパスに使用します。

app.get('/redirect', (req, res) => {
  // ランダムな状態検証 Cookie を生成
  const state = req.cookies.state || crypto.randomBytes(20).toString('hex');
  // localhost で安全でない Cookie を許可
  const secureCookie = req.get('host').indexOf('localhost:') !== 0;
  res.cookie('state', state.toString(), {maxAge: 3600000, secure: secureCookie, httpOnly: true});
  const redirectUri = oauth2.authorizationCode.authorizeURL({
    redirect_uri: `${req.protocol}://${req.get('host')}/instagram-callback`,
    scope: 'basic',
    state: state
  });
  res.redirect(redirectUri);
});

また、セッション固定攻撃を避けるため、OAuth リクエストの state パラメータでランダムな文字列を渡し、それを HTTP Cookie として保存しています。これによって、戻された state パラメータと Cookie に保存されている値を比較することができ、アプリがフローを開始したことを確認できます。

クライアントでは、ポップアップを表示するボタンとして、次のようなコードを書きます。

function onSignInButtonClick() {
  // ポップアップで Auth フローをオープン
  window.open('/redirect', 'firebaseAuth', 'height=315,width=400');
};

ユーザーがログインボタンをクリックすると、ポップアップがオープンし、ユーザーが Instagram の同意画面にリダイレクトされます。

同意したユーザーには URL の code クエリ パラメータに認証コードが渡され、先ほど渡した state の値と合わせて、/instagram-callback URL ハンドラにリダイレクトされます。

アクセス トークン用の認証コードの交換

ユーザーがコールバック URL にリダイレクトされた際には、以下の処理を行います。
app.get('/instagram-callback',(req, res) => {
  // state Cookie を受け取ったことを確認
  if (!req.cookies.state) {
    res.status(400).send('State cookie not set or expired.Maybe you took too long to authorize.Please try again.');
  // state Cookie が state パラメータと一致することを確認
  } else if (req.cookies.state !== req.query.state) {
    res.status(400).send('State validation failed');
  }

  // アクセス トークン用に認証コードを交換
  oauth2.authorizationCode.getToken({
    code: req.query.code,
    redirect_uri: `${req.protocol}://${req.get('host')}/instagram-callback`
  }).then(results => {
    // Instagram アクセス トークンとユーザー ID の取得が完了
    const accessToken = results.access_token;
    const instagramUserID = results.user.id;
    const profilePic = results.user.profile_picture;
    const userName = results.user.full_name;

    // ...

  });
});

これでこの実装での OAuth 2.0 固有部分は完成です。以降は、ほぼ Firebase に固有の部分です。

次は、Firebase カスタム認証トークンを作成します。そのカスタム認証トークンを使ってログインを実行し、Firebase のユーザー プロフィールをアップデート(詳細は後述)する HTML ページを提供します。

app.get('/instagram-callback', (req, res) => {

    // ...

  }).then(results => {
    // Instagram アクセス トークンとユーザー ID の取得が完了
    const accessToken = results.access_token;
    const instagramUserID = results.user.id;
    const profilePic = results.user.profile_picture;
    const userName = results.user.full_name;
      
    // Firebase カスタム認証トークンの作成
    const firebaseToken = createFirebaseToken(instagramUserID);

    // ログインを実行し、ユーザー プロフィールをアップデートする HTML ページの提供
    res.send(
        signInFirebaseTemplate(firebaseToken, userName, profilePic, accessToken)));
  });
});

カスタム認証トークンの作成

Firebase カスタム認証トークンを作成するには、Firebase にサービス アカウント認証情報を設定する必要があります。これは、このようなトークンを生成する際に必要となる管理権限を付与するために必要になります。サービス アカウント認証情報ファイルは、service-account.json として保存してください。

const firebase = require('firebase');
const serviceAccount = require('./service-account.json');
firebase.initializeApp({
  serviceAccount: serviceAccount
});

カスタム認証トークンの作成はシンプルです。Instagram のユーザー ID に基づいて uid を選択するだけで構いません。

function createFirebaseToken(instagramID) {
  // ユーザーに割り当てる uid
  const uid = `instagram:${instagramID}`;

  // カスタム トークンの作成
  return firebase.auth().createCustomToken(uid);
}

注: サービス アカウント認証情報は安全に保管する必要があるため、カスタム トークンの作成は必ずサーバー側で行います。

カスタム トークンを作成した後は、それをクライアントに渡して Firebase にログインします。

カスタム トークンによるログイン

この時点で、サーバーはポップアップ ウィンドウ内で実行される HTML ページを提供し、以下の処理を行います。

ここでポイントとなるのは、プロフィールをアップデートする際にデフォルトの Firebase アプリを使うのではなく、一時的な Firebase App インスタンスを使っている点です。これによって、ユーザー プロフィールがアップデートされる前にメインページの Auth リスナーが呼ばれることを防いでいます。

app.get('/instagram-callback', (req, res) => {

    // ...

    // ログインを実行し、ユーザー プロフィールをアップデートする HTML ページの提供
    res.send(
        signInFirebaseTemplate(firebaseToken, userName, profilePic, accessToken)));
  });
});

function signInFirebaseTemplate(token, displayName, photoURL, instagramAccessToken) {
 return `
   <script src="https://www.gstatic.com/firebasejs/3.4.0/firebase.js"></script>
   <script src="promise.min.js"></script><!-- Promise Polyfill for older browsers -->
   <script>
     var token = '${token}';
     var config = {
       apiKey:MY_FIREBASE_API_KEY, // 要変更
       databaseURL:MY_DATABASE_URL // 要変更
     };
     // 一時 Firebase アプリにログインしてプロフィールをアップデート
     var tempApp = firebase.initializeApp(config, '_temp_');
     tempApp.auth().signInWithCustomToken(token).then(function(user) {
    
       // Realtime Database に Instagram API アクセス トークンを保存
       const tasks = [tempApp.database().ref('/instagramAccessToken/' + user.uid)
           .set('${instagramAccessToken}')];
  
       // 必要に応じて displayname と photoURL をアップデート
       if ('${displayName}' !== user.displayName || '${photoURL}' !== user.photoURL) {
         tasks.push(user.updateProfile({displayName: '${displayName}', photoURL: '${photoURL}'}));
       }
  
       // 以上のタスクの完了を待機
       return Promise.all(tasks).then(function() {
         // 一時 Firebase アプリを削除し、デフォルト Firebase アプリにログインし、ポップアップをクローズ
         var defaultApp = firebase.initializeApp(config);
         Promise.all([
             defaultApp.auth().signInWithCustomToken(token),
             tempApp.delete()]).then(function() {
           window.close(); // 完了!ポップアップをクローズ
         });
       });
     });
   </script>`;
}

ポップアップ内でユーザーがデフォルト Firebase アプリにログインすると、認証状態リスナーがメインページを起動し(Firebase では、すべてのタブで認証状態が共有されます)、それですべて完了です。ユーザー プロフィール情報を表示したり、Realtime Database や Firebase Storage を使ったりすることができます。

試してみる

自由に試していてだけるデモアプリを作成しています。https://instagram-auth.appspot.com/

サンプルはオープンソースです。ご自由に Github のリソースをご覧ください。 https://github.com/firebase/custom-auth-samples

Android と iOS への対応

この記事で紹介したコードはウェブアプリ用です。Instagram 認証を Android や iOS アプリに追加するテクニックはいくつかあります。この投稿では紹介できませんが、今後にご期待ください。

これで完了

他の ID プロバイダ用のサンプルを探している場合や、それを組み込む際に問題が発生している場合は、コメントや GitHub リポジトリの問題点でお知らせください。喜んでお手伝いいたします。


Posted by Khanh LeViet - Developer Relations Team
Share on Twitter Share on Facebook



Todd Kerpelman
Developer Advocate
Firebase Remote Config の機能の中で特に気に入っているのが、異なるユーザー グループに異なるコンテンツを提供できる機能です。たとえば、アプリ上で課金サービスを多く利用しているユーザーには、異なるフロントページを表示したり、フィットネスアプリであれば、ランナーにはあるコンテンツを、ウェイトリフターには別のコンテンツを強調して表示させることができます。

Remote Config が初めてリリースされてから、異なる Firebase Analytics Audience に属するユーザーにそれぞれ異なるコンテンツを提供することにより、極めて高度なユーザー ターゲティングを実現できるようになりました。


そして最近、異なるユーザー プロパティを有するユーザーに、異なるデータセットを送信する機能が追加されたことで、Remote Config はますます便利になりました。

「ちょっと待って。今の時点でも Audience を使って、ユーザー プロパティだけを使ったターゲティングよりも高度なターゲティングができているのに、なぜこの機能がターゲティングの向上につながるの?」と思われたかもしれません。

確かに、Audience を使えば多数のイベントやユーザー プロパティごとに異なるユーザー グループを定義できるので、特定のグループを対象にした効果的なユーザー ターゲティングが可能です。たとえば、「このゲームのレベル 5 をクリアした、左利きのカナダ人」という Audience を作成できます。1

しかし、Audience には以下の 2 つの制限があり、これにより Remote Config 内での利用が難しくなっています。

1 つ目は、利用できる Audience が 50 に制限されていること、そして Audience が、Analytics レポートの作成や、Google AdWords を使ったリマーケティング キャンペーンの構築といった、Firebase 内の他のターゲティング機能の Audience と共有されることです。これにより、小規模のアドホックなユーザー グループの作成や、社内で他の人が使っている既存の Audience の編集が難しくなります。

2 つ目は、現在の Audience では、一度ユーザーが Audience に入ると、そこから抜けられないということです。Audience をリマーケティング キャンペーンに活用する、一般的なマーケティング担当者にとってはまったく問題もないですが、Remote Config で利用する場合は、これが問題となることがあります。

たとえば、ゲームは開始したけれど、レベル 10 をクリアしていないユーザーに対し「新人」という Audience を作成したいとします。このグループから Audience を作成しようとした場合、ゲームを開始したユーザー全員が、その Audience に入ることになります。しかし、レベル 15 や 20 をクリアした後も、ユーザーはこの Audience に入ったままとなるため、実質この Audience が「すべてのユーザー」という Audience になってしまいます。

このため、Remote Configでのユーザー ターゲティングにおいては、ユーザー プロパティを使ってコンテンツをカスタマイズする方が便利です。ユーザー プロパティを使うと、より小さなワンオフのターゲット グループを多数作成でき、従来の Audience よりもダイナミックなターゲティングが可能になります。

たとえば、フィットネス アプリにおいて、ユーザーの好きなエクササイズのタイプによって、異なるフロントページを表示させたいとします。この場合、ユーザーの好きなエクササイズをユーザー プロパティに保存し、このプロパティを基にして新たな条件を作成することで、簡単にフロントページの設定が可能です。


そして、それぞれの条件を基に、異なるフロントページを表示させることができるようになります。


このように、複数の異なる条件を簡単に作成でき、Firebase Analytics Audience の「上限」を気にする必要もなくなります。2

別の例として、ゲームアプリにおいて、ユーザーのレベルによりゲームのビヘイビアを変更したいとします。この場合、ユーザーのレベルをユーザー プロパティに保存し、階層別に異なる条件を多数作成できます。たとえば、レベル 4 からレベル 10 のユーザーを「中間」層として作成し、毎日異なるボーナスを該当するプレイヤーに付与することができます。





ユーザーがレベルを上げていくにしたがって、ユーザー プロパティによって、ユーザーが違う階層に配置されるので、その階層に基づき、Remote Config が自動的に対応する値を付与します。それぞれに対し、新しい Audience を作成する必要はありません。

後でグループ分けを変更したい場合も、簡単に実施できます。もし、中間層をレベル 6 からスタートさせたいと考えた場合、Firebase のコントロール パネル上で変更可能であり、変更は Remote Config に即座に反映されます。


また、さらに別のプレイヤーグループに対して、ゲームのビヘイビアを変更しようと突然思い立った場合でも、4番目の階層を簡単に追加できます。


ユーザー プロパティは、通常文字列で保存されているので、「完全一致」や「を含む」などの文字列比較を実行することができます。ユーザーの好きな 3 つのエクササイズを文字列(たとえば、yoga|interval_training|running)で保存している場合、エクササイズに「running」を含むユーザーをターゲットにすることで、「すべてのランナー」という条件を作成できます。

また、上記のプレイヤー階層で説明したように、数字による比較を実行することも可能です。Remote Config は、たとえば、"42" を 42 に、 "3.14159" を 3.14159 にといったように、既知の方法で文字列を数値に変換します。ただし、整数や浮動小数に変換されないユーザー プロパティの文字列(たとえば、"Level_42")との数値比較は、エラーとなります。

ユーザープロパティを使ったターゲティングは比較的新しい機能なので、まだ利用されたことがない場合はぜひ試してみてください。Remote Config Panel に移動し、既に保存しているユーザー プロパティを使って、まずはちょっとした変更を実施してみましょう。正常に動作した場合、自分のアプリのどの部分をカスタマイズするのが効果的かを考え、それをサポートするためのユーザー プロパティを追加してみてください。

もし、うまく活用することができたら、ぜひお知らせください。フィードバックを楽しみにしています。

「利き手」をユーザー プロパティとしてターゲティングしたと仮定。
正確に言えば、Remote Config の条件作成数にも 100 という上限がありますが、50 に比べればさまざまな実験をする余地があります。


Posted by Yoshifumi Yamaguchi - Developer Relations Team

Share on Twitter Share on Facebook

(右)イメージ キーボードのサポート: ユーザーはキーボードから直接、画像やその他のコンテンツを入力できる。
(左)アプリ ショートカット: キー操作に対応したショートカットを配置すると、瞬時にアプリの深い階層まで移動できる。

アプリの準備

Android 7.1 は追加リリースですが、メジャー リリース同様に(特にユーザーの手に渡る端末上で)アプリが正しく表示され、機能することを確認する必要があります。

Android 7.1 Developer Preview には、アプリのテストを実施し、ショートカットやキーボード イメージなどの新機能を用いてアプリを拡張するために必要なものがすべて備わっています。具体的には、新しい API に対応した SDK、ビルドツール、ドキュメント、サンプル、さらにエミュレータや端末のシステム イメージなど、サポート対象の Nexus 端末でアプリを実行するために必要なものが含まれています。またアプリ ショートカット機能に対応したランチャーとアプリ、キーボード イメージ機能に対応したキーボードも提供されています。

Developer Preview を自動で受信したい場合は、Android ベータ版プログラムで端末を登録してください。以前に端末を登録済みで、登録解除を行っていない場合は、アップデートが端末に配信されます。登録済みで今回のアップデートは受信したくない場合は、Android ベータ版プログラムで、早めに端末の登録を解除してください。

まずは Nexus 5X、Nexus 6P、Pixel C 端末向けに Developer Preview を提供し、プレビュー終了時にサポート対象端末を増やす予定です。12 月初旬の Android 7.1.x の最終リリース時には、すべてのサポート対象端末(Nexus 6、Nexus 5X、Nexus 6P、Nexus 9、Nexus Player、Pixel C、対象の Android One 端末)にアップデートを展開します。

ユーザー端末にも順次展開

Google では Android 7.1 をエコシステム全体の端末に提供するため、パートナーと連携して何カ月も前倒しで準備を進めています。デベロッパーの皆さまには、できるだけ早めに Android 7.1 Developer Preview をダウンロードすることをお勧めします。円形のアプリアイコンやアイコン ショートカットを使用するなどして、アプリの互換性や最適化のテストを実施し、魅力的なアプリを作成してください。

Developer Preview の詳細については今後も情報を配信していきますので、期待してお待ちください。


Posted by Takeshi Hagikura - Developer Relations Team

Share on Twitter Share on Facebook


  • Washington Post — 7 日以内に戻ってくるモバイル検索ユーザーが 23% 増加 
  • Slate — 月間の一意な訪問者数が 44% 増加、月間の一意な訪問者数 1 人あたりの訪問数が 73% 増加 
  • Gizmodo — AMP ページからの Gizmodo へのトラフィックのうち 80% が新しいトラフィック、インプレッション数が 50% 増加 
  • Wired — 検索結果からのクリックスルー率(CTR)が 25% 増加、AMP 対応トップニュースの広告の CTR が 63% 増加 
  • Relay Media — 直近 30 日だけで、The Daily Dot、Hearst Television、The Miami Herald などのパブリッシャー向けに 250 万以上の AMP ページを変換。AMP 記事を読み始めたモバイル ユーザーは、通常のモバイルページを開いたユーザーよりも 10% 多く時間を使うことも判明 
コンテンツは高速なほどよいことには、疑いの余地はほとんどありません。 驚くことではありませんが、広告にも同じことが言えます。 今年行われた DoubleClick の調査によれば、150 のパブリッシャーに対して AMP 対応と AMP 非対応のモバイルページのパフォーマンスを比較したところ、以下のことがわかりました。 

  • 80% 以上のパブリッシャーが視認率の増加を実現 
  • 90% 以上のパブリッシャーでエンゲージメントが増加し、CTR が向上 
  • 大半のパブリッシャーで eCPM が向上(影響や上昇の割合は、地域や AMP 非対応サイトの最適化度合いによって異なる) 

そして、こちらのケーススタディでは、ヨーロッパ最大のネイティブ広告プラットフォームである plistan-tv.defaz.netabendzeitung.degolem.de などの大手パブリッシャーを対象に、ウェブアプリ ウィジェットのスピードや利益に対する AMP の影響を測定するという実験を独自に行っています。 

  • あるパブリッシャーは、AMP の実装後、CTR が 600% 上昇 
  • テスト対象のパブリッシャーの平均増加率は 220% 

このオープンソースの取り組みが成果を上げているのは、グループでの作業から、提案やフィードバック、コード仕様を含む GitHub ページへの掲載に至るまで、あらゆることに関与している強力なコミュニティが背後に存在するためです。

AMP Project は最初の 1 年でよいスタートを切りましたが、この先にはまだ多くの作業が残されています。 今後の予定についての最新情報を確認したい場合は、AMP ロードマップをご覧ください。 私たちは、あらゆる人にとってモバイルウェブをすばらしいものにするために力を合わせています。1 年後、さらに進展して戻ってくることを楽しみにしています。

投稿者: Google 検索担当副社長兼 Google AMP Project リーダー、David Besbris

1-wholeinfographic

Posted by Yoshifumi Yamaguchi - Developer Relations Team
Share on Twitter Share on Facebook



Todd Kerpelman
Developer Advocate
一部のグループの人たちの間でプライベートな会話ができるチャットアプリがあるとしましょう。または、友人たちのグループで一緒にアルバムを作ることができる写真共有アプリでも構いません。こういった種類のデータ共有を小さなグループのユーザーに制限し、世界に向けて公開しないようにするにはどうすればよいでしょうか。

Firebase のセキュリティ ルールが活躍するのはこのような場合です。Firebase のセキュリティ ルールはとても強力ですが、ちょっとしたガイダンスが必要になる場合もあります。それはルールが複雑であるからではなく、ほとんどの人々は十分な知見を得られるほど頻繁にこの機能を使っていないからです。

皆さんはラッキーです。なぜかと言えば、私はこのブログ投稿を書くために、ここ数週間 Firebase セキュリティの専門家の人々の隣にいてずいぶん彼らを悩ませてきたからです。さらに重要なのは、セキュリティ ルールを理解しやすくするある奇策を見つけたことです。これについては、本投稿の最後の部分で皆さんにお話しします。

ここでは、プライベート グループで会話を行うチャットアプリの例について考えてみましょう。チャット グループに参加しているユーザーは全員チャット メッセージを読み書きできますが、その他のユーザーには見られたくありません。

Database の構造は次のようになっているものとします。もちろん、これを実現する方法はたくさんありますが、デモ用の構造として一番簡単なのはおそらくこのようなものでしょう。

準プライベートと言える各チャットの中には、参加できるユーザーの一覧とチャット メッセージの一覧があります(現実の世界では、これらのユーザー ID は user_abc よりははるかに複雑になるでしょう)。

ここで設定したい最初のセキュリティ ルールは、メンバーのリストにあるユーザーのみがチャット メッセージを参照できるようにすることです。これは、いくつかのセキュリティ ルールを使って作成できます。
{
    "rules": {
      "chats": {
        "$chatID": {
          "messages": {
            ".read": "data.parent().child('members').child(auth.uid).exists()"
          }
        }
      }
    }
}

ここで言っているのは、chats//messages 内のチャットは、同じチャットの members セクションに userID が存在する場合に限り読み取りが許可されているということです。
ところで、$chatID という行は気にならないでしょうか。これはワイルドカードと同じく何にでもマッチしますが、マッチした値を後で使えるように $chatID 変数に格納することを示します。

では、user_abc の場合はどうなるでしょうか。チャット メッセージは完全に読み取りが可能です。しかし、user_xyz は読み取りが許可されていません。チャット グループに members/user_xyz というエントリがないからです。

このことがわかれば、同じようにしてメンバーのみがチャット メッセージを書き込めるルールを追加するのも簡単です。

"chats": {
  "$chatID": {
    "messages": {
      ".read": "data.parent().child('members').child(auth.uid).exists()",
      ".write": "data.parent().child('members').child(auth.uid).exists()"
    }
  }
}

お望みであれば、さらに細かく制御することも可能です。このチャットアプリに、参照はできてもメッセージを書き込むことはできない "lurker" というユーザータイプが存在する場合はどうなるでしょうか。


これにも対応することができます。その場合、「owner または chatter のみメッセージの書き込みが可能」ということを示すルールに変更します。これは、次のようなルールになるでしょう。

"chats": {
  "$chatID": {
    "messages": {
      ".read": "data.parent().child('members').child(auth.uid).exists()",
      ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'"
    }
  }
}

(簡潔になるように、以降のサンプルでは "rules" 行を省略します。)

ちなみに、「『lurker でないユーザーのみチャット メッセージを書き込めるようにする』方が簡単ではないか」と思われる方もいらっしゃるかもしれません。その場合、当然ながらコードは 1 行少なくて済みます。

"chats": {
  "$chatID": {
    "messages": {
      ".read": "data.parent().child('members').child(auth.uid).exists()",
      ".write": "data.parent().child('members').child(auth.uid).val() != 'lurker'"
    }
  }

しかし、セキュリティはブラックリストに基づくよりもホワイトリストに基づく方がよいものです。考えてみてください。アプリに突然新しいクラスのユーザー(たとえば "newbies")を追加し、このルールの更新を忘れてしまった場合はどうなるでしょうか。

最初のルールでは、新しいグループのユーザーは投稿はできません。しかし、2 番目のルールでは、何でも投稿できるようになってしまいます。どちらの場合も意図とは違うので問題にはなりますが、セキュリティの観点からすれば、後者の方がはるかによくない状況です。

もちろん、ここに記載したことはすべて、あるとても小さな問題を見逃しています。そもそも、ユーザーのリストはどのようにして設定したのでしょうか。

しばらくの間は、ユーザーがアプリから友人のリストを取得できたことにしておきましょう(これは、読者向けの練習問題として残しておきます)。グループチャットに新しいユーザーを追加する際に考えられるオプションはいくつかあります。
  1. チャットのユーザーは誰でも他のユーザーを追加できる
  2. チャットの所有者だけが他のユーザーを追加できる
  3. チャットへの参加申請は誰でもできるが、所有者による承認が必要

いずれの案でも問題ないでしょう。アプリにとってどれが最適なユーザー エクスペリエンスであるかを判断するのはアプリのデベロッパーです。

それでは、1 つずつ順番に見ていきましょう。


チャットのユーザーは誰でも他のユーザーを追加できる

最初のオプションに対応するには、「既にメンバーリストに含まれているユーザーはメンバーリストの書き込みが可能」というルールを設定する必要があります。


メンバーリストに対して、既に設定してある投稿用のルールによく似たものを設定します。

"chats": {
  "$chatID": {
    "messages": {
      ".read": "data.parent().child('members').child(auth.uid).exists()",
      ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'"
    },
    "members": {
      ".read": "data.child(auth.uid).exists()",
      ".write": "data.child(auth.uid).exists()"
    }
  }
}

このルールは、基本的に現在のユーザー ID がリスト内にあるユーザーであれば、メンバーリストの読み取りや書き込みができることを示しています。


チャットの所有者だけが他のユーザーを追加できる

所有者だけがリストに書き込めるような制限を加えるのも簡単でしょう。

"chats": {
  "$chatID": {
    "messages": {
      ".read": "data.parent().child('members').child(auth.uid).exists()",
      ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'"
    },
    "members": {
      ".read": "data.child(auth.uid).val() == 'owner'",
      ".write": "data.child(auth.uid).val() == 'owner'"
    }
  }
}

これは、「userID が所有者として登録されていれば、チャットの members 要素に書き込みが可能」という意味になります。

これで 2 番目のケースにも対応できます。


チャットへの参加申請は誰でもできるが、所有者による承認が必要

では、ユーザーが参加申請を行い、所有者が承認する場合はどうでしょう。この場合は、Database に members のリストだけでなく、ユーザーが追加できる保留中のリストを含めるとよいでしょう。


グループの所有者は、これらの見込みユーザーをメンバーリストに追加したり、保留中のリストから削除できるようにします。


宣言する最初のルールは、「自分のエントリであれば、保留中リストに追加できる」です。つまり、追加する項目のキーは自分のユーザー ID でなければなりません。

これを追加したルールは次のようになります。

"chats": {
  "$chatID": {
    "messages": {
      ".read": "data.parent().child('members').child(auth.uid).exists()",
      ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'"
    },
    "members": {
      ".read": "data.child(auth.uid).val() == 'owner'",
      ".write": "data.child(auth.uid).val() == 'owner'"
    },
    "pending": {
      "$uid": {
        ".write": "$uid === auth.uid"
      }
    }
  }
}

ここで言っているのは、「pending/ 以下の要素には、uid が自分の userID であれば書き込みできる」ということです。

さらに厳密にするなら、まだ "pending" リストに追加されていない場合のみ追加可能と指定することもできます。これは次のようになります。
"pending": {
  "$uid": {
    ".write": "$uid === auth.uid && !data.exists()"
  }
}

さらに、既にチャットのメンバーである場合は、自分を追加することができないルールを指定してみましょう。これにはあまり意味がないかもしれませんが、最終的に次のようなルールになります。

"pending": {
  "$uid": {
    ".write": "$uid === auth.uid && !data.exists() && !data.parent().parent().child('members').child($uid).exists()"
  }
}

次に、所有者は pending フォルダ全体を読み書きできるというルールを追加します。
"pending": {
  ".read": "data.parent().child('members').child(auth.uid).val() === 'owner'",
  ".write": "data.parent().child('members').child(auth.uid).val() === 'owner'",
  "$uid": {
    ".write": "$uid === auth.uid && !data.exists() && !data.parent().parent().child('members').child($uid).exists()"
  }
}

これで完成です。チャットの所有者のみメンバーリストを読み書きできるという前のセクションからのルールをそのまま使っていれば、所有者が保留中リストからエントリを削除してメンバーリストに追加できるセキュリティ ルールになります。

最後に 1 つだけ、まだ触れていなかった重要なルールがあります。それは、新しいチャット グループの作成です。チャット グループはどのようにすれば設定できるでしょうか。「メンバーリストが空であれば、誰でもそこに書き込んで自分を所有者とすることができる」ことを宣言すれば、チャット グループを追加することができます。

"members": {
  ".read": "data.child(auth.uid).val() == 'owner'",
  ".write": "data.child(auth.uid).val() == 'owner' ||(!data.exists()&&newData.child(auth.uid).val()=='owner')"
}

この書き込みを行うために、まず /chats/chat_345/members にオブジェクト { "user_zzz" : "owner" } を書き込む場合を考えてみてください。newData 行はこのオブジェクトを参照し、ログインユーザー(user_zzz)のキーの子要素が所有者になっていることを確認しています。

その後、所有者は自由にメッセージやユーザーを追加することができます。所有者としてリストに登録されているため、セキュリティ ルールはこのようなアクションを何の問題もなく許可します。

なお、セキュリティ ルールでは、「ディレクトリ作成」の操作は個別の概念として存在しない点に注意してください。ユーザーが chat_456/messages/abc への書き込みを許可されている場合、messages が既に存在するかしないかにかかわらずルールが適用されます(chat_456 についても同じことが言えます)。


セキュリティ ルールの動作を理解する

私は Firebase セキュリティのエキスパートではありませんが、ブログ投稿ではそのふりをすることができます。その秘訣は、ルール シミュレータです。

ルールを変更する際、それを発行する前に Database への読み取りや書き込みをシミュレートしてテストすることができます。Firebase コンソールの Database 配下にある Rules タブの右上には、「SIMULATOR」と書かれるボタンがあります。クリックするとフォームが表示され、そこから任意の種類の読み取り操作や書き込み操作をテストできます。

今回の例では、最後のルールについて、"user_zzz" としてログインしているユーザーで自分自身を所有者として空の /chats/chat_987/members リストに追加する操作をテストしてみます。すると、ルール シミュレータにこの操作は許可されている旨が表示され、書き込みアクションが true と評価された場所がハイライト表示されます。


(厳密に言えば、間違った行がハイライト表示されており、実際に true と評価されるのはルールの 13 行目です。おそらく、ハイライト表示は文字列内の改行を細かく認識していないものと思われます)

一方、このユーザーが空でないリストに自身を所有者として追加しようとすると失敗します。これは想定どおりの動作です。


さらなる改善

さらにいくつかの改善を行うことができます。現在の状況では、所有者は他のメンバーを所有者として追加できるようになっています。実際は、これが望ましい場合もそうでない場合もあるでしょう。

考えてみると、新しいメンバーが適切な役割で追加されているかどうかを検証してはいませんでした。また、UI が扱うことができる長さにチャット メッセージが収まるようにする検証ルールもいくつか追加できるでしょう。この点については、ぜひいろいろ試してみてください。

次に示すのは、最終的なルールです。これをご自分のチャットアプリにコピーして貼り付け、どのように改善できるかを考えてみてください。

{
  "rules": {
    "chats": {
      "$chatID": {
        "messages": {
          ".read": "data.parent().child('members').child(auth.uid).exists()",
          ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'"
        },
        "members": {
          ".read": "data.child(auth.uid).val() == 'owner'",
          ".write": "data.child(auth.uid).val() == 'owner' ||(!data.exists()&&newData.child(auth.uid).val()=='owner')"
        },
        "pending": {
          ".read": "data.parent().child('members').child(auth.uid).val() === 'owner'",
          ".write": "data.parent().child('members').child(auth.uid).val() === 'owner'",
          "$uid": {
            ".write": "$uid === auth.uid && !data.exists() && !data.parent().parent().child('members').child($uid).exists()"
          }
        }
      }
    }
  }
}

さらにヘルプが必要な場合は、ぜひドキュメントをご覧いただき、シミュレータでいろいろ試してみてください。シミュレータで Database のセキュリティ ルールを試してみれば、きっと楽しい 1 週間を過ごすことができるでしょう。少なくとも、最初の 3 日は楽しめます。


Posted by Khanh LeViet - Developer Relations Team
Share on Twitter Share on Facebook