Diary

@ssig33

Firebase で普通の Rails アプリにログイン したい、という要求がある。

Firebase + Rails というコンテキストでよくあるのは

  • クライアントサイドは普通の React アプリや iOS アプリで Firebase をつかって認証
  • Rails 側は API を提供し、 Firebase で生成した idToken でユーザーを特定

みたいな事例だと思う。この時 Rails 側は基本的にセッション管理の必要がない。

ただ今回は Firebase で認証して Rails のセッションを作りたい。ようするに、ユーザーの管理とログインまわり(典型的には Google とか Github とかを使うだろう)だけを Firebase にやらせたい。

ではどうするか、といえば以下のような感じにすればよい。

まずログインページは以下のような感じにする。

<button id="login">ログイン</button>

<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.6.8/firebase-app.js";
import { GoogleAuthProvider, getAuth, signInWithPopup } from "https://www.gstatic.com/firebasejs/9.6.8/firebase-auth.js";
const firebaseConfig = {
  ...
};

const app = initializeApp(firebaseConfig);
const authenticate = async ()=>{
  const provider = new GoogleAuthProvider();
  const auth = getAuth(app);
  const result = await signInWithPopup(auth, provider);
  return result.user;
}

document.querySelector("button#login").addEventListener("click", async ()=>{
  const user = await authenticate();
  const idToken = await user.getIdToken();
  document.querySelector("input#idToken").value = idToken;
  document.querySelector("form").submit();
});
</script>

<div style="display:none">
  <%=form_tag("/login", :method => :post) do %>
    <%=hidden_field_tag("authenticity_token", form_authenticity_token)%>
    <%= text_field_tag("idToken", "") %>
  <% end %>
</div>

このようにすればログインボタンを押せば Firebase でログインし、ログインに成功すれば Firebase の idToken が /login という Rails の action に post される。

/login の先の Rails のサーバーサイドは以下のようなかんじ。

def login
  jwks =
    JSON.parse RestClient.get(
                  'https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com'
                ).body
  id_token = params[:idToken]
  payload, = JWT.decode(id_token, nil, true, { algorithms: ['RS256'], jwks: jwks })
  session[:user_id] = payload['uid']
  redirect_to home_path
end

ようするに、 API 提供するときと同じように idToken を検証して Rails の session を吐き出してしまえばよい。

こうすれば普通の Rails アプリにたいしてログイン部分だけ Firebase を使う、みたいなことができて楽ができる。