OAuth 2.0 認証フローを理解してみた


OAuth 2.0は、現代の認証メカニズムの中で最も広く採用されている標準の一つ。特に、異なるユースケースに合わせた複数の認証フローが存在することを学んだ。このポストでは、それぞれのフローの役割や目的、そしてどういった違いがあるかを見ていく。

1. 認可コードグラント

説明

Authorization Code Grant は、最も一般的なOAuth 2.0のフローで、クライアントとサーバー間の安全な通信のために設計されている。主にウェブやネイティブアプリで、認証コードをアクセストークンに交換するために使われる。

  1. ユーザー認証: ユーザーが認証サーバーで認証を行う。
  2. 認可コード: 認証が完了した後、リダイレクトURIを通してクライアントアプリが認可コードを受け取る。
  3. トークン交換: クライアントがPOSTリクエストを認証サーバーに送り、認可コードをアクセストークンに交換する。

このフローは、アクセストークンがブラウザに公開されることがなく、認可コードはサーバー側で安全に処理されるため、セキュリティが保たれる。

Flow chart
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/json
 
{
  "grant_type": "authorization_code",
  "code": "<authorization_code>",
  "client_id": "<client-id>",
  "client_secret": "<client-secret>",
  "redirect_uri": "https://example.com/callback"
}

2. PKCE付き認可コードグラント

Authorization Code Grant with PKCEは認可コードグラント(Authorization Code Grant)とほぼ同じ流れとなっている。唯一の差は、公開クライアント(モバイルアプリやシングルページアプリなど)のセキュリティリスクに対処するためにOAuth 2.0で 導入されたPKCE (Proof Key for Code Exchange) がフローのステップになっているということ。これにより、リクエストを行ったクライアントだけが認可コードをアクセストークンに交換できるようにする追加のセキュリティが提供される。

  1. コードベリファイア & コードチャレンジ: クライアントがランダムな文字列(コードベリファイア)を生成し、それをハッシュ化してコードチャレンジを作成する。
  2. 認可リクエスト: クライアントが認可リクエスト中にコードチャレンジを送信する。
  3. トークン交換: クライアントが認可コードと共にコードベリファイアを送信し、アクセストークンを取得する。
Flow chart
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/json
 
{
  "grant_type": "authorization_code",
  "code": "<authorization_code>",
  "client_id": "<client-id>",
  "code_verifier": "<code_verifier>",
  "redirect_uri": "https://example.com/callback"
}
// Helper function to generate a code challenge from the verifier
async function generateCodeChallenge(verifier: string) {
  // Encodes the verifier
  const encoder = new TextEncoder()
  const data = encoder.encode(verifier)
 
  // Hashes the encoded verifier
  const hashBuffer = await crypto.subtle.digest("SHA-256", data)
  const hashArray = Array.from(new Uint8Array(hashBuffer))
 
  // Encodes the hash as base64
  const base64String = btoa(String.fromCharCode.apply(null, hashArray))
 
  // Converts the base64 string to a URL safe string
  return base64String.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")
}

3. インプリシットグラント(非推奨)

Implicit Grant は、クライアントサイドのアプリ(例えばシングルページアプリケーション)向けに設計されたもの。しかし、セキュリティの問題があるため、現在は非推奨となっている。このフローでは、アクセストークンがURLフラグメントに直接露出するため、悪意のある第三者にインターセプトされる可能性がある。

  1. クライアントが直接認証サーバーからアクセストークンをリクエスト。
  2. アクセストークンがURLフラグメント内で返され、これがクライアントに表示されるため、潜在的に危険。

アクセストークンがURLに露出してしまうとセキュリティリスクが高まるため、PKCE付き認可コードグラントがクライアントサイドアプリ向けの推奨フローとなっている。

Flow chart

4. クライアントクレデンシャルグラント

Client Credentials Grant は、マシン間通信用のフロー。クライアントアプリケーションが自身のクレデンシャル(クライアントIDとシークレット)を使って認証サーバーに認証を行い、アクセストークンを取得する。主に自分のリソースやサードパーティのAPIにアクセスするために使われる。

  1. クライアント がクライアントIDとシークレットを使って認証サーバーに認証を行う。
  2. 認証サーバー がクライアントにアクセストークンを発行。
  3. クライアント がアクセストークンを使って保護されたリソースにアクセスする。
Flow chart
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/json
 
{
  "grant_type": "client_credentials",
  "client_id": "<client-id>",
  "client_secret": "<client-secret>"
}

5. エクステンショングラント

Extension Grant は、標準のOAuth 2.0フローに適合しない特定のユースケースに対応するためのカスタムグラントタイプ。例えば、デバイス認証グラント (Device Authorization Grant) は、スマートホームデバイスなど入力が限られたデバイスが、スマートフォンなどのセカンダリデバイスを使って認証を行うために利用される。

  1. デバイス がサーバーに認証リクエストを送信。
  2. ユーザー が別のデバイス(例: スマートフォン)で認証を完了。
  3. デバイス はユーザー承認後にアクセストークンを受け取る。
Flow chart

さいごに

これらのOAuth 2.0認証フローを学ぶことで、さまざまなシナリオにおけるセキュリティの懸念や、適切な実装方法を理解することができた。モバイルアプリやシングルページアプリ向けには、PKCE付きの認可コードグラントが推奨されるが、サーバー間通信やカスタムユースケースでは、Client Credentials GrantExtension Grant などのフローが重要になる。

特に、Authorization Code Grant は柔軟性が高く、ユーザーの関与が必要なシチュエーションに最も適している。フロントエンドに機密情報を露出させることなく、認可コードを安全にアクセストークンに交換できる点が、このフローの最大の利点だ。PKCEを追加することで、クライアントシークレットを保護できない公開クライアントでも高いセキュリティレベルを維持できる。

一方、Client Credentials Grant は、ユーザーが関与しないサーバー間通信に不可欠だ。特にバックエンドサービスがAPIやマイクロサービスに認証する必要がある場合に便利で、マシン間での自動化をサポートする。

また、標準的なグラントでは対応できないユースケースには、Extension Grant が柔軟な認証フローを提供する。IoTデバイスやスマートデバイス向けの Device Authorization Grant はその良い例で、入力が制限されたデバイスに最適だ。こうした拡張グラントにより、OAuth 2.0は進化するテクノロジーや非伝統的なクライアント環境にも対応できる。

これらのフローを理解することは、セキュアで拡張可能な認証システムを設計する際に非常に重要だ。それぞれのフローには特定のユースケースがあり、適切なグラントタイプを活用することで、セキュリティリスクを大幅に減らし、ユーザーエクスペリエンスを向上させることができる。急速に進化するデジタル環境において、OAuth 2.0は多様なシステムやデバイス間でアクセスを管理するための堅牢で標準化されたアプローチを提供し、現代のアイデンティティ管理において欠かせないフレームワークとなっている。

最後に、OAuth 2.0は業界のニーズに合わせて進化を続けており、それぞれのフローをいつ、どのように使うべきかを理解することで、開発者やシステムアーキテクトはセキュリティ脅威に先んじて対処することができる。適切な認証方法を実装することは、ユーザーデータを保護するだけでなく、システム全体の健全性を維持するためにも重要だ。


執筆Marko Leinikka

文字数:4291
9分で読めます