OAuth2.0の認可コードフローを整理したい

登場するもの

OAuthにおけるロール

Role役割
Resource Ownerリソースの所有者。通常はユーザ。
ClientResource Ownerの代わりにリソースへアクセスするアプリケーション。
Authorization Server認可コードやアクセストークンを発行するサーバー。
Resource Serverアクセストークンを受け取り、保護されたリソースを提供するサーバー。

このほかに、実際の認可コードフローでは、ブラウザ(User-Agent)がClientとAuthorization Server間のリダイレクトを仲介する。
(RFCとしてはブラウザは出てこない)

Client Secret

Client Secretは「どのClientなのか」を認証する仕組み。ClientSecretは共通だが、PKCEのcode_verifierは認可処理ごとに生成する。

PKCE(ピクシー/Proof Key for Code Exchange)

OAuth 2.0の認可コードフローにおいて、Authorization Code(認可コード)を第三者に盗まれても、Access Tokenへの交換を防ぐための仕組み。
PCKEでは、Clientが2つの値を使用する。

code_verifier:Clientが生成する推測困難なランダム文字列
code_challenge :code_verifier から計算した値

Public Clientでは、PKCEの使用が必須とされており、Confidential Clientでも利用を推奨されている。
Confidential Client:Clientの認証情報を安全に保持できるクライアント(サーバサイドWebアプリ)
Public Client:Clientの認証情報を安全に保持できないクライアント(SPA、スマートフォンアプリ、デスクトップアプリ)

簡易認可コードフロー

sequenceDiagram
    actor RO as Resource Owner<br/>ユーザー
    participant B as Browser<br/>※OAuthのRoleではない
    participant C as Client
    participant AS as Authorization Server
    participant RS as Resource Server

    RO->>B: ① 外部サービスとの連携を開始
    B->>C: ② 連携開始

    Note over B,AS: フロントチャネル
    C-->>B: ③ Authorization Serverへ誘導
    B->>AS: ④ 認可リクエスト
    AS-->>B: ⑤ 認証・認可
    B->>AS: ⑥ アクセスを許可
    AS-->>B: ⑦ Authorization Codeを付けてClientへ誘導
    B->>C: ⑧ Authorization Code

    Note over C,AS: バックチャネル
    C->>AS: ⑨ Authorization Codeを<br/>Access Tokenに交換
    AS-->>C: ⑩ Access Token

    C->>RS: ⑪ Access TokenでAPIアクセス
    RS-->>C: ⑫ リソース

フロントチャネルとは、ブラウザを経由する通信です。
バックチャネルとは、ClientとAuthorization Serverが直接行う通信です。

詳細な認可コードフロー

sequenceDiagram
    actor RO as Resource Owner<br/>ユーザー
    participant B as Browser<br/>※OAuthのRoleではない
    participant C as Client
    participant AS as Authorization Server
    participant RS as Resource Server

    %% OAuth開始
    RO->>B: ① 「外部サービスと連携」を選択
    B->>C: ② 連携開始リクエスト

    %% Client側の準備
    C->>C: ③ state生成<br/>code_verifier生成<br/>code_challenge生成

    %% Front Channel
    rect rgb(240, 245, 255)
        Note over B,AS: フロントチャネル(User-Agentを経由)

        C-->>B: ④ Authorization Endpointへ<br/>302 Redirect

        B->>AS: ⑤ Authorization Request<br/>GET /authorize<br/>response_type=code<br/>client_id<br/>redirect_uri<br/>scope<br/>state<br/>code_challenge<br/>code_challenge_method=S256

        AS-->>B: ⑥ 認証・認可画面

        RO->>B: ⑦ 認証情報入力・アクセス許可
        B->>AS: ⑧ 認証・認可に必要な情報を送信

        AS-->>B: ⑨ Clientへ302 Redirect<br/>code + state

        B->>C: ⑩ Redirect URIへアクセス<br/>GET /callback<br/>code + state
    end

    %% Back Channel
    rect rgb(245, 255, 245)
        Note over C,AS: バックチャネル(User-Agentを経由しない)

        C->>AS: ⑪ Token Request<br/>POST /token<br/>grant_type=authorization_code<br/>code<br/>code_verifier

        AS->>AS: ⑫ Authorization Code検証<br/>PKCE検証

        AS-->>C: ⑬ Token Response<br/>Access Token
    end

    %% Resource Access
    rect rgb(255, 250, 240)
        Note over C,RS: Resource ServerへのAPIアクセス

        C->>RS: ⑭ Resource Request<br/>Authorization: Bearer Access Token

        RS-->>C: ⑮ Resource Response<br/>Protected Resource
    end

②Browser → Client:連携開始リクエスト

連携開始リクエストです。ユーザが「外部サービスとの連携」などのボタンをクリックしたら、例えばClientが app.example.com だった場合、以下のような表示が行われます。

<a href="/oauth/start">外部サービスと連携</a>
GET /oauth/start HTTP/1.1
Host: app.example.com
Cookie: session=...

上記のようなリクエストが送られます。

③Client:認可リクエストの準備

②を受け取ったClientは、Authorization Serverへユーザーを誘導する準備をします。

