Ruby
Android
Rails
JWT
Realm

Realm CloudのJWT認証を利用するための環境をRailsで作る

はじめに

railsアドベントカレンダー1日目が投稿されていなかったため代わりに投稿します。

皆様、Realm Cloud使ったことありますか?
アプリ向けの組み込みデータベースをクラウドにも対応させたもので、最大の特徴はオフラインでデータ更新が出来、オンラインに繋がればデータをシームレスに同期してくれるという点です。これが非常に便利なんですが今の時代、オフライン利用の需要がないのか日本語の情報が非常に少ないです…
今回はそんな不遇な存在だけど非常に便利なRealm CloudをJWT(Json Web Token)認証で利用するためのRails環境の構築及びRealm Cloudの設定をしてみます。

やり方

  • まずは環境です。下記の環境使います。
Gemfile
ruby '2.5.3'

gem 'rails', '~> 5.2.1'
gem 'jwt'
  • 次にRealm CloudのJWT認証は、署名する際にRS256が必要なため秘密鍵と公開鍵を作成します。
shell
openssl genrsa -out private.pem 4096
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
  • 次に先ほど作成したprivate.pemをcredentialsに登録します。
shell
EDITOR='vi' bin/rails credentials:edit
  • 改行データを登録する場合はパイプを入れて下記のように。
config/credentials.yml
rsa_private: |
  -----BEGIN RSA PRIVATE KEY-----
  MIIJKAIBAAKCAgEArdxxxxxxxxxxxxxxxxx
  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  
  -----END RSA PRIVATE KEY-----

  • Railsサーバー側で暗号化するため、encode用のクラスを作成。
lib/json_web_token.rb
class JsonWebToken
  class << self
    def encode(payload)
      JWT.encode(payload,
                 OpenSSL::PKey::RSA.new(Rails.application.credentials.config[:rsa_private]),
                 'RS256'
                )
    end
  end
end
  • コントローラー側でRealmに登録するためのuser_idキーと値のu2を設定してあげます。
app/controllers/api/v1/json_web_tokens_controller.rb
class Api::V1::JsonWebTokensController < ApplicationController
  # devise等使って適宜制限掛けてあげる
  before_action :authenticate_api_user!, only: [:create]

  # json_web_token作成
  def create
    render json: payload(current_api_user)
  end

private
  # 今回は`u2`というcodeを使ってみる。
  def payload(user)
    return nil unless user and user.id
    {
      jwt: JsonWebToken.encode({user_id: user.code}) 
    }
  end

  def http_token
    @http_token ||= params['data']
  end

  def auth_token
    @auth_token ||= JsonWebToken.decode(http_token)
  end

  def user_id_in_token?
    http_token && auth_token && auth_token[:user_id].to_i
  end
end
  • routesはこんな感じです。
config/routes.rb
# https:///api/v1/json_web_token
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resource :json_web_token, only: [:create]
    end
  end
  • ここまで来るともう少しです。次にRealm Cloudにブラウザからログインし、JWT認証を設定したいインスタンスを選択してSettingsタブを開きます。下記はすでに設定済みのためEnabled
    になってますが、デフォルトはDisabledです。
    スクリーンショット 2018-12-02 0.34.29.png

  • 次にJWTを展開するとPublic KeyとUserID fieldを設定する箇所があるのでここに前述の公開鍵とrealmに登録する際に使用するキー名のuser_idを設定してあげてSaveします。

Untitled.png

ここまでくればサーバー側の設定は完了です!今回はAndroidで接続テストしてみます。

// 先程作成したRailsサーバーからJWTを取得し、その値をcredentialsに設定します。
val credentials = SyncCredentials.jwt(requireNotNull(success.body()).jwt)
    SyncUser.logInAsync(credentials, "https://example.us1a.cloud.realm.io/auth", object : SyncUser.Callback<SyncUser> {
        override fun onSuccess(result: SyncUser) {}
        override fun onError(error: ObjectServerError) {}
    })
  • 上記のコードを実行してRealm Studioで確認してみると、、、無事、u2というユーザーが作成されました!

スクリーンショット 2018-12-02 0.50.26.png

最後に

このあたりの日本語の情報があまりなく英語のドキュメントのみでかなり苦戦しました…(英語勉強したい)
jwtサーバーを立てる上でこちらのサイトが大変参考になりました。
http://blog.naoshihoshi.com/entry/2018/05/14/153000

皆様のお役にたてれば幸いです!