Railsでgemのapple_idを使ってSign in with Appleを実現し、AppleのJWKをキャッシュする


RailsアプリでSign in with Appleを行う場合、Sign in with Appleは結局の所OpenID Connectなので、id_tokenの検証をRailsで行う必要があります。
その検証はRubyであれば https://github.com/nov/apple_id このgemを使うとかんたんに行えます。

apple_idを使ってid_tokenの検証をする

id_token = AppleID::IdToken.decode(id_token)
id_token.verify!(code: code)

こんな感じでid_tokenの検証を行えるのですが、verify!メソッドの中では何が行われているかと言うと、Appleが提供する公開鍵(JWK)をHTTPSで取得し、そのJWKを用いて署名検証をしています。
デフォルトではverifyするたびにAppleにHTTPSで通信をしていますが、この公開鍵はほとんど更新されないためキャッシュすることが可能で、サーバ実装としてもキャッシュしたほうが望ましいです。

gemのapple_idは最近1.0.0がリリースされ、このバージョンアップによってJWKを任意の場所にキャッシュすることが可能になりました。
本記事では、そのキャッシュのやり方を紹介します。

キャッシュの方法

キャッシュのやり方は至ってかんたんで、引数を一つだけとるfetchメソッドを実装したクラスをAppleID::JWKS.cacheにセットするだけです。
fetchメソッドの中でyieldを呼び出すことで、JWKが取得できます。
例えばプロセスにキャッシュするのであれば以下のようなコードを書きます。

class ProcessCache
  def initialize
    @cache = {}
  end

  def fetch(cache_key)
    @cache[cache_key] ||= yield
  end
end

AppleID::JWKS.cache = ProcessCache.new

AppleID::JWKS.cacheはクラス変数として定義されるので、Cacheクラスの実装としてはインスタンス変数に代入すればプロセスにキャッシュされることになります。
プロセスではなく、ファイルやキャッシュサーバ、DBなどに保存したくなったとしても、同様にfetchメソッドを実装すれば差し替え可能で便利ですね。
Cacheクラスはappやlib以下に実装し、config/initializers/apple_id.rbなどを用意して、そこでAppleID::JWKS.cacheへの代入を行うなどすれば良いでしょう。