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