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 を使う、みたいなことができて楽ができる。

10 Mar 2022 Thu 01:41 (UTC)