🔑

OAuthとは何か?― 既存の認証方式と比較して理解する「OAuthが解いている問題」

に公開

OAuthとは何か?

関連記事

既存の認証方式と比較して理解する「OAuthが解いている問題」

Webアプリケーション開発において「認証」は避けて通れないテーマです。
Basic認証、セッション認証、APIキー認証、SAML……様々な手法が存在しますが、その中で OAuth は「少し特殊で分かりにくい」と感じられがちです。

この記事では、OAuthの仕様詳細に入る前に、

  • OAuthは 他の認証方式と何が違うのか
  • どのようなユースケースで 意味を持つ仕組みなのか
  • 逆に OAuthが不要・不適なケース は何か

を、既存の認証方式との比較を通して整理します。


認証・認可の整理(超重要)

まず大前提として、OAuthを理解するには 認証(Authentication)認可(Authorization) を明確に分ける必要があります。

用語 意味
認証 「あなたは誰か?」を確認する
認可 「あなたに何を許可するか?」を制御する

多くの仕組みは 認証+認可を一体で扱う のに対し、
OAuthは 「認可」を中心に設計されたプロトコル である、という点が最重要ポイントです。

✅ OAuth = 認証の仕組み ではなく 認可の仕組み

(※ OpenID Connect は OAuth の上に乗った「認証」の仕組み)


OAuth以外の代表的な認証方式

OAuthを理解するために、まずは他の方式を整理します。
1〜3の方式には共通した本質があります:

アプリが秘密情報(資格情報)を特定の場所に格納し、それを毎回提示することで認証する

つまり、「正しい秘密情報を持っている=本人である」というモデルです。


1. Basic認証

本質

ユーザーの username:password という秘密情報を、リクエストのたびに Authorization ヘッダに載せてサーバーへ送る。

Authorization: Basic dXNlcjpwYXNzd29yZA==
                       ↑ username:password をBase64エンコード

具体的な流れ:

  1. ユーザーがブラウザでアクセス
  2. ブラウザがID/パスワードのダイアログを表示
  3. 入力値をBase64エンコードしてリクエストに載せる
  4. サーバーがデコードして認証情報を検証

格納場所は「HTTPリクエストヘッダ」そのもの。リクエストのたびに毎回送信されます。

限界

  • 資格情報を 毎回ネットワークに流す(HTTPSでないと丸見え)
  • 権限の粒度がない(あるかないかの2択)
  • 第三者への権限委譲は不可能

👉 「ユーザー本人が毎回直接アクセスする」という前提の方式


2. セッション認証(Cookie + Session)

本質

ログイン時に一度だけID/パスワードを検証し、サーバー側にセッション情報を格納する。以降はクライアントの Cookie に保存されたセッションIDを毎回送ることで認証する。

具体的な流れ:

  1. ユーザーがID/パスワードでログイン
  2. サーバーがDB等に セッションID → ユーザー情報 を保存
  3. セッションIDをCookieとしてブラウザへ返す
  4. 以降のリクエストでブラウザが自動的にCookieを送信
  5. サーバーがセッションIDを照合して認証

秘密情報(セッションID)の格納場所は「ブラウザのCookie」と「サーバーのセッションストア」の2箇所。

限界

  • ブラウザ前提の設計なのでAPI連携・モバイルに不向き
  • セッションストアをスケールさせる必要がある
  • 第三者アプリに限定的な権限だけを渡すことができない

👉 「同一アプリ内で完結」する前提の方式


3. APIキー認証

本質

アプリケーションに固定の秘密情報(APIキー)を発行し、リクエストのヘッダやクエリパラメータに埋め込んで送信する。

