OAuth認証の基本 - 初心者のための備忘録

OAuth認証の基本をまとめました。
自分の備忘録として整理したものですが、同じように認証周りで悩んでいる方の参考になれば。図解でフロー別の違いをまとめてみました。📝

![マインドマップ (26).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/0b7134e6-c7c9-4f7d-bd99-47a3e9aefab0.png)

## はじめに
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/4afd5a2e-b46d-4819-97bc-cfbf3dc180a1.png)
WebアプリやモバイルアプリでAPI連携をする際、「OAuth認証」という言葉をよく耳にします。この記事では、OAuth認証の基本概念から実装の流れまでを初心者向けにまとめました。何度も調べる手間を省くための備忘録としてもご活用ください。

## OAuthとは？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/341f01c1-677d-4635-833e-c574a5756699.png)

OAuthは「Open Authorization」の略で、ユーザーの認証情報（ID・パスワード）を第三者のアプリケーションに渡すことなく、APIへのアクセス権を付与するための標準プロトコルです。

簡単に言えば：
- あなたがTwitterでログインする際に使うID・パスワードを
- 別のアプリ（例：TweetDeck）に渡さなくても
- そのアプリがあなたのTwitterアカウントでツイートできるようにする仕組み

## なぜOAuthが必要なのか？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/2165040a-60d7-49d3-9830-13168e38f9ed.png)

OAuthがない世界では、以下のような問題があります：

1. **セキュリティリスク**: ユーザーが様々なアプリにID・パスワードを直接入力する必要がある
2. **アクセス制御の粒度**: アプリに全ての権限を与えるか、全く与えないかの二択しかない
3. **アクセス権の管理**: 一度パスワードを渡したら、そのアプリのアクセス権を後から取り消せない

OAuthはこれらの問題を解決するために設計されています。

## OAuth 2.0の基本フロー

現在主流のOAuth 2.0には、いくつかの認可フローがありますが、最も一般的な「認可コードフロー」を説明します。

### 登場人物

- **リソースオーナー**: サービスを利用するユーザー（あなた）
- **クライアント**: OAuthを使ってAPIにアクセスしたいアプリ
- **認可サーバー**: ユーザー認証と認可を行うサーバー
- **リソースサーバー**: 保護されたリソース（データ）を持つサーバー

### 認可コードフローの流れ

```
+--------+                               +---------------+
|        |--(A)- 認可リクエスト -------->|               |
|        |                               | 認可サーバー   |
|        |<-(B)- 認可コード ------------+|               |
|        |                               +---------------+
| クライアント|
|        |                               +---------------+
|        |--(C)- 認可コード+             |               |
|        |     クライアント認証 -------->| トークンエンドポイント|
|        |<-(D)- アクセストークン -------+|               |
+--------+                               +---------------+

                                         +---------------+
                                         |               |
                                         |               |
+--------+                               | リソースサーバー |
|        |--(E)- アクセストークン ------->|               |
| クライアント|                               |               |
|        |<-(F)- 保護されたリソース ------+|               |
+--------+                               +---------------+
```

1. **認可リクエスト**: クライアントはユーザーを認可サーバーにリダイレクト
2. **ユーザー認証**: ユーザーは認可サーバーで認証（ログイン）
3. **認可**: ユーザーはクライアントに与える権限を確認・承認
4. **認可コード発行**: 認可サーバーはクライアントに認可コードを発行
5. **トークン交換**: クライアントは認可コードをアクセストークンと交換
6. **APIアクセス**: クライアントはアクセストークンを使ってAPIにアクセス

## 実装のための基本ステップ

### 1. アプリケーション登録

OAuthを利用するには、まずOAuthプロバイダ（Google, Twitter, GitHubなど）にアプリケーションを登録します。

```
例：GitHubの場合
1. GitHub > Settings > Developer settings > OAuth Apps > New OAuth App
2. 以下の情報を入力：
   - Application name: あなたのアプリ名
   - Homepage URL: アプリのホームページURL
   - Authorization callback URL: コールバックURL
```

登録すると以下が発行されます：
- **クライアントID**: 公開可能な識別子
- **クライアントシークレット**: 秘密鍵（漏洩しないように管理）

### 2. 認可リクエストの作成

ユーザーを認可ページへリダイレクトするためのURLを作成します。

```
https://[認可サーバーのドメイン]/oauth/authorize?
  client_id=[クライアントID]&
  redirect_uri=[コールバックURL]&
  response_type=code&
  scope=[必要な権限の範囲]&
  state=[CSRF対策の状態値]
```

※ `state`パラメータはCSRF攻撃から保護するために重要です。ランダムな文字列を生成し、セッションに保存しておきましょう。

### 3. コールバックの処理

ユーザーが認可を許可すると、指定したリダイレクトURIにコードが付与されます。

```
https://[あなたのアプリのコールバックURL]?code=[認可コード]&state=[送信した状態値]
```

このコールバックで行うべき処理：
1. `state`パラメータが元の値と一致することを確認（CSRF対策）
2. 認可コード（`code`）を取得

### 4. アクセストークンの取得

認可コードを使ってアクセストークンを取得します。これはサーバーサイドで行う必要があります。

```
POST https://[認可サーバーのドメイン]/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=[取得した認可コード]&
redirect_uri=[コールバックURL]&
client_id=[クライアントID]&
client_secret=[クライアントシークレット]
```

