OAuthとOIDC

# 概要
現在弊社業務にて、Auth0というIDaaSを利用した認証/認可基盤の開発をしています。
そこでOIDCの概念を用いて設計するのですが、OIDCとはどういった概念なのかなかなか腑に落ちて理解することができませんでした。
ここでは自分なりにOIDCの理解をまとめて、認証/認可周りの理解を深めようと思っている方々の参考になればと思います。

OIDCを理解するにはまずいくつかの概念を理解する必要があります。

# 認証
システムにアクセスしてきたユーザーの本人確認をすること。
WebサービスでEmail + パスワードを入力してログインを実行することなどが認証にあたります。

# 認可
認証されたユーザーに利用が許可された機能を確認すること。
APIを叩く際にアクセストークンをヘッダーに含めることが多いかと思います。
このアクセストークンを払い出すことが認可にあたります。

# Oauth
認可のためのプロトコルとなります。
最終的にアクセストークンを払い出すまでのフローがあるのですが、予め用語を整理しておきます。

## 旧来のフロー
Oauthのプロトコルが規定される以前はこのようなフローになっていました。
クライアントでWebサーバーのID/PASSを保持することになるため、Webサーバーの認証情報漏洩リスクが高まります。
さらにWebサーバー側でID/PASSの変更が行われてしまうと、クライアントもそれに追従する必要があります。
クライアントが様々なサーバーのAPIに依存すれば依存するほど、上記のようなデメリットが大きくなるようなフローとなっています。

この課題を解決するために制定されたのがOauthとなります。

```mermaid
sequenceDiagram
    ユーザー ->> クライアント: 認証情報登録(WebサーバのID/PASS)
    クライアント -->> ユーザー: 認証情報登録完了(WebサーバのID/PASS)
    ユーザー ->> クライアント: APIの実行
    クライアント ->> Webサーバー: APIの実行(WebサーバのID/PASS)
    Webサーバー -->> クライアント: APIの返却値
    クライアント -->> ユーザー: APIの返却値
```

### Oauthの登場人物

#### ユーザー
サービスを利用するユーザーです。
Oauthのフローでは、アクセストークンを取得してWebサーバーのAPIを利用することを目的とします。

#### クライアント
ユーザーが利用するWebサービスです。
Webサーバーの機能を利用して成り立っているWebサービスにとなります。

#### 認可サーバー
ユーザーの認証を行い、クライアントに認可コードを払い出したり、アクセストークンを払い出したりする
弊社ではAuth0というIdaaSを利用しています。

#### webサーバー
アプリケーションの持つwebサーバーです。
例えばクライアント自身が持っているwebサーバーの場合もあれば、連携したSNSのユーザ詳細APIなど、こちらのサーバーで提供します。

#### 認可コード
ユーザーがクライアントへの権限移譲に同意した結果、クライアントが取得するものです。
クライアントから認可サーバーにアクセストークンを要求する際に使用します。
発行のフローの都合上、流出するリスクが高いため有効期限が短く設定されます。標準仕様では10分以内。

#### アクセストークン
認可情報が入ったトークンです。
下記のような情報が含まれています。

- スコープ
このアクセストークンで許可された操作の一覧。
アクセストークンの取得時に利用したいスコープを指定する。

- 有効期限
アクセストークンの有効期限。

#### リフレッシュトークン
アクセストークンの期限が切れた際に、再発行を要求するために使用します。


Oauthの認証フローは大きく4種類あります。
- 認可コードグラント(認可コードグラント＋PKCE)
- インプリシットグラント
- クライアントクレデンシャルグラント
- リソースオーナーパスワードクレデンシャルグラント

実際に認可コードグラントのOauthフローを書き起こしました。
ユーザーが認証情報を入力する際はID/PASSを使用するケースが多く、実際には認証とセットで行われることが多いです。

```mermaid
sequenceDiagram
    ユーザー ->> クライアント: 認可リクエスト
    クライアント ->> 認可サーバー: 認可リクエスト
    認可サーバー -->> ユーザー: 認証画面
    ユーザー ->> 認可サーバー: 認証情報入力
    認可サーバー -->> クライアント: 認可コード返却
    クライアント ->> 認可サーバー: アクセストークンリクエスト
    認可サーバー -->> クライアント: アクセストークン返却
    クライアント -->> ユーザー: アクセストークン返却
    ユーザー ->> Webサーバー: APIを実行
```