具体的な流れ:

  1. 管理画面などでAPIキーを発行(例:sk-abc123...
  2. クライアントがリクエスト時にキーを送信
Authorization: Bearer sk-abc123...
# または
GET /api/data?api_key=sk-abc123...
  1. サーバーがキーをDBと照合してアクセスを許可

秘密情報(APIキー)の格納場所は「クライアントの環境変数や設定ファイル」。サーバーtoサーバーの通信で使われることが多い。

限界

  • APIキーはアプリケーション単位の認可であり、どのユーザーが操作しているかは識別できない
  • キーが漏れると全権限が奪われる(スコープが弱い)
  • ユーザーの同意・委譲という概念がない

👉 「アプリケーション単位の認可」に適した方式


まとめ:1〜3の共通モデル

方式 秘密情報 格納場所
Basic認証 ID/パスワード HTTPリクエストヘッダ(毎回)
セッション認証 セッションID Cookie + サーバーのセッションストア
APIキー認証 APIキー クライアントの環境変数・設定ファイル

共通の限界:「秘密情報を持っている者が本人」というモデルは、第三者への安全な権限委譲ができない。


4. SAML(Security Assertion Markup Language)

概要

SAMLは、エンタープライズ向けのシングルサインオン(SSO)を実現するXMLベースのプロトコルです。
「社内の複数のWebサービスに、IDシステムを一元化してログインする」ユースケースで広く使われています。

XMLベースの構造

SAMLの核心は SAMLアサーション と呼ばれるXML文書です。
IdP(Identity Provider)が「このユーザーは認証済みです」と署名して発行する証明書のようなもので、大まかにこんな構造をしています。

<!-- 誰が・誰に・いつまで有効か、ユーザー属性をXMLで表現 -->
<saml:Assertion ...>
  <saml:Issuer>https://idp.example.com</saml:Issuer>  <!-- 発行者 -->
  <saml:Subject>
    <saml:NameID>tanaka@example.com</saml:NameID>      <!-- ユーザー -->
  </saml:Subject>
  <saml:AttributeStatement>
    <saml:Attribute Name="role">
      <saml:AttributeValue>admin</saml:AttributeValue> <!-- 属性 -->
    </saml:Attribute>
  </saml:AttributeStatement>
  <ds:Signature>...</ds:Signature>                     <!-- デジタル署名 -->
</saml:Assertion>

仕様の詳細は OASIS SAML 2.0 公式ドキュメント を参照してください。

SAMLアサーションの送信方法

SAMLアサーションは、主に ユーザーのブラウザ経由で SPへ届けられます。

  1. IdPがHTMLフォームにBase64エンコードしたアサーションを埋め込む
  2. ブラウザが自動的にそのフォームをSPへPOST送信する(HTTP POST Binding)
IdP → ブラウザ(HTMLフォーム) → SP(POST /acs)

アサーション自体はサーバー間で直接やり取りされるのではなく、ブラウザが仲介する点がSAMLの設計上の特徴であり、モバイルアプリとの相性が悪い理由でもあります。

SAMLのSSO全体フロー

SAMLには3つの登場人物がいます。

役割 英語
ユーザー User / Principal 社員のブラウザ
サービスプロバイダ SP (Service Provider) SalesforceやSlack
IDプロバイダ IdP (Identity Provider) Okta、Azure AD
[ユーザー] → [SP(Salesforce)] にアクセス

[SP] 「うちは認証しない。IdPへ行って」→ SAMLリクエストを生成し、ユーザーをIdPへリダイレクト

[ユーザー] → [IdP(Okta)] でログイン(社内のID/パスワード)

[IdP] 認証成功 → SAMLアサーション(XML)を生成・署名

[IdP] → ユーザーのブラウザ経由でSAMLアサーションをSPへPOST

[SP] アサーションの署名を検証して、ユーザーのログインを許可

SAMLの特徴と限界

強み:

  • デジタル署名によりアサーションの改ざんを検出できる
  • エンタープライズで実績が豊富(Okta、Azure AD、Google Workspaceなど)
  • 詳細なユーザー属性(ロール、部署など)を安全に渡せる

限界:

  • XMLの解析が複雑で実装が重い
  • モバイルアプリ・ネイティブアプリとの相性が悪い(ブラウザリダイレクト前提)
  • REST APIとの親和性が低い(JSONではなくXML)
  • セットアップが大がかり

👉 企業内ID統合・エンタープライズSSOの標準。モダンなWeb APIとは相性が悪い。


OAuthが登場した背景

1〜3の方式(Basic認証・セッション・APIキー認証)に共通する前提は、

ユーザーとサービスが直接やり取りする

という2者間の構図です。SAMLはIdPを介した3者構造を持ちますが、あくまで「ユーザーが自分のアカウントでサービスにログインする」という目的に特化しており、後述する問題には対応していません。

しかし、現実のプロダクトはこうなっています:

あなたはGoogleカレンダーのユーザーです。
myapp.com というスケジュール管理アプリが「Googleカレンダーを読み込みたい」と言っています。

この構図では 3者 が登場します。

登場人物
ユーザー(あなた) Googleアカウントを持っている人
リソースを持つサービス Google(カレンダーのデータを持っている)
第三者アプリ myapp.com(あなたのデータにアクセスしたい)

「第三者アプリ」とは、Googleとは別の会社・別のサービスのことです。
ここが1〜3の方式と根本的に異なる点で、これまでの方式は「あなたとGoogleの2者間」しか想定していませんでした。

問題は、myapp.com があなたのGoogleカレンダーにアクセスするには、あなたのGoogleパスワードが必要になってしまうことです。しかしパスワードをmyapp.comに渡すのは明らかに危険です。

  • Googleカレンダーの予定は読めればいいのに、メールも削除できてしまう
  • myapp.comがパスワードを漏洩させたら、Googleアカウント全体が乗っ取られる
  • パスワードを変えたら、連携しているすべてのアプリの設定をやり直す必要がある

ここで初めて出てくる要求が、

第三者アプリに、限定的な権限だけを安全に委譲したい

これを解決するために設計されたのが OAuth 2.0 です。


OAuth 2.0:認可の委譲プロトコル

OAuthの核心

OAuthが提供する価値を一言で言うと:

「資格情報を共有せずに、権限だけを委譲する」

従来(NG): サードパーティアプリにID/パスワードを渡す → アプリがユーザーになりすまして操作
OAuth(OK): パスワードは一切渡さず、限定的なアクセストークンだけを渡す

OAuthの4つの登場人物

役割 説明
Resource Owner 権限を委譲するユーザー あなた
Client 権限を借りるアプリ サードパーティアプリ
Authorization Server トークンを発行するサーバー Googleの認証サーバー
Resource Server 実際のデータを持つサーバー Google Calendar API

実際のOAuthフロー:WebアプリでのGoogle認証

「Google でログイン」ボタンを押したときに、裏側で何が起きているかを追います。

[あなた] myapp.com で「Googleでログイン」をクリック

    ↓ ① myapp.com がブラウザをGoogleの認可画面へリダイレクト

[ブラウザ] → Googleのログイン・権限承認画面へ
              ※ここはGoogleのページ。myapp.comは関与しない

    ↓ ② あなたがGoogleアカウントでログイン&権限を承認
         ────────────────────────────────────────────
         ✅ Googleのパスワードを入力するのはGoogleのページ上
            myapp.com にはパスワードが一切届かない
         ────────────────────────────────────────────

    ↓ ③ Googleが「認可コード」を発行し、myapp.com へリダイレクト

[ブラウザ] → https://myapp.com/callback?code=4/aBcD...

    ↓ ④ myapp.com のサーバーが認可コードをGoogleへ送り、
         アクセストークンと交換(ブラウザは介在しない)

[myapp.com サーバー] ←→ [Google サーバー]
                           アクセストークンを取得

    ↓ ⑤ アクセストークンでGoogle APIを呼ぶ

[myapp.com サーバー] → Google Calendar API(許可された範囲のみ)

なぜパスワードが届かないのか:
myapp.com がリダイレクトするのは「Googleのページ」です。ユーザーはGoogleのページ上でパスワードを入力し、Googleが直接認証します。myapp.com に渡るのは「アクセストークン」だけ—これは カレンダーの読み取りのみ などスコープで制限された、期限付きの鍵です。

パスワードを渡す必要がないので、myapp.com がパスワードを盗んだり漏洩させたりするリスクがゼロになります。


ブラウザが開くのはなぜか:デスクトップアプリの場合

VS CodeやSlack、FigmaなどのデスクトップアプリでGitHubやGoogleのアカウント連携をすると、認証時にブラウザが開きます。これもOAuthの仕組みです。

なぜブラウザが開くのか?

デスクトップアプリ自体はGitHubのパスワードを扱えません(扱うべきではありません)。
ユーザーをGitHubのログインページに誘導するためにブラウザを起動します。

[デスクトップアプリ] 「Sign in with GitHub」をクリック

    ↓ システムブラウザを起動してGitHubの認可画面を開く

[ブラウザ] ユーザーがGitHubでログイン&承認

    ↓ GitHubが認可コードをカスタムURIスキームでアプリへ返す

[GitHub] → vscode://callback?code=... または myapp://callback?code=...

    ↓ OSがそのURIをデスクトップアプリに渡す

[デスクトップアプリ] 認可コードを受け取り、アクセストークンに交換

vscode://myapp:// のようなカスタムURIスキームを使うことで、ブラウザからデスクトップアプリへコードを受け渡しています。

これがOAuthの「ユーザーの操作を正規の認証サーバーに委ねる」という設計の本質です。

モバイルアプリの場合

モバイルアプリも基本的に同じ仕組みです。

[モバイルアプリ] 「Googleでログイン」をタップ

    ↓ システムブラウザ(またはアプリ内ブラウザ)でGoogleの認可画面を開く

[ブラウザ] ユーザーがGoogleでログイン&承認

    ↓ Googleが認可コードをカスタムURIスキームで返す

[Google] → com.myapp://callback?code=...

    ↓ OSがそのURIをモバイルアプリに渡す

[モバイルアプリ] アクセストークンに交換してAPI利用

モバイルアプリも、デスクトップアプリと同様にカスタムURIスキームでコールバックを受け取ります。またパブリッククライアント(client_secretを安全に保持できないモバイルアプリやSPAなど)では、認可コード横取り攻撃への対策として PKCE(Proof Key for Code Exchange)の使用が標準化されています。


OpenID Connect(OIDC):OAuthの上に乗る「認証」

OAuthだけでできること・できないこと

OAuthとOIDCの違いは、OAuth単体では何ができて、何ができないか を見ると分かりやすいです。

OAuth 2.0 だけの場合:

myapp.com が取得できるもの → アクセストークン(ya29.xxxxx)

これでできること:
  ✅ Google Calendar API を呼んでカレンダーデータを取得する
  ✅ Google Drive API を呼んでファイルを操作する

これができない:
  ❌ このトークンが「誰の」ものかを知る
  ❌ ユーザーをDBに登録・管理する(何を主キーにすればいい?)
  ❌ 「田中太郎としてログインした」と確認する

アクセストークンは「Googleに対してAPIを呼べる鍵」でしかなく、myapp.comにとってユーザーが誰かは分からないのです。

「Googleでログイン」のような 認証(ユーザーが誰か) を実現するために、OAuthの上に OpenID Connect(OIDC) が定義されています。

OAuth 2.0 + OIDC の場合:

myapp.com が取得できるもの → アクセストークン + IDトークン(JWT)

IDトークンをデコードすると:
  {
    "sub": "1234567890",         ← ユーザーの一意ID(DBの主キーに使える)
    "email": "tanaka@gmail.com",
    "name": "田中太郎"
  }

これが追加でできる:
  ✅ ユーザーをDBに登録・管理する
  ✅ 「田中太郎としてログインした」と確認する
  ✅ 次回ログイン時に同じユーザーを識別する(subが同じ)

OIDCが追加するもの

OIDCがOAuthに追加するのは実質1つだけです:リクエストに scope=openid を加えると、トークンエンドポイントのレスポンスに IDトークン(JWT) が加わります。

// OAuthのみ
{ "access_token": "ya29.xxxxx", "expires_in": 3600 }

// OAuth + OIDC(scope=openid を追加するだけ)
{ "access_token": "ya29.xxxxx", "id_token": "eyJhbGci...", "expires_in": 3600 }

IDトークンをデコードすると:

{
  "iss": "https://accounts.google.com", // 発行者(Googleが署名した)
  "sub": "1234567890", // ユーザーの一意ID
  "email": "tanaka@example.com",
  "name": "田中太郎",
  "exp": 1600003600 // 有効期限
}
  • sub(Subject)はGoogleが各アプリケーションに対して一意に割り当てる不変のユーザーIDです。これをDBの主キーとしてユーザー管理できます
  • 署名付きなので、Googleの公開鍵で改ざんを検証できます

OAuthとOIDCの役割分担

OAuth 2.0 OpenID Connect
目的 認可(権限委譲) 認証(本人確認)
発行するもの アクセストークン IDトークン(JWT)
答える問い 「このアプリは何をしていいか」 「このユーザーは誰か」
単体でログインに使えるか

✅ 「Googleでログイン」 = OAuthで権限委譲OIDCでユーザー特定


「Googleでログイン」した後、アプリに何が残るか

OIDCでIDトークンを取得した後、myapp.com のサーバーは次の処理を行います。

[myapp.com サーバー] IDトークンを受け取る

    sub(ユーザーの一意ID)でDBを検索

    ┌─ 初回ログイン → usersテーブルに新規登録(email/nameも保存)
    └─ 2回目以降   → 既存ユーザーレコードを照合

    アプリ独自のセッションIDを発行

    Set-Cookie: session_id=xxxx; HttpOnly; Secure  → ブラウザへ返す

この時点でアプリが保持する情報は次のとおりです。

アプリが保持するもの 保存先 用途
sub(ユーザー外部ID) users テーブル ユーザー特定の主キー
email / name users テーブル プロフィールの初期値
セッションID セッションストア + HTTP-only Cookie 以降のリクエスト認証
アクセストークン サーバーサイドのみ 外部API(Google等)の呼び出し

重要な点が2つあります:

  1. OIDCはセッション確立の入口にすぎない。 IDトークンで誰がログインしたかを確認したら、以降はアプリ独自のセッション管理(セッションID + Cookie)が引き継ぎます。IDトークン自体をセッションCookieとして使い回すのは誤りです(詳細は次の記事)。

  2. アクセストークンはブラウザに渡さない。 アクセストークンはGoogle APIを呼ぶためのものであり、サーバーサイドで保持します。ブラウザ(JavaScript)からは見えない場所に置くのが基本です。

✅ 「Googleでログイン」した後のアプリの挙動は、パスワードログイン後と変わらない。
違いは「セッション確立の入口」がOIDCかパスワード検証かの差だけです。

また、MFAが有効なGoogleアカウントでログインした場合、Googleの認証画面上でMFAが処理されます。myapp.comにはMFAの処理結果(認証済みのIDトークン)が届くだけで、myapp.com自身はMFAを実装する必要がありません。MFAはIdP(Google)の責務です。


OAuthが「意味を持つ」ユースケース

OAuthが必須レベルのケース

  • 外部サービス連携(Google / GitHub / X など)
  • サードパーティアプリ連携
  • モバイルアプリ + API
  • マイクロサービス間での権限委譲

OAuthが過剰なケース

  • 単一Webアプリのみ(外部サービス連携なし)
  • 社内限定ツール
  • ユーザー数が少ない管理画面

👉 「第三者が介在しない」ならOAuthは不要


応用フロー:Device Authorization Grant(デバイス認証)

ブラウザを持たないデバイスのOAuth

これまで見てきたフローはすべて「ブラウザでリダイレクトできる」という前提でした。しかし次のようなケースではブラウザリダイレクトが使えません。

  • CLIツールgh auth logingcloud auth login など)
  • スマートTV・ゲーム機(入力デバイスが限られている)
  • IoTデバイス・プリンター(ブラウザがない)