成功すると、以下のようなレスポンスが返ってきます：

```json
{
  "access_token": "ACCESS_TOKEN",
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "REFRESH_TOKEN",
  "scope": "read write"
}
```

### 5. APIの呼び出し

取得したアクセストークンを使って、保護されたAPIを呼び出します。

```
GET https://[APIエンドポイント]/api/resource
Authorization: Bearer ACCESS_TOKEN
```

### 6. トークンの更新（オプション）

アクセストークンには有効期限があります。期限切れになった場合、リフレッシュトークンを使って新しいアクセストークンを取得します。

```
POST https://[認可サーバーのドメイン]/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&
refresh_token=[リフレッシュトークン]&
client_id=[クライアントID]&
client_secret=[クライアントシークレット]
```

## 主要なOAuth 2.0の認可フロー

### 1. 認可コードフロー（上述）

```mermaid
sequenceDiagram
    participant User as ユーザー(リソースオーナー)
    participant Client as クライアント(アプリ)
    participant AuthServer as 認可サーバー
    participant ResourceServer as リソースサーバー(API)
    
    Client->>AuthServer: 1. 認可リクエスト
    Note over Client,AuthServer: client_id, redirect_uri, scope, state など
    AuthServer->>User: 2. ログイン・認証画面表示
    User->>AuthServer: 3. 認証情報入力・権限の承認
    AuthServer->>Client: 4. リダイレクト with 認可コード
    Note over AuthServer,Client: redirect_uri?code=xxx&state=xxx
    
    Client->>AuthServer: 5. アクセストークンリクエスト
    Note over Client,AuthServer: code, client_id, client_secret
    AuthServer->>Client: 6. アクセストークン発行
    Note over AuthServer,Client: access_token, refresh_token, expires_in
    
    Client->>ResourceServer: 7. APIリクエスト with アクセストークン
    Note over Client,ResourceServer: Authorization: Bearer {token}
    ResourceServer->>Client: 8. 保護されたリソース返却
```

- Web アプリケーションに最適
- 最も安全なフロー

### 2. インプリシットフロー

```mermaid
sequenceDiagram
    participant User as ユーザー
    participant SPA as SPAクライアント
    participant AuthServer as 認可サーバー
    participant ResourceServer as リソースサーバー
    
    SPA->>AuthServer: 1. 認可リクエスト with response_type=token
    Note over SPA,AuthServer: client_id, redirect_uri, scope, state
    AuthServer->>User: 2. ログイン・認証画面表示
    User->>AuthServer: 3. 認証情報入力・権限の承認
    AuthServer->>SPA: 4. リダイレクト with アクセストークン
    Note over AuthServer,SPA: redirect_uri#access_token=xxx&expires_in=xxx
    
    SPA->>ResourceServer: 5. APIリクエスト with アクセストークン
    Note over SPA,ResourceServer: Authorization: Bearer {token}
    ResourceServer->>SPA: 6. 保護されたリソース返却
```

- SPA（シングルページアプリケーション）向け
- リダイレクトURIにアクセストークンが直接付与される
- クライアントシークレットが不要
- ※セキュリティ上の懸念からPKCE付き認可コードフローが推奨されるようになっている

### 3. クライアントクレデンシャルフロー


```mermaid
sequenceDiagram
    participant Backend as バックエンドサーバー
    participant AuthServer as 認可サーバー
    participant ResourceServer as リソースサーバー
    
    Backend->>AuthServer: 1. アクセストークンリクエスト
    Note over Backend,AuthServer: grant_type=client_credentials<br>client_id, client_secret
    AuthServer->>Backend: 2. アクセストークン発行
    Note over AuthServer,Backend: access_token, expires_in
    
    Backend->>ResourceServer: 3. APIリクエスト with アクセストークン
    Note over Backend,ResourceServer: Authorization: Bearer {token}
    ResourceServer->>Backend: 4. 保護されたリソース返却
```

- マシン間通信（サーバー間）に使用
- ユーザーの関与なしでアクセストークンを取得

### 4. リソースオーナー・パスワード・クレデンシャルフロー
- ユーザーのID/パスワードを直接使用
- 高度な信頼関係がある場合のみ使用（非推奨）

## セキュリティに関する注意点

1. **HTTPS**: すべての通信でHTTPSを使用する
2. **state パラメータ**: CSRF攻撃対策として必ず使用する
3. **PKCE (Proof Key for Code Exchange)**: 公開クライアント向けのセキュリティ拡張
4. **スコープの最小化**: 必要最低限の権限のみを要求する
5. **クライアントシークレットの保護**: 漏洩しないようサーバーサイドでのみ使用する

## 開発に役立つライブラリ

### Node.js
- Passport.js: 認証ミドルウェア
- node-oauth2-server: OAuth 2.0サーバーの実装

### PHP
- League OAuth 2.0 Server
- PHP OAuth 2.0 Server

### Python
- Authlib
- python-oauth2

### Ruby
- Doorkeeper
- OAuth2 gem

## 参考資料
- [OAuth 2.0の仕様書](https://tools.ietf.org/html/rfc6749)
- [OAuth.com](https://oauth.com/)
- [Auth0 Docs](https://auth0.com/docs/)