OpenID Connect 入門: 土岐 孝平 (著) のまとめ

OIDCについて勉強する必要がでたので、[OpenID Connect入門: 概念からセキュリティまで体系的に押さえる Kindle版, 土岐 孝平 (著)](https://www.amazon.co.jp/OpenID-Connect%E5%85%A5%E9%96%80-%E6%A6%82%E5%BF%B5%E3%81%8B%E3%82%89%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%81%BE%E3%81%A7%E4%BD%93%E7%B3%BB%E7%9A%84%E3%81%AB%E6%8A%BC%E3%81%95%E3%81%88%E3%82%8B-%E5%9C%9F%E5%B2%90-%E5%AD%9D%E5%B9%B3-ebook/dp/B08SCCDLJH/ref=sr_1_2?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=OIDC&qid=1633909911&sr=8-2)を読んでまとめてみました。

まだ勉強したてのため、理解が間違っているかもしれませんので、参考程度に。
詳細は上記書籍を見るようにしてください。薄い本ですのですぐ読めます。オススメです。

OAuth2.0
===========

OAuth2.0とは
-----------------

- OAuth2.0は、2012年に公開された**認可のためのHTTPベースのプロトコル**
- [RFC6749](https://datatracker.ietf.org/doc/html/rfc6749)で仕様が示されています。
- アプリケーションに対してユーザが認可する
    - 例えば、名刺管理アプリがFacebookにアクセスし、友達のデータにアクセスしたい場合、ユーザが名刺管理アプリに対して、「Facebookの友達データにアクセスしてOK」と認可する


OAuth2.0の仕組み, アクセストークン
-----------------

上記の例でいうと

- ユーザはFacebookに対して、名刺管理アプリからの友達データへのアクセスを許可するよーと伝える
- Facebookは、名刺管理アプリに対してアクセストークンを発行する
- 名刺管理アプリはアクセストークンを使って、友達データにアクセスさせる

![OAuth2Overview](https://docs.google.com/drawings/d/e/2PACX-1vQRnt5ueWNzF7ew5f9ObkxVSO8v6IUS4BlFDusPW2BfzjHZXP40HLn33lVl2KxwnB9s7K25XWiLJGbB/pub?w=835&h=604)



OAuth2.0の問題点
-----------------------------------------

- 認可だけでなく、認証もさせちゃうと便利だよね。上記だとFacebook
- でもOAuthは、"認証時の情報", "ユーザの情報" を取得するための仕様が定められていないよね！
    - 認証時の情報: 認証の日時, 手段
    - ユーザの情報: ユーザのID, 名前, メールアドレス
- こまでは上記情報を、各自独自実装して提供していたので面倒だよね〜

OIDC (OpenID Connect)
======================

OIDCとは
--------------------

OIDCとは OAuth2.0をユーザ認証としても活用するために拡張したもの

- 2014年、OpenID Foundation が仕様 [OpenID Connect Core 1.0 incorporating errata set 1](https://openid.net/specs/openid-connect-core-1_0.html) を公開
- 主な拡張としては以下を定めた
    - 認証時の情報とユーザ情報を記載したIDトークン
    - ユーザ情報を取得するためのエンドポイント (IDトークンにある程度ユーザ情報が含まれるので不要なときもある)

![](https://docs.google.com/drawings/d/e/2PACX-1vSRs1RKCBgr9vF2anWPloivBzf7ez9JdrVu6irwkBVX5qlKub-l3p3Nx0ZLoGSOEcxA6guSz78rAAc3/pub?w=835&h=604)


[IDトークンのデータ形式](https://qiita.com/daikumatan/private/4de18e8c0ca2d15a8c0c#id%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E5%BD%A2%E5%BC%8F)については、以下で詳細に説明しています。



OIDCの登場人物
-----------------

| 役割                                             | OAuth2.0の呼び方     | OIDCの呼び方    | 本書の呼び方         | 
| ------------------------------------------------ | -------------------- | --------------- | -------------------- | 
| アプリケーションを使うユーザ                     | Resource Owner       | End User        | ユーザ               | 
| ユーザが操作する端末                             | User Agent           | 定義なし        | ユーザ端末           | 
| アクセストークンを発行するサーバ                 | Authorization Server | OpenID Provider | Authorization Server | 
| アクセストークンを発行してもらうアプリケーション | Client               | Relying Party   | Client               | 
| アクセストークンを使ってアクセスされるサーバ     | Resource Server      | 定義なし        | Resource Server      | 

![OIDCの搭乗人物](https://docs.google.com/drawings/d/e/2PACX-1vTfnJnCddCUavkfK47JAqSztj2ZTgWMecbO4CQrhH6D2AQgvlWxTzuGypaKR9SlViaf3VWFJncUmi7g/pub?w=969&h=672)

エンドポイント
-------------------

３つのエンドポイントを覚えましょう。

- 認可エンドポイント
    - エンドポイントの場所: Authorization Server
    - 意味: ユーザの認証,認可を提供するエンドポイント
- トークンエンドポイント
    - エンドポイントの場所: Authorization Server
    - 意味: トークンを取得するためのエンドポイント。ClientがトークンリクエストをするためのURL
- リダイレクションエンドポイント
    - エンドポイントの場所: リソースサーバ (提供者はClientであり例えばブラウザ上のJavacsriptである)
    - 認証認可後、リダイレクトされClientが受け取るためのエンドポイント


フローの種類
--------------------

OICDのフローの種類を以下に紹介します。まずはSPA認可コードフローを勉強すればよさそうなため、簡単な紹介に留めます。
[各種シーケンス図はこちら](https://qiita.com/daikumatan/private/726a6986c620a41f4a19)を参照してください。

- **SPA認可コードフロー**
    - OIDCの認可フローとしてもっとも一般的。まずはこれを深く勉強しよう。
    - Clientの種類: ブラウザ上のJavaScript, Web ServerはJSをダウンロードするだけのサーバ
- **MPA認可コードフロー**
    - Multi Page Applicationは、リクエスト毎にサーバ側でHTMLを生成する昔ながらのWebアプリケーション。必要に応じて習得すればよい
    - Clientの種類: Web Server上のアプリケーション
- **ネイティブアプリの認可コードフロー**
    - iPhoneアプリやAndroidアプリなどのネイティブアプリ
    - Clientの種類: ネイティブアプリ
- **インプリシットフロー**
    - 認可レスポンスのリダイレクションエンドポイントのURLに直接トークンの値を含める
    - CORS(CrossOriginResourceSharing)が整備された後、OAuth2.0のドラフト「OAuth2.0securityBestCurrentPractice」では非推奨
    - Clientの種類: ブラウザ上のJavaScript
- **ハイブリッドフロー**
    - 認可コードフローと同じ。認可レスポンスの中に、IDトークンやアクセストークンを含めることができる。
- **クライアントクレデンシャルフロー**: 
    - ユーザが関わらない、システムどうしのやりとりなどで使用
    - ClientIDとClientSecretを使って、AuthorizationServerからアクセストークンを取得
    - 利用シーンとしては、バッチ処理でユーザが管轄しないコンテンツ(例えば、商品データ・注文データ)
    - Clientの種類: ユーザが直接関わらないバックエンドのシステム
- **リソースオーナーパスワードクレデンシャルフロー**: 
    - 認証情報を一度Clientに渡す必要がありノンセキュア。OAuth2.0の仕様書に今後使うなという記載がある。一旦無視して良い。
    - Client: SNSサイトの公式のネイティブアプリなど。信頼性の高いサービス



SPA認可コードフローの詳細
---------------------------

OIDCの認可フローとしてもっとも一般的なSPA認可コードフローを詳細に見てみます。

- 青色で示したフローと赤色で示したエンドポイントに注目しながら見てください
- またこのシーケンス図はClientとAuthorization Serverの関係が詳しく書かれており、Resource Serverは示されていないことに注意してください(Web ServerはResource Serverでない)

![SPA認可コードフロ-](https://docs.google.com/drawings/d/e/2PACX-1vR83kKeQZ3xBxGyP-8n3FLBGWgngDkw4atNJZbqzXqoHIDQZpyyIdfeLjm6VuCkNgD03qKTJ0-N_p0I/pub?w=963&h=949)

### Authorization Request

認可リクエストは、Authorization Serverに対して、フローを開始するための情報を指定します。具体的には、フローの種類や、Clientが許可してほしいアクセスの内容 `Scope` などです。情報はリクエストパラメータ(URLの後ろに?や&がつくやつ)で送信します。サンプルを以下に示します

```https:Sample
https://server.example.com/authorize?
response_type=code
&response_mode=fragment
&scope=openid profile email
&client_id=myapp
&redirect_uri=https://client.example.org/app.html
&state=af0ifjsldkj
&nonce=n0S6_WzA2Mj
```

- `scope`: scopeの名前を","区切りでつないだもの。"openid"は必須
    - Clientが許可してほしいアクセスの内容であり、仕様で示されている主なスコープは以下とおり。
        - `openid`: openidのリクエストを示す。必須
        - `profile`: ユーザのプロフィールのClaimを取得
            - Claimとは、認証時の情報やユーザの情報を、キーと値で表現したデータのこと
            - ClaimはIDトークンの中に含められたり、ユーザ情報エンドポイント(別の章で説明)のレスポンスに含められたりする。
        - `email` : ユーザのemailのClaimを取得
        - `address` : ユーザの住所のClaimを取得
        - `phone` : ユーザの電話番号のClaimを取得
- `response_type`: 認可コードフローの場合は `code` を指定
    - 認可レスポンスで受け取るデータ。この指定によってフローが変わる。response_typeの値の種類と、使用されるフローの対応を以下の通り
        - 認可コードフローで使用される
            - `code`: Authorization Code
        - インプリシットコードフローで使用される
            - `id_token`: IDトークン
            - `id_token token`: IDトークン, アクセストークン
        - ハイブリッドフローで使用される
            - `code id_token`: 認可コード, IDトークン
            - `code token`: 認可コード, IDトークン
- `response_mode` : `query` もしくは `fragment` を指定
    - 認可レスポンスでのデータの受け取り方を表す。
    - `?`で渡すのがqueryで`#`で渡すのがfragment
        - `https://sample.com/app.html?code=(省略)&state=(省略)`
        - `https://sample.com/app.html#code=(省略)&state=(省略)`
    - SPAの場合はfragmentがベター
- `client_id`: Authorization Serverが管理する Client ID
    - Authorization ServerがClientを登録するときに割り当てられる。
    - Client側はこのIDをAuthorization Request のときに付与できるように開発する必要がある。 
- `redirect_uri`: リダイレクションエンドポイントのURL
    - 認証、認可後のリダイレクトURL
    - 事前にAuthorization Serverへの登録も必要。
    - もしredirect_uriで指定されたURLと事前に登録されたURLが不一致の場合はエラーになる。
- `state` : ランダムな任意の値
    - 認証・認可後のリダイレクト先のURLに含めてもらうランダムな値。
    - Client側で生成
    - リダイレクションエンドポイントへのCSRF(Cross Site Request Forgery)を防ぐ用途で使用
- `nonce` : ランダムな任意の値
    - Client側で生成するランダムな値
    - Authorization ServerがIDトークンを発行するとき、この値も含めてくれる。これによりClientがIDトークンの正当性を確認できる。

### Authorization Response

```https:sample
HTTP/1.1 302 Found 
Location:https://client.example.org/app.html#
   code=SplxlOBeZQQYbYS6WxSbIA
   &state=af0ifjsldkj
```

- `Location`で Redirection Endpoint を指定します。
- Authorization Request時、fragmentを指定している想定のため`#`でパラメターが渡されています。

各種パラメータの説明をします。

- `code`: Authorization Serverが発行したAuthorization Code
- `state`: Authorization Request時に付与したstateの値

### Request to Redirection Endpoint

```https:sample
GET /app.html
HOST:client.example.org
```

このケースではブラウザはfragmentの値が取り除き、Redirection Endpointである Web Server へアクセスします。


### Token Request

```https:sample
POST /token HTTP/1.1
Host:server.example.com
ContentType:application/xwwwformurlencoded

grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&client_id=myapp
&redirect_uri=https://client.example.org/app.html
```

- `grant_type`: トークンを取得する手段
- `code`: Authorization Serverが発行したAuthorization Code
- `client_id`: Authorization Serverに事前に登録済みのclient_id
- `redeirect_uri`: ClientのRedirection Endpoint

### トークンレスポンス

```https:sample
HTTP/1.1 200 OK
ContentType: application/json
CacheControl: nostore
Pragma: no-cache

{
"access_token": "SlAV32hkKG",
"token_type": "Bearer",
"refresh_token": "8xLOxBtZp8",
"expires_in": 3600,
"id_token": "eyJhbGciOiJSUzI.....(省略)"
}
```

- `access_token` : アクセストークン
- `id_token` : IDトークン
- `refresh_token` : リフレッシュトークン
- `token_type` : Resource Severにトークンを送信する手段。通常は`Bearer` を指定
- `expires_in` : 有効期限[秒数]


IDトークンのデータ形式
-------------------

### JWTデータ形式

IDトークンは認証時の情報とユーザ情報がJWTのデータ形式で記述されています。このJWT形式には以下の２つがあります。基本的にはOIDCにおいてJWS形式が使われます

- JWS(Json Web Signature) <= こちらが一般的
- JWE(Json Web Encryption)

このJWSは以下の3つで構成され、これらを`.`でつなげたものが、IDトークンとなります。

- ヘッダ
    - Base64URLで変換する
    - 電子署名した際のアルゴリズムが記載されている
- ペイロード
    - Base64URLで変換する
    - Claim情報が記載
- 電子署名
    - Base64URLで変換されたヘッダ+ペイロードのハッシュ値をAuthorization Serverの秘密鍵で暗号化

![JWTFormat](https://docs.google.com/drawings/d/e/2PACX-1vQq6QK4G8GGbp38dXVIGGK7IixsDLkI2QX_Pqf-yI3l9Px63DD5CLgt3pCThAdnA03VLat4cxW_xIAV/pub?w=893&h=532)


Base64について補足しておきます。

- [Base64](https://ja.wikipedia.org/wiki/Base64)は、データを64種類の印字可能な英数字のみを用いて、それ以外の文字を扱うことの出来ない通信環境にて日本語のように1バイト(255文字)に収まらないマルチバイト文字やバイナリデータを扱うためのエンコード方式です
- Base64URL: URLアプリケーションのための変形Base64。Base64の`+`と`/`をURLに混ぜると問題を引き起こすため、Base64URLでは、62番目の`+`を`-`へ、63番目の`/`を`_`に変更されている。その他パディングがなかったりする。詳細は上記Base64のリンクを参照


### ペイロードに記載されるClaimの種類

標準的なClaimはRFC7519やOIDCの仕様で記載されている。主なClaimを以下に示します。

- `iss`: Issuerの略。トークンの発行元
- `sub`: Subjectの略。認証された主体。通常はユーザを表す
- `azp`: AuthorizedPartyの略。ClientIDが設定される
- `aud`: Audienceの略。トークンの読者を表す。基本的に、IDトークンの場合はClient ID、アクセストークンの場合はResource Serverの識別子が設定される
- `exp`: ExpirationTimeの略。有効日時がUnix時間で表記される
- `iat`: IssuedAtの略。発行日時がUnix時間で表記される
- `jti`: JWTIDの略。トークンを一意に特定するためのID
- `acr`: Authentication Context Class Referenceの略。認証の手段を表す値
- `nonce`: 認可リクエストでClientが指定したnonceの値
- `name`: ユーザの名前
- `profile`: ユーザのプロフィールページのURL