このようなデバイス向けに定義されているのが Device Authorization Grant(RFC 8628)です。

Device Flowのしくみ

[デバイス(CLIやTV)]
        ↓ ① Authorization ServerにDevice Codeをリクエスト

[Authorization Server] → device_code と user_code を返す

[デバイス] ユーザーに表示する:
  「https://example.com/activate にアクセスして
   コード: WXYZ-1234 を入力してください」

[ユーザー] スマートフォンやPCのブラウザで上記URLを開いてコードを入力
        ↓ ② ユーザーがブラウザで認証・承認

[Authorization Server] ユーザーの承認を記録

[デバイス] ③ Authorization Serverへ定期的にポーリング(数秒おきにトークンを確認)
        ↓ ユーザーが承認したタイミングで
[Authorization Server] → アクセストークンを返す

[デバイス] アクセストークンを使ってAPIを呼び出す

ポイントは「デバイス」と「ブラウザ」を分離している点です。
デバイス自体はコードを表示するだけで、実際の認証はユーザーが別のデバイス(スマートフォンなど)のブラウザで行います。

具体例:gh auth login

GitHubのCLIツール gh でログインすると、このフローが使われています。

$ gh auth login
? What account do you want to log into? GitHub.com
? What is your preferred protocol for Git operations? HTTPS
? Authenticate Git with your GitHub credentials? Yes
? How would you like to authenticate GitHub CLI? Login with a web browser

