シーケンス図の例:PKCE を使った OAuth 2.0 認可コードフロー

多くのエンジニアが出会うフローでシーケンス図の記法を学びます。ブラウザアプリが OAuth 2.0 と PKCE でサインインし、API を呼び出すまでです。

· 創業者・開発者

公開日:

シーケンス図の例:PKCE を使った OAuth 2.0 認可コードフロー

構成図は、誰が誰と通信するかを示します。しかし、どの順番で通信するのか、誰が誰を待つのかは示せません。そして多くの問題では、その順番こそがすべてです。サインインのフローはその典型で、同じ 5 つのメッセージでも順番が違えばセキュリティ上のバグになります。

このガイドでは、多くのエンジニアがいずれ出会うフローを 1 つ使って、シーケンス図の記法を説明します。シングルページアプリが、PKCE を使った OAuth 2.0 認可コードフローでユーザーをサインインさせ、API を呼び出すまでの流れです。

シーケンス図が示すもの

時間は下に向かって流れます。参加者ごとにライフラインがあり、これは下に破線が伸びた箱です。メッセージは、送信者から受信者への水平な矢印です。上から下に読むとイベントの順番が、横に読むと各ステップに誰が関わっているかがわかります。

UML 2.5.1 仕様に基づく記法は次のとおりです。

要素 描き方 意味
ライフライン 下に破線が伸びた箱 参加するシステム、サービス、コンポーネント
アクター 棒人間 人、または外部の役割
同期メッセージ 実線、塗りつぶした矢じり 呼び出し。送信者は応答を待つ
非同期メッセージ 実線、開いた矢じり 送信者が応答を待たないメッセージ
応答 破線 先行するメッセージへの応答
複合フラグメント 演算子付きの箱:alt、opt、loop、par 条件付き、繰り返し、または並行に実行される部分
ノート 角を折った箱 参加者が行う確認などのコメント

フロー:PKCE を使った認可コード

シングルページアプリはパブリッククライアントです。ブラウザに配信するものはすべて読めてしまうため、クライアントシークレットを秘密にしておけません。RFC 7636 で定義された PKCE は、それでも認可コードを保護します。コードを交換する者が、フローを開始したのと同じインスタンスであることをアプリが証明するからです。OAuth 2.0 のセキュリティに関する現行のベストプラクティスである RFC 9700 は、パブリッククライアントに PKCE を必須とし、その他のクライアントにも推奨し、クライアントはインプリシットグラントを使うべきではないとしています。

手順は次のとおりです。

  1. ユーザーが「サインイン」を選びます。
  2. アプリが code verifier と challenge を作ります。 code_verifier は 43〜128 文字のランダムな文字列です。code_challenge は BASE64URL(SHA256(code_verifier)) で、これが S256 方式です。アプリはランダムな state も保存します。
  3. アプリが challenge を付けて、ブラウザを認可エンドポイントに送ります。
GET /authorize?response_type=code
    &client_id=orders-spa
    &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
    &scope=orders.read
    &state=af0ifjsldkj
    &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
    &code_challenge_method=S256 HTTP/1.1
Host: auth.example.com
  1. 認可サーバーがユーザーをサインインさせ、同意を求めます。
  2. 認可サーバーがリダイレクトで戻します。 宛先は redirect_uri で、有効期間の短い code と同じ state が付きます。
  3. アプリが state を確認します。 保存しておいた値と照合します。
  4. アプリがトークンエンドポイントでコードを交換します。 保存しておいた verifier を送ります。
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&client_id=orders-spa&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
  1. 認可サーバーが verifier をハッシュ化し、その結果をステップ 3 の challenge と比較します。一致すればアクセストークンを返し、発行する設定であればリフレッシュトークンも返します。
  2. アプリが API を呼び出します。 Authorization: Bearer <access token> を付けます。
  3. API がトークンを検証します。 署名、発行者、オーディエンス、有効期限、スコープなどを確認し、応答します。

上の verifier と challenge は RFC 7636 の例の値なので、S256 の計算を自分で確かめられます。

図

4 本のライフラインを持つシーケンス図。ユーザー、ブラウザアプリ、認可サーバー、API です。番号付きの 14 個のメッセージが上から下へ並び、応答は破線で、ノートが state と verifier の確認を示し、opt フラグメントが 401 応答後の更新を囲んでいます。

9 個のコンポーネント · 14 個の接続

Archboard で開く
ダイアグラムの内容
  • User
  • Browser app
  • Authorization server
  • API
  • [access token expired]
  • 1 Sign in
  • 2 GET /authorize + code_challenge, state
  • 3 Sign-in and consent page
  • 4 Credentials, consent
  • 5 Redirect: code, state
  • 6 POST /token + code, code_verifier
  • 7 access_token (+ refresh_token)
  • 8 GET /orders, Bearer access_token
  • 9 200 OK
  • 10 GET /orders, expired token
  • 11 401 invalid_token
  • 12 POST /token + refresh_token
  • 13 New access and refresh tokens
  • 14 Retry with the new token
PKCE を使った認可コードフローを UML シーケンス図で表したもの。ブラウザアプリがコードを受け取り、code verifier と一緒にトークンと交換し、API を呼び出し、期限切れのアクセストークンを opt フラグメントの中で更新します。

