🧩

webauthn-rubyでマルチドメイン対応する方法

に公開

はじめに

これはGLOBIS Advent Calendar 2025 シリーズ2の17日目の記事です。

こんにちは!グロービスのtsuki_です。
普段は認証システムの開発に携わってます。
初めて記事を書くのでドキドキしていますが、よろしくお願いします!

今回は、Passkey(WebAuthn)をRailsで実装する際に、RP(Relying Party)周りでちょっと引っかかったのでメモとして残しておきます。

具体的には、同じRailsアプリで複数のドメイン(例:example.comauth.example.com)を扱っていて、それぞれでWebAuthnを使いたいケースです。

webauthn-rubyのInstance Based Configurationを使うことで解決できたので、その方法を共有します。

前提知識

使用ライブラリ

https://github.com/cedarcode/webauthn-ruby

RP ID(Relying Party ID)とは

WebAuthnにおいて、RP IDはサーバー(Relying Party)を識別するためのIDです。通常はドメイン名を使用します。

例えば、example.com でWebAuthnを使う場合、RP IDは example.com になります。

認証器(Authenticator)は、このRP IDと紐づけてクレデンシャル(認証情報)を保存するため、異なるRP IDでは同じクレデンシャルを使えません

webauthn-rubyのデフォルト設定(Global Configuration)

webauthn-rubyでは、通常initializersで以下のようにグローバル設定を行います。

# config/initializers/webauthn.rb
WebAuthn.configure do |config|
  config.allowed_origins = ["https://example.com"]
  config.rp_id = "example.com"
  config.rp_name = "Example App"
end

この設定はアプリケーション全体で共有され、WebAuthn::Credential.options_for_create などのメソッドで自動的に使用されます。

デフォルト設定の問題点:マルチドメイン環境

例えば、同じRailsアプリで以下の2つのドメインを扱っているとします:

ドメイン 用途
example.com メインサービス
auth.example.com 認証専用

WebAuthnの仕様上、RP IDを親ドメイン(example.com)に設定すれば、サブドメイン(auth.example.com)からも利用できます。

WebAuthn.configure do |config|
  config.allowed_origins = ["https://example.com", "https://auth.example.com"]
+ config.rp_id = "example.com"  # サブドメインからも使える
end

しかし、ここで問題が発生します。

auth.example.com で登録したパスキーも、RP IDは example.com に紐づきます。そのため、example.com で認証しようとしたときに、auth.example.com で保存したパスキーも候補として表示されてしまいます

同様に、stg.example.comdev.example.com などの検証環境で登録したパスキーも、本番環境で候補として表示されてしまいます。

これはユーザー体験として混乱を招きます。ドメインごとに別々のパスキーを管理したい場合は、RP IDを分ける必要があります。

この問題を解決するのが、次に紹介するInstance Based Configurationです。

解決策:Instance Based Configuration

Global Configurationとの違い

項目 Global Configuration Instance Based Configuration
設定方法 WebAuthn.configure で一度だけ設定 WebAuthn::RelyingParty.new で都度作成
設定の数 アプリ全体で1つ 複数のインスタンスを作成可能
用途 単一ドメインのアプリ マルチドメイン、マルチテナントなど

Instance Based Configurationを使えば、リクエストごとに異なる設定を使い分けることができます。

基本的な使い方

公式ドキュメントによると、Global ConfigurationとInstance Based Configurationは共存できます。

Adding a configuration for a new instance does not mean you need to get rid of your Global configuration. They can co-exist in your application and be both available for the different usages you might have. WebAuthn.configuration.relying_party will always return the global one while WebAuthn::RelyingParty.new, executed anywhere in your codebase, will allow you to create a different instance as you see the need. They will not collide and instead operate in isolation without any shared state.

https://github.com/cedarcode/webauthn-ruby/blob/38471d6718ae922613f25bddc0168c1475aa5c25/docs/advanced_configuration.md?plain=1#L172-L174

つまり、既存のGlobal Configurationを残したまま、必要な箇所でインスタンスを作成できます。

  • Global Configurationは WebAuthn.configuration.relying_party で参照される
  • Instance Based Configurationは WebAuthn::RelyingParty.new で作成する
  • 両者は独立して動作し、状態を共有しない
# config/initializers/webauthn.rb(そのまま残す)
WebAuthn.configure do |config|
  config.allowed_origins = ["https://example.com"]
  config.rp_id = "example.com"
  config.rp_name = "Example App"
end
+ # 必要な箇所でインスタンスを作成
+ relying_party = WebAuthn::RelyingParty.new(
+   allowed_origins: ["https://auth.example.com"],
+   id: "auth.example.com",
+   name: "Example App"
+ )

メソッドの呼び出し方も変わります。

登録(Registration)

- options = WebAuthn::Credential.options_for_create(user: ...)
- credential.verify(challenge)
+ options = relying_party.options_for_registration(user: ...)
+ relying_party.verify_registration(credential, challenge)

認証(Authentication)

- options = WebAuthn::Credential.options_for_get(allow: ...)
- credential.verify(challenge, public_key:, sign_count:)
+ options = relying_party.options_for_authentication(allow: ...)
+ relying_party.verify_authentication(credential, challenge)

注意点

RP IDを変更すると、変更前のRP IDで登録されたパスキーは使えなくなります。

例えば:

  • Global Configurationで rp_id = "example.com" でパスキーを登録していた
  • Instance Based Configurationで id = "auth.example.com" に変更
  • → 既存のパスキーは example.com に紐づいているので、auth.example.com では検証できない

既存ユーザーのパスキーを移行する場合は、再登録が必要になります。

まとめ

  • マルチドメイン環境でWebAuthnを使う場合、RP IDを親ドメインに統一すると、意図しないパスキーが候補に表示される問題がある
  • webauthn-rubyのInstance Based Configurationを使えば、ドメインごとに異なるRP IDを設定できる
  • Global ConfigurationとInstance Based Configurationは共存可能
  • RP IDを変更すると既存のパスキーは使えなくなるので、導入時は注意

参考:webauthn-ruby 公式ドキュメント


ではでは、メリークリスマス & 良いお年を!🎄🎍

GLOBIS Tech

Discussion