PKCEを使用する今回の例では、[ state / code_verifier / code_challenge ] を生成しています。

state : Clientが認可リクエストと、そのあとに返ってくるレスポンスを関連付けるために使用する値です。認可リクエストとコールバックを関連づける値でもある。

code_verifier : PCKEのためにClientが生成するランダムな値。この時点ではAuthorizationServerには送らず、Client側で保持しておく。11のToken Requestで初めてAuthorizationServerへ送る。

code_challenge : code_verifierから生成する値。BASE64URL(SHA256(code_verifier))という関係性。

④ Client → Browser:Authorization Endpointへ302 Redirect

③で準備した値を使って、BrowserをAuthorization Serverへ誘導します。

HTTP/1.1 302 Found
Location: https://auth.example.com/authorize?
response_type=code&
client_id=client123&
redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback&
scope=profile&
state=xyz123&
code_challenge=xxxxx&
code_challenge_method=S256

⑤ Browser → Authorization Server:Authorization Request

④の Location を受け取ったBrowserが、Authorization Serverへアクセスします。

GET /authorize?
response_type=code&
client_id=client123&
redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback&
scope=profile&
state=xyz123&
code_challenge=xxxxx&
code_challenge_method=S256 HTTP/1.1
Host: auth.example.com
パラメータ意味
response_type=codeAuthorization Codeを要求
client_idClientの識別子
redirect_uri認可後の戻り先
scopeClientが要求する権限
stateリクエストとレスポンスの関連付け等
code_challengePKCEで使用
code_challenge_method=S256SHA-256を使用

ここからはAuthorizationServer側の処理になる。

⑥ Authorization Server → Browser:認証・認可画面

Authorization Serverは、必要に応じてユーザにログインや認可画面を表示します。

例えば、

PhotoEditorがあなたの写真データへのアクセスを要求しています。
「許可」「キャンセル」

という画面です。

⑦ Resource Owner → Browser:認証・アクセス許可

ユーザーがBrowser上で、ID/パスワードを入力し、「許可」をクリックします。
(人間による操作なので、Resource OwnerからAuthorization Serverへ直接HTTP通信しているわけではありません。)

⑧ Browser → Authorization Server:認証・認可情報の送信

Browserがユーザーの操作結果をAuthorization Serverへ送信します。

概念的には、

POST /... HTTP/1.1
Host: auth.example.com

...

のような通信です。このHTTPリクエストはOAuthが標準化したものではありません。ログイン画面やMFA、同意画面の実装はAuthorizationServerによって異なるためです。

⑨ Authorization Server → Browser:Authorization Codeを付けてRedirect

ユーザーによる認可が完了すると、Authorization ServerはAuthorization Codeを発行します。

そしてBrowserに対してClientへ戻るよう指示します。

HTTP/1.1 302 Found
Location: https://client.example.com/callback?
  code=ABC123&
  state=xyz123

あくまでAuthorizationServerがBrowserに必要なデータを渡し、Clientへ行きなさいと指示しているだけです。

⑩ Browser → Client:Redirect URIへアクセス

Browserが⑨のリダイレクトに従います。

GET /callback?code=ABC123&state=xyz123 HTTP/1.1
Host: client.example.com

これによって、Clientが

Authorization Code = ABC123
state = xyz123

を受け取ります。

Clientはここで返されたstateが、自分が⑤で送ったものと対応しているか確認します。

ここまでがフロントチャネルです。

⑪ Client → Authorization Server:Token Request

ここから大きく通信経路が変わります。

Browserを経由せず、ClientがAuthorization ServerのToken Endpointへリクエストします。

POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=ABC123&
redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback&
client_id=client123&
code_verifier=yyyyyyyy

③で生成して保持していたcode_verifier がここで登場します。

⑫ Authorization Server:Authorization Code・PKCE等を検証

Authorization ServerはToken Requestを検証します。

PKCEについては概念的に、⑪で受信したcode_verifierから算出した値と⑤で受け取っていたcode_verifierを比較します。

Authorization Codeだけを持ってきても、正しい code_verifier を提示できなければTokenを取得できません。

Authorization CodeそのものやClient、Redirect URIなど、Authorization Code Grantとして必要な検証も行われます。

⑬ Authorization Server → Client:Token Response

検証に成功すると、Access Tokenが発行されます。

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store

{
"access_token": "TOKEN123",
"token_type": "Bearer",
"expires_in": 3600
}

構成によってはRefresh Tokenなども返されます。

⑭ Client → Resource Server:Access TokenでAPIアクセス

Clientは取得したAccess Tokenを使ってResource Serverへアクセスします。

例えば、以下のかたちです。

GET /api/photos HTTP/1.1
Host: api.example.com
Authorization: Bearer TOKEN123

Resource ServerはAccess Tokenを確認して、要求されたリソースへのアクセスを許可するか判断します。

ここで初めて、OAuthを使ってClientが取得した権限が実際のAPIアクセスに使われます。

⑮ Resource Server → Client:Protected Resourceを返す

Access Tokenが有効で、必要な権限を持っていればResource Serverがリソースを返します。

HTTP/1.1 200 OK
Content-Type: application/json

{
  "photos": [...]
}

これで一連の流れが完了します。