Ruby
Rails
Firebase

RubyでFirebaseを利用して認証を行う

Rubyで Firebase で認証を行う実装のサンプルが見当たらなかったので業務で実装したものをまとめてみました。RubyでのFirebaseの活用例が増えれば幸いです。


Firebaseでの認証

Firebase Auth REST API を利用してユーザー登録、認証、セッショントークンの管理を行います。

Firebaseの uid からJWTを生成し、クライアントとの認証はJWTを介して行います。

Firebase Auth REST API のラッパーとして firebase-auth が便利だったので利用しています。

なお、Firebaseを利用した認証はセッションレスで常にJWTを介してリクエストを処理します。

トークンの有効期限が切れる毎に認証操作が発生するのを防ぐためにはクライアント側で発行されたJWTを保持する必要があります。

Firebaseのセッショントークンの有効期限は1時間です。

有効期限を過ぎたらリフレッシュトークンで新たにセッショントークンを生成する必要があります。


注意事項

Firebase Auth REST API は個人用(ログインユーザー)のAPIしか提供されていません。

例えば一部の不正行為をしたユーザーを利用停止するなどの操作がAPIでは実現できません(Firebaseコンソールからは可能)。

公式のSDK が提供されているNode.jsやGoといった言語は管理者用の操作も実行できます。

組織やグループ管理を扱うアプリケーションではFirebaseだけではユーザー管理に支障がでるので独自のユーザー管理の仕組を導入する必要があります。


サンプルアプリケーションの説明

firebase-auth-api をGitHubで公開しているので参考にしてください。

RailsのAPIサーバーになります。


実装の特徴

ユーザー管理をアプリケーションで行えるようにするためにFirebaseのuidを直接利用するのではなく users テーブルを導入しています。

users.deleted = true のユーザーはアカウントが無効となり、認証に失敗します。

トークンの更新時には必ずFirebaseでの認証と users.deleted の確認を行い無効になったユーザーがアプリケーションを不正操作し続けるのを防いでいます。

なお、JWTは独自に生成しているので有効期限を設定しないことも可能ですが、セキュリティ上の大きなリスクを生じることになるのでFirebaseに合わせて有効期限を1時間にしています。

Firebase::AuthClient がFirebaseへのリクエストを司どっています。

Firebase::AuthClient ラッパークラスで実際にリクエストを発行しているのは firebase-auth になります。

以下はメールアドレス・パスワードでの認証処理の実装の抜粋です。

module Firebase

class AuthResponse
attr_accessor :uid, :id_token, :refresh_token

def initialize(raw_response)
@uid = raw_response.body['localId'] || raw_response.body['user_id']
@id_token = raw_response.body['idToken'] || raw_response.body['id_token']
@refresh_token = raw_response.body['refreshToken'] || raw_response.body['refresh_token']
end
end

class AuthenticationException < StandardError; end

class AuthClient
def self.client
Firebase::Auth::Client.new(FIREBASE::API_KEY)
end

def self.sign_in_email!(email, password)
response = client.sign_in_email(email, password)
unless response.success?
message = ERROR_MESSAGES[response.body['error']['message']] || 'ログインに失敗しました'
raise Firebase::AuthenticationException.new(message)
end

Firebase::AuthResponse.new(response)
end
end
end


動作環境・バージョン

Ruby: 2.5.1

Rails: 5.2.0


セットアップ

bundle install 実行後に users テーブルを作成します。

なお、DBはPostgreSQLを前提にした実装になっているので他のDBを利用する場合は config/initializers/constants.rb の定数を書き換えてください。

bundle install

bin/rake db:migrate


Firebase

Firebaseプロジェクトで ログイン方法メール / パスワード を有効にしてユーザーを事前に登録してください。

firebase-authentication.png


環境変数

以下の設定を環境変数で指定します。

キー
値の例
説明

JWT_ALGORITHMS_TYPE
HS256
JWTの署名アルゴリズム

JWT_EXPIRATIONTIME
3600
セッショントークンの有効期間(秒)

JWT_ACTIVITY_API_SECRET_KEY
dummy
任意の文字列

FIREBASE_API_KEY
AIz...
FirebaseプロジェクトのAPIキー


API

以下のAPIを用意しています。

ユーザーの登録から、メールアドレス・パスワードでの認証、ユーザーの無効化が行えます。

URI
メソッド
パラメーター
説明

/session
POST
メールアドレス、パスワード
認証、セッショントークンの生成

/session
PUT
-
セッショントークンのリフレッシュ

/user
POST
メールアドレス、パスワード、ユーザー名
ユーザーの登録

/user/:id
DELETE
-
ユーザーの無効化

/user/current
GET
-
ログインユーザーの情報を取得

クライアントからのHTTPリクエストには以下の形式のHTTPヘッダーを付加してください。

JWT にはセッショントークンをセットします。

トークンのリフレッシュ処理(PUT /session)を呼び出す場合だけリフレッシュトークンをセットします。

Authorization: Bearer eyJ...(JWT)

POST /user でもセッショントークンを要求しているのは組織やグループで利用する想定だからです。

その場合にユーザー登録をするのはグループのリーダーや組織の管理者となります。

一般ユーザー向けのアプリケーションなら POST /user は認証無しで実行できるようにしてください。


curlでのコマンド例

動作検証のためのcurlコマンドのサンプルです。

最初に POST /session で認証し、セッショントークン・リフレッシュトークンを取得してください。

他のAPIはセッショントークン、またはリフレッシュトークンが必要になります。


認証

curl -i -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{"email": "user01@example.com","password": "dummy1234"}' http://localhost:3000/session


ユーザー登録

Authorization ヘッダーにはセッショントークンを設定します。

curl -i -H "Accept: application/json" -H "Content-type: application/json" -H "Authorization: Bearer eyJ..." -X POST -d '{"email": "user01@example.com","password": "dummy1234", "name":"user01"}' http://localhost:3000/user


ログインユーザーの情報取得

URIが /user/:id でない点に注意です。

ユーザーを特定するためのidはセッショントークンから取得するのが基本になります。

curl -i -H "Accept: application/json" -H "Content-type: application/json" -H "Authorization: Bearer eyJ..." http://localhost:3000/user/current


セッショントークンのリフレッシュ

Authorization ヘッダーにはリフレッシュトークンを設定します。

curl -i -H "Accept: application/json" -H "Content-type: application/json" -X PUT -H "Authorization: Bearer AK2..." http://localhost:3000/session