上記のフローとなったため、クライアントでWebサーバーのID/PASSを保持する必要がなくなりました。
また、クライアントとWebサーバ間の通信でID/PASSをやりとりする必要もなくなります。

# OIDC(Open ID Connect)
Oauthの概要を踏まえた上で、OIDCの説明に移ります。
OAuthではユーザー認証も実施することが多いため、ユーザー認証の仕組みと認可を合わせて使うことを目的として拡張したものが、OIDC(Open ID Connect)となります。

OIDCではOAuthで取得できるアクセストークンに加えて、認証のための**IDトークン**を取得する仕様となっています。
IDトークンにはユーザーの情報が含まれているため、アプリ上で情報を取り出すことでユーザーの認証に利用することができます。

OauthとOIDCの比較表を記載しました。

||Oauth|OIDC|
|---|---|---|
|機能|認可のみ  |認可 + 認証  |
|発行するトークン|アクセストークンのみ  |アクセストークン + IDトークン|

OIDCの場合のトークン発行フローは下記のようになります。
```mermaid
sequenceDiagram
    ユーザー ->> クライアント: 認可リクエスト
    クライアント ->> 認可サーバー: 認可リクエスト
    認可サーバー -->> ユーザー: 認証画面
    ユーザー ->> 認可サーバー: 認証情報入力
    認可サーバー -->> クライアント: 認可コード返却
    クライアント ->> 認可サーバー: アクセストークンリクエスト
    認可サーバー -->> クライアント: トークン(アクセストークン/IDトークン)返却
    クライアント -->> ユーザー: アクセストークン返却
    ユーザー ->> Webサーバー: APIを実行
```

お気づきかと思いますが、OAuthのトークン発行フローとほぼ同じです。
違っているのはアクセストークンを返却するか、アクセストークンに加えてIDトークンも発行している点です。
双方の処理フローが似ているのは、前述のようにOIDCがOAuthの拡張仕様だからです。
OAuthはアクセストークンを発行するための処理フローを定めていますが、そのフローを参考にIDトークンを発行できるようにしたのがOIDCとなります。

## IDトークンについて
結局OAuthからOIDCへ拡張したところでどんな旨味があったのか、今時点ではピンと来ないかと思います。
**認可に加えて認証の概念を取り入れた**というところになるのですが、それを理解するためにIDトークンについて深掘りをしていこうと思います。