図の読み方は次のとおりです。

  • 4 つの参加者が 1 列に並んでいます。アクターとしてのユーザーと、ブラウザアプリ、認可サーバー、API のライフラインです。各メッセージはそれぞれの高さにある水平な矢印で、順番に番号が付いています。
  • メッセージ 2〜7 が認可コードフローです。ノートは、フローを安全にする 2 つの確認を示しています。アプリはリダイレクト後に state を照合し、認可サーバーはハッシュ化した verifier を challenge と比較します。
  • メッセージ 8 と 9 が API 呼び出しです。応答は破線で、矢じりは開いています。
  • opt フラグメントは、アクセストークンの期限が切れた場合を扱います。401、更新、再試行です。

ハッピーパスだけでなく、失敗の経路も描く

多くのシーケンス図は「200 OK」で終わります。しかし面白いバグは分岐に潜んでいるので、少なくとも毎日起きる分岐は描きましょう。それぞれガード条件を持つ、互いに排他的な複数の分岐を示すなら alt を、ガード条件が真のときだけ実行される 1 つの部分なら opt を使います。ここでの更新は opt です。アクセストークンの期限が切れたときにしか起きないからです。

更新の分岐には独自のルールがあります。ブラウザアプリでは、リフレッシュトークンは通常ベアラートークンです。そのため RFC 9700 と、2026 年 8 月に公開されたブラウザベースのアプリ向けベストプラクティスである RFC 10017 は、認可サーバーに対し、使用のたびにリフレッシュトークンをローテーションするかクライアントに紐付けること、そして有効期間を制限することを求めています。メッセージ 13 が新しいリフレッシュトークンを返すのはそのためです。

RFC 10017 は、ブラウザアプリのアーキテクチャをセキュリティの観点で順位付けもしています。トークンをサーバー側に保持する Backend for Frontend が最も強固で、この図のようにブラウザアプリ自身が OAuth クライアントになる構成は、最も単純で、悪意のある JavaScript に最もさらされやすい構成です。ブラウザで完結するパターンを選ぶなら、その理由を書いておく場所として図が適しています。

シーケンス図によくある間違い

  • ライフラインが多すぎる。 6〜7 本を超えるとラベルがぶつかり、読み手は流れを見失います。フローを分割するか、内部サービスを 1 本のライフラインにまとめましょう。
  • 応答にラベルがない。 「レスポンス」では何も伝わりません。ステータスや中身を書きます。たとえば「302(code 付き)」「401 invalid_token」です。
  • 詳細度が混在している。 ある矢印には HTTP パラメータ、次の矢印には「ユーザーが満足」と書くと、どちらの読み手も混乱します。粒度を決めて揃えましょう。
  • ハッピーパスしかない。 タイムアウト、再試行、期限切れの認証情報こそ、図が役に立つ場面です。
  • パラメータをすべて書く。 state、code_challenge、code_verifier のように読み手が確認すべきものだけを示し、残りは API リファレンスに任せます。

使うべきとき、使うべきでないとき

プロトコル、ハンドシェイク、再試行、タイムアウト、競合状態、インシデントのタイムラインなど、順番と待ちが重要なものにはシーケンス図を使います。静的な構造には構成図や C4 図を、多数のコンシューマーへのイベントのファンアウトにはイベント駆動アーキテクチャ図を、データが信頼境界を越える場所を確認するセキュリティレビューには脅威モデリングのためのデータフロー図を使いましょう。

Archboard で作る

  1. Google または GitHub でサインインし、図を作成します。
  2. シーケンスパックから、ユーザー用に アクター を 1 つ、ブラウザアプリ、認可サーバー、API 用に ライフライン ノードを 3 つ追加し、同じ高さで 1 列に並べます。
  3. 各メッセージを 2 本のライフライン間のまっすぐなエッジとして、上から下へ、番号付きのラベルを付けて描きます。応答は破線にします。
  4. メッセージ 10〜14 を囲む 複合フラグメント を追加し、演算子を opt、ラベルを「[access token expired]」にします。
  5. 確認内容として、UML パックの ノート ノードを追加します。state、ハッシュ化した verifier、トークンの検証です。

この例で使うダイアグラムノードは 5 個で、Free プランの範囲に収まります。Archboard 自身の MCP サーバーも、AI アプリのサインインにこのフロー、つまり PKCE(S256)を使った認可コードグラントを使っています。詳しくは AIエージェントで構成図を作る方法を参照してください。

よくある質問

PKCE はモバイルアプリ専用ですか? いいえ。ネイティブアプリ向けに設計されましたが、RFC 9700 はシングルページアプリを含むすべてのパブリッククライアントに PKCE を必須とし、コンフィデンシャルクライアントにも推奨しています。

シングルページアプリはインプリシットフローを使うべきですか? いいえ。RFC 9700 は、クライアントはインプリシットグラントを使うべきではないとしています。PKCE を使った認可コードフローを使いましょう。

OAuth 2.1 は完成した標準ですか? 執筆時点(2026 年 10 月)では完成していません。OAuth 2.0 と、PKCE を含むセキュリティプラクティスをまとめた IETF の Internet-Draft のままです。

alt と opt の違いは何ですか? alt は、それぞれガード条件を持つ、互いに排他的な 2 つ以上の分岐を含みます。opt は、ガード条件が真のときだけ実行される 1 つの部分を含みます。

1 つのシーケンス図にメッセージはいくつまで入れられますか? 閲覧時のサイズで読みやすさを保てる数までで、通常は 15〜20 個です。それより長いフローは、フェーズごとに複数の図に分けましょう。

無料で始める — 1 シート、クレジットカード不要。

関連記事

← ブログに戻る

1分でキャンバスを開く

Google または GitHub でサインイン。1シートは無料で、チームが加わったらアップグレード。