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プロジェクトで ログイン方法
で メール / パスワード
を有効にしてユーザーを事前に登録してください。
環境変数
以下の設定を環境変数で指定します。
キー | 値の例 | 説明 |
---|---|---|
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