! First copy your one-time code: ABCD-1234
Press Enter to open github.com in your browser...

ターミナルに表示される ABCD-1234user_code です。
ブラウザでGitHubにアクセスしてこのコードを入力することで、CLIがアクセストークンを取得します。

Webアプリフローとの違い

Webアプリ(Authorization Code Flow) デバイス(Device Flow)
リダイレクト ブラウザがリダイレクトする なし
認証場所 同じデバイスのブラウザ 別のデバイスのブラウザ
トークン取得 コールバックで受け取る ポーリングで取得する
適したクライアント Webアプリ・モバイル CLI・TV・IoT

まとめ

方式 本質 限界
Basic認証 秘密情報をリクエストに毎回添付 第三者委譲不可
セッション認証 秘密情報をCookieとサーバーに格納 ブラウザ前提・API不向き
APIキー認証 秘密情報を環境変数等に格納 ユーザー単位の委譲不可
SAML XMLアサーションで認証情報を連携 実装が重い・モダンAPI不向き
OAuth 2.0 パスワード不要で権限だけを委譲 認証(本人確認)は別途必要
OIDC OAuthの上でユーザー認証を実現 OAuth前提
  • OAuthは 「認証」ではなく「認可の委譲」 の仕組み
  • 「Googleでログイン」は OAuth(権限委譲) + OIDC(本人確認) の組み合わせ
  • ブラウザが開くのは、ユーザーが正規の認証サーバーに直接ログインするため
  • OIDCでIDトークンを取得した後は、アプリ独自のセッションを発行して管理する(IDトークンをセッションに使い回さない)
  • ブラウザを持たないデバイスには Device Flow(user_code + ポーリング) を使う
  • 単純なWebアプリに無理に使うものではない

さらに学びたい人向け:公式ドキュメント

OAuth 2.0

PKCE

ネイティブアプリのOAuth

  • RFC 8252 - OAuth 2.0 for Native Apps
    デスクトップ・モバイルアプリでOAuthを使う際のベストプラクティス。カスタムURIスキームや外部ブラウザの利用方針が定義されています。

デバイス認証

  • RFC 8628 - OAuth 2.0 Device Authorization Grant
    CLIツール・TV・IoTデバイスなどブラウザリダイレクトが使えない環境向けのOAuthフロー。device_code / user_code のやり取りとポーリングによるトークン取得が定義されています。

OpenID Connect

  • OpenID Connect Core 1.0
    OIDCの仕様原文。IDトークンの構造、sub/iss/audなどのクレーム、UserInfoエンドポイントの仕様が定義されています。

  • OpenID Connect Discovery 1.0
    OIDCプロバイダが自身のエンドポイントやサポートする機能を公開するための仕様(/.well-known/openid-configuration)。

SAML 2.0

ヘッドウォータース

Discussion