まず[ドキュメントから抜粋した](https://openid.net/specs/openid-connect-core-1_0.html#id_tokenExample)発行されるIDトークンの例を見てみます。(実際は改行は含みません)
```
eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ.ewogImlz
cyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4
Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAi
bi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEz
MTEyODA5NzAsCiAibmFtZSI6ICJKYW5lIERvZSIsCiAiZ2l2ZW5fbmFtZSI6
ICJKYW5lIiwKICJmYW1pbHlfbmFtZSI6ICJEb2UiLAogImdlbmRlciI6ICJm
ZW1hbGUiLAogImJpcnRoZGF0ZSI6ICIwMDAwLTEwLTMxIiwKICJlbWFpbCI6
ICJqYW5lZG9lQGV4YW1wbGUuY29tIiwKICJwaWN0dXJlIjogImh0dHA6Ly9l
eGFtcGxlLmNvbS9qYW5lZG9lL21lLmpwZyIKfQ.rHQjEmBqn9Jre0OLykYNn
spA10Qql2rvx4FsD00jwlB0Sym4NzpgvPKsDjn_wMkHxcp6CilPcoKrWHcip
R2iAjzLvDNAReF97zoJqq880ZD1bwY82JDauCXELVR9O6_B0w3K-E7yM2mac
AAgNCUwtik6SjoSUZRcf-O5lygIyLENx882p6MtmwaL1hd6qn5RZOQ0TLrOY
u0532g9Exxcm-ChymrB4xLykpDj3lUivJt63eEGGN6DH5K6o33TcxkIjNrCD
4XB1CKKumZvCedgHHF3IAK4dVEDSUoGlH9z4pP_eWYNXvqQOjGs-rDaQzUHl
6cQQWNiDpWOl_lxXjQEvQ
```

このIDトークンはよく見ると2つピリオドで区切られており、3セクションに分かれています。
セクションの内訳は下記の通りです。
```
ヘッダー.ペイロード.署名
```

IDトークンはbase64でエンコードされるため、デコードすることで中身を見ることができます。

#### ヘッダー
```shell
echo "eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ" | base64 --decode

{"kid":"1e9gdk7","alg":"RS256"}
```
#### ペイロード
```shell
echo "ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAsCiAibmFtZSI6ICJKYW5lIERvZSIsCiAiZ2l2ZW5fbmFtZSI6ICJKYW5lIiwKICJmYW1pbHlfbmFtZSI6ICJEb2UiLAogImdlbmRlciI6ICJmZW1hbGUiLAogImJpcnRoZGF0ZSI6ICIwMDAwLTEwLTMxIiwKICJlbWFpbCI6ICJqYW5lZG9lQGV4YW1wbGUuY29tIiwKICJwaWN0dXJlIjogImh0dHA6Ly9leGFtcGxlLmNvbS9qYW5lZG9lL21lLmpwZyIKfQ" | base64 --decode

{
 "iss": "http://server.example.com",
 "sub": "248289761001",
 "aud": "s6BhdRkqt3",
 "nonce": "n-0S6_WzA2Mj",
 "exp": 1311281970,
 "iat": 1311280970,
 "name": "Jane Doe",
 "given_name": "Jane",
 "family_name": "Doe",
 "gender": "female",
 "birthdate": "0000-10-31",
 "email": "janedoe@example.com",
 "picture": "http://example.com/janedoe/me.jpg"
}
```

#### 署名
ヘッダーのalgの値を見るとRS256となっています。つまり、RSAアルゴリズムによる署名ということがわかります。
RSAの署名はバイナリデータのなのでただデコードするだけでなく10進数表記にすると中身が見れます。
ただ10進数表記にしても読めないのでここでは省略します。

## IDトークンのヘッダー
前述の通り、IDトークンのヘッダーでは`kid`と`alg`というキーを持っています。
他にも含めることができるパラメータは[こちら](https://datatracker.ietf.org/doc/html/rfc7515#section-4.1)に記載されています。

ヘッダーではIDトークンの基本情報を表し、複数のトークンで共通の値を持つことになります。

## IDトークンのペイロード
ペイロードに配置されるcliamsは[こちら](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims)にまとまっています。
全て解説することはしませんがここでは主要なものにだけ触れます。

**iss:** IDトークンの発行者 (issuer) を識別するための識別子です。
**sub:** ユーザーの一意識別子を表します。
**aud:** 誰を対象として発行されたのかを示すものです。別の言い方をすると、IDトークンの受け取り手が誰であるべきかを示しています。
**exp:** IDトークンの有効期限を表す

このようにペイロードではユーザー固有の情報多く含んだ内容になっています。

#### IDトークンの署名
ヘッダーにある暗号化方式と鍵情報を用いて、ヘッダーとペイロード部分を結合したもので署名しています。
これによりトークンの改竄などを検証することができます。

# まとめ

IDトークンについて理解を深めてみると、OAuthの比較がやりやすいのではと思います。

**OAuthによるアクセストークン:** 認可までを担当。リソースへのアクセスコントロール＝あるリソースへの権限（readやwriteなど）を持っているかどうかまで
**OIDCによるIDトークン**: OAuthの認可に加えて、認証までを担当。その人が誰かを確認することができる。

認証/認可周りは難しくこれに加えてセキュリティにあった認証/認可フローを考える必要があります。
少しずつでも理解して実務に役立てていければと思っています。


# さいごに

トレタでは一緒に開発する仲間を募集しています。<br>
興味がある方は是非カジュアル面談へお越しください！

https://www.wantedly.com/companies/toreta/projects