前回までで、Firebase上にさまざまな方法でデータを保存し、用途に合わせたいろいろなコールバックを使ってデータを読み出す方法を学んできました。簡単なアプリならばこれまでの知識で十分作ることができますが、今回はサービスを安全に提供する上で欠かすことのできない「セキュリティ」について解説して行きたいと思います。
今回も、前回の連載に引き続き、例としてリアルタイムチャットサービスを取り上げます。
Firebaseを適切に設定することで、
- メールアドレスとパスワードを使ってログイン処理を行う
- チャットメッセージは適切にログインしたユーザしか閲覧もメッセージの投稿もできない
といったよくある機能を簡単に実現することができます。
ユーザ認証とアクセス制御
Authentication(認証)
「セキュリティ」を考える場合に、そのユーザが「誰であるのか」を確認することは不可欠です。 「誰であるのか」がわかって初めて、
- 「Aさんは
/messages/01
を閲覧できる」
- 「Bさんは
/messages/02
を閲覧・書き込みできる」
といったアクセス制御が可能です。
この「誰であるのかを確認する」作業をAuthentication(認証)と言います。
Firebaseは次の表にある認証方法を提供しており、アプリに柔軟な認証機能を簡単に組み込むことができます。
認証の種類 | 概要 |
大手SNS認証 | Facebook, GitHub, Google, Twitter アカウントを使った認証 |
パスワード認証 | Firebase自身にEメール、パスワードを登録し、それを使った認証 |
匿名ログイン | 一時的な匿名ログイン |
カスタムログイントークン | すでに自前で持っている認証基盤との連携 |
今回は、Firebase自身にEメール、パスワードを登録して、それを使ってユーザ認証を試してみます。
今後の連載中では、実践テクニックとしてTwitterアカウントを使った認証もご紹介する予定ですので、楽しみにしてください。
Authorization(認可)
ユーザ認証によって、そのユーザが「誰であるか」がわかり、したがって「何をすることができ、どこにアクセスすることができる」というようなことがわかります。こういった情報をAuthorization(認可)と呼びます。
Firebaseではこの認可情報でユーザを識別し、「アカウントダッシュボード」の「Firebase Rules」と呼ばれる設定ルールに従って細やかなアクセス制御を実現することができます。
たとえば、以下のルールは「/foo
は誰でも閲覧できるが、書き込みはできない」という意味になります。詳しくは本連載で解説して行きたいと思います。
パスワード認証
今回の連載では、Firebase自身にEメール、パスワードを登録する「パスワード認証」を利用します。
パスワード認証を利用するには、ダッシュボードから「Login & Auth」を選択し、「Enable Email & Password Authenticaion」にチェックを入れます。
これで準備は整いました。
さっそくFirebase上にユーザを作成するコードを見て行きましょう。
ユーザの作成
まずは以下のコードを実行してみてください。
まず、1行目でFirebaseのルート参照を取得します。
次に、ユーザを作成するためにcreateUser()
メソッドを呼び出します。createUser()
は第1引数にEメールアドレス、第2引数にパスワードを取ります。
認証の結果は、第3引数のFirebase.ValueResultHandler
インタフェースで受け取ることができます。成功した場合はonSuccess()
の第1引数に認証結果がMap
形式で渡されます。ここではユーザを一意に識別するuid
を取得しています。
以下のようにログ出力されれば成功です。
失敗した場合はonError()
の第1引数にFirebaseError
形式でエラーが渡されます。FirebaseError
からはgetMessage()
やgetDetail()
でエラー理由を取得することができます。
たとえば、すでに存在するユーザをもう一度作成しようとした場合は、以下のようにエラー出力されます。
登録済みユーザはダッシュボードから確認することができます。
uid
このuid
は、Firebaseが提供するさまざまな認証方法によらず、一意であることが保証されています。同じEメールアドレスとパスワードを使ったとしても、「Twitter認証」や「Facebook認証」といった具合に認証プロバイダが異なれば、きちんと違うユーザと見なされますので、ここで取得できるuid
をアプリで一意なユーザのユニークキーとして利用するのは正しい使い方です。
ユーザの認証
ユーザの作成ができたので、さっそくパスワード認証を試してみましょう。
パスワード認証にはauthWithPassword()
メソッドを利用します。第1引数にEメールアドレス、第2引数にパスワードを指定し、認証結果は第3引数のFirebase.AuthResultHandler
で受け取ります。
認証に成功すると、onAuthenticated()
の第1引数にAuthData
オブジェクトが渡されます。以下のようにログ出力されれば成功です。
エラーはユーザ登録の時と同様、FirebaseError
形式で受け取ります。以下は、認証パスワードを間違えた際のエラー例です。
AuthData
AuthDataはさまざまな認可情報が詰まった非常に重要なデータです。AuthDataからは以下のような情報を取得することができます。
プロパティ | 概要 |
uid | ユーザを一意に識別するID |
token | 認証トークン |
expires | 認証の有効期限を表現するUNIXタイム |
provider | 認証プロバイダ情報 |
他にも、認証プロバイダの種類によって、プロフィール画像等の重要な情報を取得することができます。
また、expires
の期限が過ぎるとこの認証は無効になってしまうため、プログラマが適切に管理してユーザに再認証を促す必要があります。認証の有効期限はデフォルトで24時間です。
認証の解除
認証は、有効期限が切れる前でも ref.unauth()
メソッドを呼び出すことでいつでも失効させることができます。ユーザログアウト時等に利用することが考えられるでしょう。
アクセス制御
ここまでで、Firebase上にユーザを作成し、パスワード認証で認証する方法をみてきました。ここからは、認証したユーザの特定のパスに対するアクセス制御について確認してみたいと思います。
アクセス制御は、ダッシュボードの「Security & Rules」タブを選択し、「Firebase Rules」ルールを編集することで設定することができます。
ここでは、前回に引き続き、チャットメッセージ一覧を格納する/messages
を使い、ここへのアクセスは認証済みのユーザにのみ許可するように設定変更してみたいと思います。
まず、rules
直下の.read
と.write
を両方ともfalse
にします。
Firebase Rulesは、上の階層から下の階層に伝播するように設計されており、上位のパスで許可されたアクセス件を下位のパスの設定で覆すことができないようになっています。したがって、まずは最上位のルールをfalse
にし、その後任意のパスのルールを設定していくのが大原則となります。
次に、messages
に認証済みのユーザのみアクセス可能とします。認証済みのユーザは、uid
という組み込み変数に必ず値が入るので、uid != null
とすることで認証済みのユーザかどうかを判定します。
設定が終わったら、「SAVE RULES」ボタンを押してルールを保存してください。
試しに、未認証の状態で/messages
の情報の取得を試み、以下のようなエラーが出力されることを確認してください。
次に、ユーザを認証して、これまでの連載同等メッセージ情報が取得できることを確認してください。きちんと情報が取得できれば成功です。
さまざまな組み込み変数
Firebase Rulesには、uid
以外にもさまざまな組み込み変数が用意されており、非常に柔軟な認証を実現することができます。
組み込み変数 | 概要 |
now | Firebaseサーバの現在時刻 |
root | Firebaseデータベースのrootパス |
newData | これから書き込まれるデータ |
data | 現在このパスに存在するデータ |
$variables | データのIDに動的にマッチする変数。後述 |
auth | 認証情報 |
この内、$variables
は非常によく使うので使い方を解説します。
$ variables
$variables
は、特定のパスの下のデータのIDに動的にマッチする変数です。$
に任意のアルファベットで変数名を付けるだけで、任意のデータにマッチします。
たとえば、次のようなルールがあった場合、$user_id
は任意のユーザIDににマッチします。マッチさせたIDはアクセス制御に利用します。
auth
組み込み変数を使ってuid
を比較し、特定のuid
を持つユーザが自分のuid
と同じパスにアクセスした場合にのみデータの書き込みを許可しています。
組み込み変数と$variables
を組み合わせると、思いのままのアクセス制御が可能です。ぜひ、いろいろ工夫してアクセスルールを考えてみてください。
今後の連載では実践テクニックとして細やかなアクセス制御のルールを公開予定です。楽しみにしていてください。
まとめ
今回の連載では、ユーザ認証とアクセス制御を組み合わせることで、アプリに柔軟でセキュアな認証基盤を簡単に付与することができるのを見て行きました。
さて、次回は、Firebaseのデータベース構造についてより深く踏み込み、どうすればより高速で効率的なデータ設計を実現できるのか見て行きたいと思います。どうぞお楽しみに。