Azure AD B2C(Microsoft Entra External ID for customers)で電話番号を使ったサインアップ/サインイン(MFA・SMSワンタイムコード)を導入すると、検証コード画面でセッションが失効した際に英語の「expired」系メッセージが出続ける――そんな“最後の一文だけ翻訳できない”問題に直面するチームは少なくありません。本記事では原因の正体、設計判断、実運用での回避策と実装テンプレートを、カスタムポリシー視点で整理します。
問題の全体像と前提
前提は次のとおりです。
- カスタムポリシー(IEF)で電話番号を用いたサインアップ/サインインを構成し、SMSワンタイムコード(OTP)で検証している。
- 誤ったコード入力や試行回数超過などは
Localizationブロックで通常どおり翻訳できる。 - ところが、検証画面の滞在中にセッションが失効(OTPの有効期限切れ、あるいは検証セッションのタイムアウト)した場合に表示されるメッセージだけが英語固定で表示される。
この現象は特定の実装ミスではなく、仕組み上の制約に由来します。
結論(サマリー)
- 「検証セッションの期限切れ」メッセージは、MFAバックエンドが返す固定文言であり、カスタムポリシーの
Localizationからはローカライズ不可です。 StringId(例:UserMessageIfWrongCodeEntered)が提供されているエラーは翻訳できますが、「期限切れ」専用のStringIdは存在しません。- 実務対応は、UIで「コードを再送信」の導線を明示し、ローカライズ可能なメッセージは確実に多言語化しつつ、ロードマップ確認と要望提出で将来の改善を促す、の三本柱が最も現実的です。
なぜ翻訳できないのか:内部アーキテクチャの要点
Azure AD B2C(Entra External ID)のカスタムポリシーは、概ね以下のレイヤで構成されます。
- ページUI:
ContentDefinitionとページテンプレート(HTML/CSS/JS)。 - ユーザー入力・自己主張(Self-Asserted):フォーム送信と表示制御。
- 技術プロファイル(Technical Profile):
PhoneFactor系などの呼び出し。 - バックエンドの検証サービス:OTPの生成・送信・検証を司るMicrosoft提供のMFAサービス。
このうち、翻訳辞書(Localization)が直接効くのは主に UI テキストと Self-Asserted の汎用エラー領域です。一方で、MFAサービスが検証結果とともに返す一部のメッセージは「サーバー側の最終文言」が優先表示され、ポリシー側の辞書を経由しません。期限切れはまさにこのタイプに該当します。
よくある誤解の解消
| 誤解 | 正しい理解 |
|---|---|
「UserMessageIfWrongCodeEntered があるなら、期限切れの StringId もどこかにあるはず」 | 期限切れはMFAサービスの固定メッセージで、Localization で差し替える仕組みが提供されていません。 |
| 「UIテンプレートに同じ文言を書けば上書きできる」 | サーバーエラーとして返るものはUIのラベル置換対象外。通知領域に“もう一文”を出しても英語の原文は併存し得ます。 |
「ポリシー中の DisplayControl を調整すれば消せる」 | 期限切れメッセージは検証結果に紐づくため、クライアント側だけでは完全には抑止できません。 |
翻訳可否の切り分け表
| エラー/状態 | 例 | 翻訳可否 | 備考 |
|---|---|---|---|
| 誤ったコード入力 | 入力値が一致しない | 可 | UserMessageIfWrongCodeEntered など既存 StringId で対応 |
| 試行回数上限 | 最大回数超過 | 可 | UserMessageIfMaxRetryAttempted などで対応 |
| 電話番号形式エラー | E.164 不整合 等 | 可 | 入力検証のローカル・エラーとして翻訳可 |
| 検証セッション期限切れ | OTP の有効期限超過/検証セッション失効 | 不可 | サーバー側固定メッセージ。StringId 非提供 |
Localization の最小実装テンプレート(翻訳可能分を確実にカバー)
翻訳できる部分は取りこぼしなく辞書化しましょう。以下は api.phonefactor 配下を想定した例です(実際の ContentDefinition や Id は環境に合わせて調整)。
<Localization Enabled="true">
<SupportedLanguages DefaultLanguage="ja" MergeBehavior="Prepend">
<SupportedLanguage>ja</SupportedLanguage>
<SupportedLanguage>en</SupportedLanguage>
</SupportedLanguages>
電話番号の確認
SMSで送信した確認コードを入力してください。
<!-- 入力ラベル -->
<LocalizedString ElementType="UxElement" StringId="PhoneNumberLabel">電話番号</LocalizedString>
<LocalizedString ElementType="UxElement" StringId="VerificationCodeLabel">確認コード</LocalizedString>
<!-- エラー(翻訳可能) -->
<LocalizedString ElementType="ErrorMessage" StringId="UserMessageIfWrongCodeEntered">確認コードが正しくありません。もう一度お試しください。</LocalizedString>
<LocalizedString ElementType="ErrorMessage" StringId="UserMessageIfMaxRetryAttempted">試行回数が上限に達しました。しばらくしてからやり直してください。</LocalizedString>
<LocalizedString ElementType="ErrorMessage" StringId="UserMessageIfInvalidPhoneNumber">電話番号の形式が正しくありません。国番号を含めて入力してください。</LocalizedString>
</LocalizedStrings>
注意: 「期限切れ」専用の StringId は存在しないため、ここには書けません。
実務的な回避策とベストプラクティス
完全な翻訳はできなくても、ユーザー体験は改善できます。次の対応を組み合わせて“英語の一文”が致命傷にならないよう設計しましょう。
UIに「コードを再送信」導線を明示
期限切れ時にユーザーが迷わないよう、最初から「コードを再送信」ボタン/リンクを目立つ場所に配置します。ボタン文言はローカライズ可能です。
- ボタンは
api.phonefactorテンプレート内のフッタやヘルプ領域に固定配置。 - クリックで同一ステップを再実行(OTP再発行)するか、OTP送信専用のステップへ遷移。
- 頻発クリック対策に短いクールダウン(例:30秒)と残り時間表示を実装。
期限切れに見舞われたユーザーへの“やさしい”ヘルパーテキスト
英語のエラーバナーが出る前提で、その直上または直下に常時表示の説明文を置きます。例:
「コードの有効期限が切れた可能性があります。『コードを再送信』を押して最新のコードを受け取り、再入力してください。」
このヘルパー自体は辞書化できるため、多言語で提示できます。
JavaScript での軽微なUX補助(公式サポート外の可能性に留意)
以下はサポート対象外になり得る取り扱いですが、現場では一定の効果があります。
- カウントダウン表示(例:
setIntervalで60秒タイマー)。時間切れと同時に「再送信」ボタンを自動フォーカス。 - エラー領域に英語メッセージが出た場合をDOM監視し、説明テキストの強調表示やスクロール誘導を行う。
あくまで“補助”として用い、B2C側のDOM構造変更に備えて機能フラグで切り替えられる実装にしておくのが安全です。
フィードバック投稿とロードマップ観測
将来的な改善を促すため、公式のフィードバック経路に「期限切れメッセージのローカライズ対応」を要望として投稿しましょう。プロダクト改善は票と具体的なユースケースが鍵です。
回避策の比較表
| 方法 | 可否 | 実装コスト | 効果 | リスク/注意点 |
|---|---|---|---|---|
別の StringId を当てる | ✕ | — | — | 期限切れ用のIDが存在しないため不可 |
| 「コード再送信」導線の常設 | ○ | 低 | 高 | 連打対策としてクールダウン必須 |
| JSで期限切れ時に独自ページへ誘導 | △ | 中 | 中 | 将来のDOM変更に弱い。サポート外の恐れ |
| フィードバック投稿 | ○ | 低 | 中(中長期) | 即効性はないが標準化の近道 |
実装パターン:オーケストレーションの設計例
「期限切れ」をUIで吸収しやすくするため、OTP送信と検証を分割するのが有効です。例えば次のように構成します。
- Step A: 電話番号入力&OTP送信
電話番号を入力 → OTP送信 → 成功でStep Bへ。ここに「再送」ボタンも常設。 - Step B: コード検証
コード入力用の Self-Asserted ページ。ヘルパーテキストを常時表示し、期限切れ時は Step A へ戻せる導線を強調。
この“分割”により、検証画面で英語の期限切れメッセージが出ても、「戻る/再送」の日本語導線が常に視界に入ります。
例:Technical Profile の組合せイメージ
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="PhoneNumberInputAndSend" TechnicalProfileReferenceId="PhoneFactor-InputOrSendCode" />
</ClaimsExchanges>
</OrchestrationStep>
上記は概念図です。実際の TechnicalProfileReferenceId 名や Metadata は環境のサンプルに合わせて調整してください。
UIテンプレート:再送ボタンの最短例
以下の断片は、api.phonefactor ページテンプレートのヘルパー領域に配置する最小例です。クリックで「同一の検証ステップを再実行」させる発火ポイント(フォーム再送)を設けます。
<div class="help-block" aria-live="polite">
<p>コードの有効期限が切れた場合は、<strong>コードを再送信</strong>を押してください。</p>
<button type="button" id="resend-otp" class="button-secondary">コードを再送信</button>
</div>
環境により送信トリガーやDOM構造は異なります。変更に備えて機能フラグで無効化できるようにしておくと運用が安定します。
セキュリティとレート制御
- レートリミットの見える化:連続再送の抑止はUX(クールダウン表示)とバックエンド両面で行う。
- 再送上限の明示:ヘルパーテキストで「一定回数を超えるとしばらく再送できない」旨を案内。
- 監査ログとアラート:OTP失敗・再送をアプリケーション監視に送出し、異常スパイク時に通知。
品質保証(QA)観点のチェックリスト
- 英語の期限切れメッセージが表示された瞬間、視界内に該当言語の再送導線と説明文があるか。
- 画面読み上げ(スクリーンリーダー)で再送導線に到達でき、説明文が意味を成しているか。
- コード再送のクールダウン中、ボタン状態と残り秒数が正しく反映されるか。
- モバイル端末(低速回線/スリープ復帰)でのタイムアウト再現テストを行ったか。
- 翻訳可能な
StringIdは全言語で充足しているか(抜け・機械翻訳の直し)。
よくある質問(FAQ)
Q. 期限切れの英語メッセージ自体を隠すことはできますか? A. クライアント側のDOM操作で一時的に非表示にすることはできますが、正式サポート外の挙動に触れる可能性があります。推奨は隠すのではなく、再送・再試行の明示と補助テキストで“迷わせない”方針です。
<dt>Q. メッセージ本文の置換をサーバー側で行う方法は?</dt>
<dd>A. カスタムポリシーからMFAサービスの固定文言を差し替えることはできません。ポリシーのフロー設計とUI側の補助で吸収します。</dd>
<dt>Q. メールOTPでは同じ問題が起きますか?</dt>
<dd>A. 実装によっては似た“英語固定メッセージ”が残る場合があります。<strong>翻訳辞書が効く層か、バックエンド固定か</strong>の切り分けが判断ポイントです。</dd>
<dt>Q. どの <code>ContentDefinition</code> を調整すべきか迷っています。</dt>
<dd>A. まずは対象フローで呼ばれる <code>api.phonefactor</code>(または相当)と、Sign-up/Sign-in の Self-Asserted ページを特定し、そのテンプレートに「再送」「ヘルパーテキスト」「カウントダウン」を実装するアプローチが分かりやすいです。</dd>
開発・運用Tips(現場で効く工夫)
- ヘルパーを“常時”表示:エラーが出たら見せるのではなく、常に「再送」手順を示すことで混乱を防止。
- 短い文章で“次の一手”を指示:「何が起きたか」の説明より「どうすればいいか」を先に提示。
- SMS遅延の定型文:「数分待っても届かない場合」ガイダンスを用意(電波状況、迷惑SMS設定、機内モード等)。
- 多言語テストの自動化:
ui_localesパラメーター(または相当)を切り替えるE2Eテストで辞書の欠落を検出。
実装の落とし穴と回避策
| 落とし穴 | 症状 | 回避策 |
|---|---|---|
| OTP送信と検証の同居 | 同一画面で両方を捌くと、期限切れ時の導線が分かりにくい | ステップを分割し、戻る/再送を明示する |
| 再送ボタンの乱打 | スパム疑義・キャリア側遮断・コスト増 | クールダウン+回数上限+監査ログで抑制 |
| 英語メッセージの完全隠蔽 | DOM差異で崩壊、デバッグ困難 | “隠す”より“迷わせない”。説明・導線でカバー |
運用モニタリングの観点
- 失敗理由の分解:誤入力/期限切れ/到達遅延をダッシュボードで可視化。
- 閾値アラート:期限切れ比率が一定閾値を超えたら、SMS遅延やUI回帰を疑う。
- サポートFAQの連動:期限切れ時の自己解決手順(再送、再起動、番号確認)をヘルプに一本化。
サンプル文言集(多言語UXの最小セット)
以下は翻訳可能なテキストの一例です。実プロダクトのトーン&マナーに合わせて編集してください。
- 「SMSでお送りした6桁の確認コードを入力してください。」
- 「コードを再送信」
- 「コードの有効期限が切れた可能性があります。再送して最新のコードでやり直してください。」
- 「連続した再送は制限されます。しばらく待ってからお試しください。」
- 「確認コードが正しくありません。もう一度お試しください。」
導入手順のリファレンス(実践向けチェックポイント)
- 対象ページの特定:
api.phonefactorおよび関連 Self-Asserted ページのテンプレートを洗い出す。 - 辞書の充実:翻訳可能な
StringIdを全言語で埋める。 - 再送導線の実装:ボタン配置、クールダウン、スクリーンリーダー対応。
- ガイダンスの常時表示:期限切れ時の対処を簡潔に。
- レートリミットの整合:クライアント表示とバックエンド制限の整合性を取る。
- モニタリング設定:失敗率・期限切れ率の監視と通知。
- 多端末E2Eテスト:低速回線・バックグラウンド遷移を含む。
まとめ:いま取れる最良の戦略
現行仕様では、MFA SMS検証セッションの「期限切れ」エラーメッセージそのものを、カスタムポリシー側で翻訳することはできません。しかし、再送導線の常設、ヘルパーテキストの常時表示、翻訳可能箇所の網羅、そして運用監視を組み合わせれば、英語の一文が残ってもユーザーの迷いは最小化できます。加えて、機能改善の要望投稿は将来的な解決への最短ルートです。ユーザーの“次の一手”を常に目に入る場所へ――それが実務での最適解です。
付録:ポリシー断片(概念)
実名・URL・キー情報を含まない、考え方の雛形です。
<TechnicalProfile Id="PhoneFactor-InputOrSendCode">
<DisplayName>Phone verification (send)</DisplayName>
<Protocol Name="Proprietary" />
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.phonefactor</Item>
</Metadata>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="phoneNumber" />
</OutputClaims>
</TechnicalProfile>
Phone verification (verify)
api.phonefactor
実運用では、ここにレート制御や例外ハンドリングを加えつつ、UIテンプレートで再送導線と多言語ヘルパーを整えます。
最終結論
MFAサービス起因の「検証セッション期限切れ」エラーは現仕様では翻訳できません。だからこそ、“翻訳できなくても迷わせない”フロー設計が重要です。再送導線をUIに常設し、翻訳可能なメッセージは確実に多言語化、運用監視でボトルネックを可視化し、必要に応じてフィードバックを積み上げる――それが日本語圏のユーザーにとって最適な体験を実現する、いま選ぶべき現実解です。

コメント