メインコンテンツまでスキップ

FIDO2/WebAuthn FAQ・トラブルシューティング


概要

FIDO2/WebAuthn実装時によく遭遇する問題とその解決方法をFAQ形式でまとめています。


FAQ

Q1: Touch IDが認証時に表示されない(QRコードのみ表示される)

症状:

  • Passkey登録は成功した
  • 認証時にTouch IDオプションが表示されない
  • 「Use your phone or tablet」とQRコードのみ表示される

原因と解決方法:

原因1: rpIdの不一致

問題: 登録時と認証時でrpIdが異なる

登録時: rpId = "local.test"
認証時: rpId = "auth.local.test" (オリジンからのデフォルト値)

解決方法: 認証チャレンジのレスポンスにrpIdを明示的に含める

// サーバーレスポンス
{
"challenge": "...",
"rpId": "local.test", // または rp.id でネスト
"allowCredentials": [...]
}
// フロントエンド
const publicKeyOptions = {
challenge: base64UrlToBuffer(challenge),
rpId: response.rpId || response.rp?.id, // 両方の形式に対応
allowCredentials: [...]
};

ポイント:

  • rpIdを省略すると、ブラウザは現在のオリジンのドメインを使用
  • サブドメインデプロイ(auth.local.test)では親ドメイン(local.test)を明示的に指定する必要がある
  • 登録時と認証時で同じrpIdを使用すること

原因2: transportsが含まれていない

問題: allowCredentialstransportsが含まれていない

// NG: transportsなし
{
"allowCredentials": [
{
"type": "public-key",
"id": "credential_id_base64url"
}
]
}

// OK: transportsあり
{
"allowCredentials": [
{
"type": "public-key",
"id": "credential_id_base64url",
"transports": ["internal", "hybrid"]
}
]
}

解決方法:

  1. 登録時にtransportsをデータベースに保存
  2. 認証時にallowCredentialstransportsを含める

transportsの値と意味:

説明
internalプラットフォーム認証器Touch ID, Face ID, Windows Hello
hybridクロスデバイス(QRコード経由)スマホのPasskey
usbUSB接続YubiKey等のセキュリティキー
nfcNFC接続NFCセキュリティキー
bleBluetooth接続Bluetoothセキュリティキー

なぜtransportsが重要か:

  • ブラウザはtransportsを参考に適切な認証器UIを表示
  • internalが含まれていればTouch ID/Face IDを優先表示
  • 省略すると、ブラウザはすべての通信方式を試行(UXが悪化)

Q2: authenticatorAttachmentとtransportsの違いは?

authenticatorAttachment - 登録時に使用

説明用途
platformデバイス内蔵認証器のみ許可Touch ID, Face ID, Windows Hello
cross-platform外部認証器のみ許可セキュリティキー、スマホ
省略/null制約なしどちらでもOK
// 登録時のオプション
const createOptions = {
publicKey: {
authenticatorSelection: {
authenticatorAttachment: "platform", // Touch IDのみ許可
userVerification: "required"
}
}
};

transports - 認証時に使用

説明
internalプラットフォーム認証器経由
hybridクロスデバイス経由
usbUSB経由
nfcNFC経由
bleBluetooth経由
// 認証時のオプション
const getOptions = {
publicKey: {
allowCredentials: [{
type: "public-key",
id: credentialId,
transports: ["internal", "hybrid"] // 通信方式のヒント
}]
}
};

まとめ:

  • authenticatorAttachment: 登録時に「どの種類の認証器を使うか」を制限
  • transports: 認証時に「どの通信方式で認証器に接続するか」のヒント

Q3: サブドメインデプロイでのrpId設定

シナリオ:

API:  https://api.local.test
認証: https://auth.local.test
Web: https://sample.local.test

正しいrpId設定:

{
"rp": {
"id": "local.test",
"name": "My Service"
}
}

ルール:

  1. rpIdは現在のオリジンと同じか、その親ドメインである必要がある
  2. auth.local.testではlocal.testまたはauth.local.testを使用可能
  3. 複数サブドメインで同じPasskeyを使うなら、共通の親ドメインを使用

NGパターン:

オリジン: https://auth.local.test
rpId: "other.local.test" // NG: 兄弟ドメインは不可
rpId: "dev" // NG: 有効なeTLD+1ではない

OKパターン:

オリジン: https://auth.local.test
rpId: "local.test" // OK: 親ドメイン
rpId: "auth.local.test" // OK: 完全一致

デバッグ手順

1. ブラウザの開発者ツールでレスポンスを確認

Network → fido2-authentication-challenge → Response

確認項目:

  • rpIdまたはrp.idが含まれているか
  • allowCredentialstransportsが含まれているか
  • Credential IDが正しいか

2. サーバーログを確認

# transportsのパース状況
grep "parseTransports" logs/app.log

# allowCredentialsの生成
grep "allowCredentials" logs/app.log

3. データベースを確認

SELECT id, username, rp_id, transports
FROM webauthn_credentials
WHERE tenant_id = 'your-tenant-id';

確認項目:

  • rp_idが期待値と一致するか
  • transportsが正しく保存されているか

参考リソース


関連ドキュメント