Azure AD B2C(カスタムポリシー)でREST API技術プロファイルを使って外部サービスを呼び出すと、APIの処理が少し長引いただけで「10秒の壁」に当たり、ResponseCode 0 や Client connection was unexpectedly closed. が返って自動リトライされることがあります。本記事では、タイムアウト/リトライを変更できるのかという結論と、現場で事故を防ぐための設計・運用の要点を整理します。
起きている現象を整理する:10秒超で接続が切れ、リトライが走る
Azure AD B2C のカスタムポリシーでは、ユーザージャーニーの途中(サインイン、サインアップ、パスワードリセットなど)で、REST API 技術プロファイル(RESTful Provider)を使って外部APIを呼び出せます。ここで外部APIが 7〜15 秒程度かかるケースがあると、一定時間を超えた時点で B2C 側が接続をクローズし、失敗として扱う挙動に遭遇します。
典型的な症状は次のとおりです。
- 外部APIが10秒を超えて応答すると、B2C側のログに
ResponseCode 0が出る - エラーメッセージとして
Client connection was unexpectedly closed.が記録される - その後、B2Cが自動で再試行(リトライ)し、同じAPIが短時間で複数回呼ばれる
このとき重要なのは、「アプリやAPIが1回しか呼ばれない」と思い込むと設計が破綻する点です。B2C の自動リトライは開発者の意図とは無関係に発生しうるため、外部API側は“少なくとも1回、場合によっては複数回”呼ばれる前提で作る必要があります。
Azure AD B2C の REST API 技術プロファイルとは
REST API 技術プロファイルは、カスタムポリシー(Identity Experience Framework / IEF)の中で外部HTTPエンドポイントを呼び出し、入力クレームを渡して、戻り値をクレームとして受け取るための仕組みです。主な用途は次のようなものです。
- ユーザー入力の追加検証(例:社員番号の照合、利用規約同意の確認)
- 外部システムから属性(例:会員ランク、契約状態)を取得してトークンに載せる
- 登録・更新のフック(例:サインアップ完了時にCRMへ連携)
一方で、B2C のユーザーフローは「ログイン体験そのもの」なので、ユーザーを待たせる処理を積み上げるとUXが急激に悪化し、タイムアウトにも到達しやすくなります。REST API で実行する処理は、短時間で完了できるものに絞るのが基本です。
結論:ポリシー側でタイムアウト時間・リトライ回数は変更できない
ドキュメント上は「REST API 呼び出しのタイムアウトはデフォルト10秒」「デフォルトのリトライは1回(合計2回トライ)」と案内されている一方、カスタムポリシー(REST API 技術プロファイル)のXMLだけで、タイムアウト値やリトライ回数を直接変更するための設定項目は公開されていません。つまり、開発者がポリシー設定だけで任意のタイムアウトやリトライ制御を行うことはできません。
| 項目 | 既定の挙動(目安) | ポリシーで変更可否 | 現実的な対応 |
|---|---|---|---|
| タイムアウト | 10秒 | 不可 | API高速化/処理分割/本番のみサポートで30秒へ戻す(暫定) |
| リトライ回数 | 1回(合計2回) | 不可 | APIを冪等にする/重複実行を検知して同一結果を返す |
REST API 技術プロファイルに設定できるメタデータは、たとえば ServiceUrl、SendClaimsIn、AuthenticationType などが中心で、タイムアウトやリトライ回数を明示的に指定するキーは一般公開されていません。ドキュメントの範囲で“できること/できないこと”を最初に切り分けると、無駄な試行錯誤を減らせます。
タイムアウトが短くなった経緯:30秒→10秒へ
過去には REST API 呼び出しのタイムアウトが 30 秒だった時期があり、その後の仕様変更で 10 秒に短縮された経緯が共有されています。これにより、以前はギリギリ通っていた「10秒超のAPI」が、現在はタイムアウト→リトライになりやすくなりました。
ここで誤解しやすいポイントは、「APIが10秒で必ず止まる」わけではなく、「B2Cが10秒で接続をクローズし、B2Cとしては失敗扱いになる」点です。外部APIがサーバー側で処理を継続していた場合、B2Cのリトライと重なって二重処理が起きやすくなります。したがって、単にタイムアウトを延ばすだけではなく、API側の冪等性が不可欠です。
本番環境のみ:サポート経由で30秒へ戻せる暫定措置
どうしても10秒に収まらない事情がある場合、本番テナントに限って Microsoft サポートへ依頼し、タイムアウトを 30 秒へ戻してもらえる暫定措置が案内されています。注意点は次のとおりです。
- 対象は本番環境のみ(開発・検証・ステージング等は変更不可)
- 恒久対応ではなく、一時的な例外措置として扱われる
- 回答時点の目安として、延長措置は2025年12月31日までと案内されている
サポートへ問い合わせる際は、「どのテナントを対象にしたいか」「どのユーザージャーニーで発生しているか」「外部APIの処理時間の実測」「リトライにより二重処理リスクがあること」まで具体的に伝えると、話が早く進みます。
サポート依頼で伝えるとよい情報(チェックリスト)
- 対象テナント(ドメイン名、テナントID、環境区分:本番)
- 対象のポリシーID(例:B2C_1A_xxx)と該当ユーザージャーニー名
- 該当REST API 技術プロファイルのID(例:REST-ValidateSomething)
- 外部APIの平均/最大応答時間(可能ならp95/p99)
- タイムアウト時のログ(
ResponseCode 0、エラー文、Correlation IDなど) - 延長が必要な理由(短期的に10秒未満へ改善できない背景、移行計画)
延長が通ったとしても、開発・検証環境では10秒制限が残るため、結局は「10秒で動く設計」へ寄せていくのが安全です。本番だけ30秒にすると、環境差によってテストで再現しない不具合を抱えやすくなる点にも注意してください。
リトライは止められない:だからAPIは必ず冪等にする
B2C側の自動リトライを無効化したり、回数を細かく制御したりする設定は提供されていません。よって外部APIは、同じリクエストが短時間に2回以上届いても問題が起きないように、冪等(べきとう)を担保します。
冪等性がないと起きる事故例
- ユーザーが1回サインアップしただけなのに、外部システム上は二重登録される
- ポイント付与、クーポン発行、メール送信などが二重に実行される
- 「登録済み」なのにB2C側は失敗扱いになり、ユーザーが先に進めない
冪等性を担保する代表的なパターン
| パターン | 概要 | 向いているケース | 注意点 |
|---|---|---|---|
| 冪等キー(トランザクションID)方式 | リクエストに一意なIDを付け、同じIDは同じ結果を返す | 登録・更新・発行系(副作用がある処理) | IDがリトライ間で変わらないように設計する |
| 自然キー+一意制約 | ユーザーIDなどの自然キーで重複を防ぎ、重複時は成功扱いで返す | 「ユーザーを作る」など重複が明確な処理 | “既に存在”をエラーにせず成功へ寄せる判断が必要 |
| 副作用の分離(同期は検証だけ) | REST APIは検証や参照のみ。副作用は後段のアプリ側で実行 | UXを崩したくない、処理が重い | 結果整合(最終的に一致する設計)を許容する必要 |
API側の実装イメージ(疑似コード)
以下は「冪等キーを受け取り、同じキーなら同じ結果を返す」典型例です。B2Cのリトライがあっても、外部システムに二重反映しないようにできます。
// 入力: requestId(冪等キー), userId, payload ...
if (existsInDb(requestId)) {
// すでに処理済み。前回と同じレスポンスを返す
return loadStoredResponse(requestId);
}
beginTransaction();
try {
// ここで外部連携やDB更新など、副作用のある処理を行う
result = doBusinessLogic(userId, payload);
// 処理結果を requestId と紐付けて保存(リトライ時に再利用)
saveProcessed(requestId, result);
commit();
return result;
} catch (e) {
rollback();
throw;
}
ポイントは「処理結果を保存しておくこと」と「同じ requestId の処理を並行実行しない(または並行実行されても片方だけが確定する)仕組み」です。DBのユニーク制約やロック、分散ロックなど、採用する基盤に合わせて実装してください。
10秒制限を前提にした“現実的な”設計方針
本番だけ30秒に戻せる可能性があっても、非本番で延長できない以上、実務では「10秒以内」を基準に設計するほうが安全です。ここでは、B2C×外部API連携で効果が出やすい改善を、優先度が高い順にまとめます。
外部APIの処理時間を短くする(最優先)
- 重いI/Oを減らす:外部システムへの同期呼び出しを減らし、可能ならキャッシュや事前集計を使う
- DBクエリを見直す:N+1、不要なJOIN、全件走査を避け、必要なインデックスを追加する
- レスポンスを小さくする:B2Cが必要とする最小限の属性だけ返す
- 暗号化や署名の計算コストを最適化:必要な範囲に限定し、重い処理は回避する
“同期でやること”と“後でやること”を分離する
10秒を超えやすい処理は、ユーザーのログイン体験(同期処理)に載せないのが鉄則です。典型的には次の分離が効きます。
- B2C → REST API:即答できる検証・参照だけ(例:会員の存在チェック、状態取得)
- 時間がかかる処理:キューに積んでバックグラウンドで実行(例:CRM登録、複数システムへの連携)
- 結果の取得:後続のアプリ側処理、または次回ログイン時・管理画面で確認
| やりたいこと | B2CのREST APIで同期実行 | 推奨アプローチ |
|---|---|---|
| 入力値の妥当性確認 | 相性が良い | 10秒以内に判定できるようにAPIを軽量化 |
| 属性の取得(少量) | 条件付きで可 | p95を短く。キャッシュ前提で設計 |
| 外部システムへ登録(副作用あり) | 事故が起きやすい | できれば非同期化。実施するなら冪等性を強化 |
| 複数システムへの一括連携 | 非推奨 | キュー+ワーカーで分散処理し、同期は受付のみ |
タイムアウトを“食らう前に”自分で諦める(内部デッドライン)
B2Cの10秒を超えると接続が切れるため、外部API側で「9秒経過したらタイムアウトとして短いエラーを返す」など、内部デッドラインを設けるのが有効です。B2Cに切られる前に自分で失敗レスポンスを返せれば、ログが追いやすく、サーバー側で無駄に処理を続ける状況も減らせます。
サーバーレス/関数の“コールドスタート”に注意する
Azure Functions などのサーバーレス基盤は、負荷が低い時間帯にインスタンスが縮退し、最初の呼び出しだけ遅くなることがあります。B2Cはユーザー起点のトラフィックなので、たまたま最初の1発が遅いだけでタイムアウト→リトライが発生し、二重処理リスクが高まります。必要に応じて常時起動プランの採用、ウォームアップ、依存関係の軽量化を検討してください。
開発・検証環境で詰まりやすい理由と、現場で効く回避策
暫定措置が本番に限定される場合、開発・検証では常に10秒制限のままです。ここでありがちな落とし穴は「本番は30秒にしたから大丈夫」という思い込みです。非本番で再現しない運用は、将来の仕様変更や延長措置の終了時に一気に破綻します。
環境差による事故を避けるための運用ルール
- 非本番で10秒を超えるAPIは“赤信号”:本番で延長できても、改善対象として扱う
- 負荷試験はp95/p99で見る:平均値ではなく“遅い尾”がタイムアウトを生む
- 外部APIにスロットリングがあるなら事前に共有:B2Cのリトライが負荷を増幅する
- 障害時のフェイル動作を決める:APIが落ちたらサインインを止めるのか、限定的に通すのか
監視・トラブルシューティング:切り分けを速くする
REST API 連携の障害は「B2C側ログ」「API側ログ」「ネットワーク」の三者を突き合わせると早く解決します。特に、タイムアウトが疑わしい場合は“実測”が重要です。
| 症状 | 可能性が高い原因 | まずやること | 次の一手 |
|---|---|---|---|
ResponseCode 0 / Client connection was unexpectedly closed. | 外部APIが10秒を超過/ネットワーク遅延/コールドスタート | API側で処理時間を計測し、10秒超のリクエストを抽出 | API高速化、内部デッドライン、非同期化、本番のみ延長申請 |
| 同じユーザー操作でAPIが2回叩かれる | B2Cの自動リトライ | API側ログで同一内容の連続リクエストを確認 | 冪等キー、重複検知、結果保存で二重処理を防止 |
| API側は成功しているのにB2Cは失敗扱い | 応答が遅くB2C側が先に接続断/レスポンス形式不一致 | APIの応答完了までの時間とB2C側の失敗タイミングを比較 | レスポンスの最小化、処理の前倒し、仕様に沿ったJSON返却 |
| ピーク時だけ失敗が増える | 外部APIのスケール不足/依存先が詰まる | CPU、DB、外部依存の待ち時間を可視化 | スケールアウト、キャッシュ、依存先の非同期化 |
ログに残すべき項目(API側)
- 受信時刻、応答時刻、処理時間(ms)
- リクエストの識別子(ユーザーID、操作種別、冪等キー)
- 外部依存呼び出しの時間(DB、別API、メッセージングなど)
- 失敗時の例外種別と、ユーザーに返すエラーコード
「B2Cがリトライしたのか」「ユーザーが戻るボタンで再送したのか」「ネットワークで再送が起きたのか」は、ログの相関が取れないと判断できません。冪等性のためにも、相関ID(Correlation ID)やリクエストIDの設計を先に固めておくと運用が安定します。
30秒延長時の“想定外な並列リトライ”と、その後の修正
一部のユーザーから、タイムアウトを30秒に延長した後でも、次のような想定外の挙動が報告されたことがあります。
- 期待:1回目のAPI呼び出しを最大30秒待ち、それでも応答がなければ1回だけ再試行
- 不具合時:1回目の呼び出し中に、10秒経過時点で2回目の呼び出しが並列で投げられる
この挙動は副作用のあるAPIにとって致命的で、同時実行による競合や二重更新を誘発します。後続のコメントでは、Azure Product Team がこの挙動を修正したと報告されており、現在は改善されていると読めます。ただし、環境やタイミングで例外は起こり得るため、延長を使う場合でも冪等性を前提に設計し、監視で二重実行の兆候を早期検知できる状態にしておくのが安全です。
よくある質問(FAQ)
REST API 技術プロファイルにタイムアウトを指定するキーはありませんか?
公開されている範囲では、ServiceUrl や SendClaimsIn などのメタデータは案内されていますが、タイムアウトやリトライ回数を任意に指定する設定は提供されていません。値を変えたい場合は、API側の改善か、本番限定のサポート対応(暫定)を検討する流れになります。
リトライされると困るので、APIを1回しか叩かないようにできますか?
開発者側でリトライを無効化する手段は提供されていないため、「叩かれても困らない」設計に寄せるのが現実的です。冪等キーや重複検知で、同じ処理が2回走らないように抑え込んでください。
外部APIがどうしても10秒を超えます。最短でできる対処は?
短期的には、本番テナントに限ってサポートへ相談し、30秒へ戻す暫定措置を検討します。ただし根本解決ではないため、同時に「同期処理の範囲を絞る」「非同期化する」「キャッシュする」などの改善計画を立てるのがおすすめです。
“処理時間は10秒以内”の目標はどのくらい余裕を見るべき?
ネットワーク揺らぎやコールドスタートを考えると、ギリギリ10秒ではなく、p95で数秒以内、p99でも8〜9秒を超えない設計が現実的です。B2Cに切られる前に自分で失敗を返す内部デッドラインも合わせて検討してください。
実装・運用の最終チェックリスト
- 外部APIのp95/p99の応答時間を計測し、10秒超がないか確認した
- タイムアウト時の挙動(B2Cのリトライ、ユーザー体験)を把握した
- APIが冪等になっており、二重登録・二重更新が起きない
- 重い処理は同期から分離し、キューやバックグラウンドへ逃がした
- 相関ID/リクエストIDをログに残し、切り分けできる
- 本番で延長する場合でも、将来の終了を見据えて改善計画がある
まとめ
- Azure AD B2C の REST API 技術プロファイルでは、タイムアウト(10秒)やリトライ回数(1回)をポリシー設定だけで変更する方法は公開されていません。
- 過去に30秒だったものが10秒に短縮された経緯があり、10秒超のAPIはタイムアウト→自動リトライになりやすい状況です。
- 本番環境のみ、サポート経由で30秒に戻せる暫定措置が案内されている一方、開発・検証環境は変更できないため、10秒以内を前提にした設計が安全です。
- リトライは止められないため、外部APIは必ず冪等にし、重い処理は非同期化・分割・キャッシュで“ログイン体験”から切り離すのが実務的な解決策です。

コメント