OAuthをホテルに例えてみた

# この記事の目的

- OAuth一連のフローで行われているやりとりを知ること
- それらのやりとりの目的を理解すること
- リクエスト・レスポンスの中身までは今回は解説しない
    - (解説記事作成予定)

# 記事の背景 (読み飛ばして可)

筆者は漠然としかOAuthが理解できていなかった。「qiitaにgithubのIDとPWDそのまま渡すのエグいからその解決策」的な把握だった。
実際これは間違っていないし、OAuthを利用する側なら十分だと思う。

ただ、プログラマがOAuthをアプリに実装するには知識不足だった。

qiita等の技術記事を読んで、OAuthの流れや実装の仕方はわかるが、**それぞれのステップがなぜ必要なのか**わからなかった。

そこでchatGPTにOAuthのフローを日常生活で例えてもらったところ、ホテルの例を出してくれた。完全な回答ではなかったが、例え話をするために非常に有効な種であったため、自分なりにOAuthをホテルに例えてしっかりと理解したいと思う。

# 本題

本記事の構成は以下のように進める
1. ホテルでの認証認可の流れを説明する
1. ホテルの例を交えてOAuthの説明

## 1. ホテルの場合

### 1.1. 状況設定

ホテルでの流れを説明するにあたって、この世界の特殊なところとアクター、モノを整理する。

:earth_asia: この世界の特殊なところ

- 全ての人間の声・仕草・見た目が全く同じで、外見では全く区別できない。
- :bookmark_tabs:  マイナンバーカードを所持していることが唯一の身分証明となる。
    - つまり、他人の :bookmark_tabs: マイナンバーカードを所持すれば完全になりすましができる

---

:man: アクター
|名前|説明|目的|
|-|-|-|
|私|ホテルのある部屋に荷物を預けている。<br/>いくつかの部屋(A, B)に分けて荷物を預けている。（大事なものはBに預けている）<br />執事を雇っている|ホテルの部屋Aにある自分の荷物を執事に取ってきてもらいたい|
|執事|私の言うことは全て従う。|私の「荷物をとってきてほしい」という要望を叶えたい|
|フロントデスク|お客様の承認があれば、お客様のルームキーを渡す。この世界は特殊なので、フロントデスクに来た人物が所持しているマイナンバーカードとお客様情報情報が一致していれば、フロントデスクにきた人はマイナンバーカードの人物としてみなされる||

---
:package: モノ

|名前|説明|
|-|-|
|:package: 荷物|ホテルの部屋Aに預けている。執事にこれをとってきてほしい|
|:moneybag: 大金|ホテルの部屋Bに預けている。執事とはいえ、この大金には触らせたくない|
|:key: ルームキーA|ホテルの部屋Aに入るためのルームキー ↔️ :package: 荷物を取り出すために必要なルームキー|
|:key2: ルームキーB|ホテルの部屋Bに入るためのルームキー ↔️ :moneybag: 大金を取り出すために必要なルームキー|
|:bookmark_tabs:　マイナンバーカード|私が私であることを示す証明書。この世界は特殊で、これさえあれば、このマイナンバーカードを所持しているひとはこのマイナンバーカードに記載されている人物としてみなされる。つまり、**他人のマイナンバーカードを取得するとその人のフリができる**|

---

ここで重要なポイントを再度掲示すると

:::note info
ホテルの部屋Aにある荷物を**執事に**とってきてほしい
他人の :bookmark_tabs: マイナンバーカードを所持すれば完全になりすましができる
:moneybag: 大金は執事の手に渡したくない(ホテルは信用してるので預けてる)
:::

この3点である。状況設定がややこしいが、この3点が重要になる。

### 1.2. ホテルでのやりとり

#### 1.2.1. よくない方法

さて、私は今ホテルのロビーにおり、ホテルの部屋Aに預けてある荷物を手に入れたい。ただこのホテルは不親切で、「本人確認できたら希望するルームキーは渡すので自分でとってこい」というのだ。自分で部屋Aまでとりにいくのも可能だが、面倒なので執事にやらせたい。
この状況で手っ取り早いのは

:::note warn
私の :bookmark_tabs:  マイナンバーカードを執事に渡し、執事がフロントデスクで本人確認をして :key: ルームキーAをもらい、 :key: 執事がルームキーAを用いて部屋Aにある :package: 荷物をとってくる
:::

ことである。
お気きづきだと思うが、この方法には致命的な欠点がある。それは、

:::note alert
もし執事が悪い奴なら、私の :bookmark_tabs: マイナンバーカードを使って本人確認をして :key2: ルームキーBをもらい、 :key2: ルームキーBを用いて部屋Bにある :moneybag: 大金を持ち逃げする(悪用する)可能性があること
:::

ここで執事をいくら信用しているかは関係なく、**上記のことが起こる可能性がある**ことが問題なのだ。なぜなら私は **絶対に** :moneybag: 大金を他人に渡したくない。
この失敗を起こした原因は、**執事に権限を与えすぎた**ことにある。 :bookmark_tabs: マイナンバーカードを執事に渡した時点で執事は私と全く同等の権限を得る。

#### 1.2.2. 改善策

さて、私は次のことを同時に満たしたい。

- 執事に :key: ルームキーAを持たせたい
- 執事に :bookmark_tabs: マイナンバーカードを持たせたくない

今までの制約下で、これを実現するためにはどうすべきか。

答えとしては、フロントデスクの

> お客様の承認があれば、お客様のルームキーを渡す

という性質を利用するのだ。

具体的にはつぎの手順になる。

1. 執事にフロントデスクへ行かせ、フロントデスクの職員を自分のもとへ来させる
1. フロントデスクに :bookmark_tabs: マイナンバーカードを示す
1. フロントデスクは提示された :bookmark_tabs: マイナンバーカードを用いて私の身分を確認する
1. フロントデスクは私が部屋Aと部屋Bにアクセスできる正当なゲストであると判断する
1. 私は :key: ルームキーAと :key2: ルームキーBを取得する権利を持つが、 :key: ルームキーAのみを希望し、執事に渡してもらう。
1. 執事は :key: ルームキーAを用いて :package: 荷物を回収してくる

ここでのポイントは

:::note info
執事に渡してもいい :key: ルームキーAは執事の手元へ
誰にも渡したくない :key2: ルームキーBは誰の手にも渡らせない
:::

ことである。

こう思った人もいるかもしれない

:::note alert
もし執事が悪い奴なら、私の :package: 荷物を持ち逃げする可能性あるんじゃないか
:::

全くその通りである。
ただ、 :moneybag: 大金に比べたら :package: 荷物は最悪持ち逃げされてもそんなに困らないのだ。
このフローの価値は「執事に与える権限を限定できる」ところにある。

## 2. ネット上の場合

ホテルの例を土台に、OAuthの流れを説明する。

### 2.1. 状況説明

OAuthの説明をするにあたって、インターネットの特殊なところとアクター、モノを整理する。

:earth_asia: この世界の特殊なところ

- アクセスするユーザーは誰なのか区別できない
- ID,pwd の認証をして、初めてその人物が誰か認証される
    - つまり、他人のID, pwdを所持していれば完全にその他人のフリができる

---

:man: アクター
|名前|説明|目的|ホテルの例でいう|OAuthでいう|
|-|-|-|-|-|
|私|私はツイッターの鍵アカウントを所有している。まだQiitaのアカウントはもっていない。|twitterに登録した「個人情報」をQiitaに登録してサインアップしたい|私|リソースオーナー|
|Qiita|まだ私のアカウントは登録されていない。|私のアカウント登録のために、twitter含む信頼性のあるアカウントから私の「個人情報」がほしい。|執事|クライアントアプリ|
|twitter|私の承認があれば、私がtwitterに登録した「個人情報」を取得することができるアクセストークンAをQiita含む他のサービスに渡す。||フロントデスク|リソースサーバー<br/>認証認可サーバー|

---
:package: モノ

|名前|説明|ホテルの例でいう|
|-|-|-|
|個人情報|twitterに登録されている私の情報。名前やメールアドレス、アイコンなど。Qiitaにこれを使ってアカウント登録してほしい|:package: 荷物|
|ツイート|鍵アカで恥ずかしいことツイートしてるので絶対Qiitaにアクセスされたくない|:moneybag: 大金|
|アクセストークンA|私のTwitterの個人情報を取得するための鍵。これを知っている人は誰でも私のTwitterに登録されている個人情報を取得できる|:key: ルームキーA|
|アクセストークンB|私のツイートを取得するための鍵。これを知っている人は誰でも私のツイートを取得できる。Qiitaには知られたくない|:key2: ルームキーB|
|Twitterのpwd|私がTwitterに対して私であることを示す証明書のようなもの|:bookmark_tabs: マイナンバーカード|


※  Qiitaのtwitter OAuth認証が実際はどこまでのスコープを要求してるのか把握してません :rolling_eyes: 

---

ここで重要なポイントを再度掲示すると

:::note info
Twitterの個人情報を使ってQiitaのアカウント登録をしたい
Twitterのpwdは誰にも渡してはいけない
私のツイートはQiitaに知られたくない
:::

この3点である。

### 2.2. OAuth
#### 2.2.1. よくない方法

さて、私はTwitterの個人情報を用いてQiitaにサインアップしたい。自分で手入力するのは可能だが、面倒なのでQiitaに私のTwitterへアクセスしてもらって情報をもってきてほしい。
この状況で手っ取り早いのは

:::note warn
私のツイッターのアカウントのpwdをQiitaへ打ち込み、Qiitaが私のTwitterへログインし、Twitterでの私の個人情報を取得する
:::

ことである。
お気きづきだと思うが、この方法には致命的な欠点がある。それは、

:::note alert
もしQiitaが悪い奴なら、私のTwitterのpwdを使って本人確認をして、私のツイートを自由に閲覧、さらには勝手に投稿されるかもしれない。もしTwitterが２段階認証を設けていなければ、最悪pwdを変更されてTwitterアカウントが完全に手元から離れる可能性がある。
:::

ここでQiitaをいくら信用しているかは関係なく、**上記のことが起こる可能性がある**ことが問題なのだ。
この失敗を起こした原因は、**Qiitaに権限を与えすぎた**ことにある。Qiitaに私のTwitterのpwdを渡した時点で、Qiitaは私と全く等しいTwitterでの権限を得ることになる。

#### 2.2.2. 改善策

さて、私は次のことを同時に満たしたい。

- Qiitaに私のTwitterでの個人情報を渡したい
    - 私のTwitterでの個人情報を得るための権限を渡したい
- Qiitaに私のTwitterのpwdを渡したくない。

今までの制約下で、これを実現するためにはどうすべきか。

答えとしては、Twitterの

> 私の承認があれば、私がtwitterに登録した「個人情報」を取得することができるアクセストークンAをQiita含む他のサービスに渡す。

という性質を利用するのだ。

具体的にはつぎの手順になる。

|step|OAuth|ホテル|
|-|-|-|
|1|QiitaがTwitterに対して、Twitterに登録された個人情報を得るためのアクセストークンをｸﾚｸﾚしにいく|執事にフロントデスクへ行かせ、フロントデスクの職員を自分のもとへ来させる|
|2|Twitterは私にTwitterの認証画面を示しID,pwdを入力させ、Twitterへ送信する|フロントデスクに :bookmark_tabs: マイナンバーカードを示す|a|
|3|Twitterは送られたIDとpwdを用いて、私の身分を確認する|フロントデスクは提示された :bookmark_tabs: マイナンバーカードを用いて私の身分を確認する|
|4|Twitterは送られたIDとpwdが正しいものと判断し、私は、私のTwitter情報にアクセスできる正当なゲストであると判断する|フロントデスクは私が部屋Aと部屋Bにアクセスできる正当なゲストであると判断する|
|5|私はTwitterの個人情報にアクセスできるアクセストークンAと、ツイートまで取得できるアクセストークンBを得る権利を持つが、アクセストークンAのみを希望し、Qiitaに渡してもらう|私は :key: ルームキーAと :key2: ルームキーBを取得する権利を持つが、 :key: ルームキーAのみを希望し、執事に渡してもらう|
|6|QiitaはアクセストークンAを用いて私がTwitterに登録した個人情報を取得する|執事は :key: ルームキーAを用いて :package: 荷物を回収してくる|

ここで、いくつかポイントがある。

:::note info
1 にて、Qiitaは「誰の個人情報か」までTwitterに伝える必要はない。
:::
なぜならQiitaはこの段階で私のTwitterのIDは知らないし、のちのステップで然るべきときに私が伝えるからだ。ホテルの例を交えると、執事の依頼でフロントデスクの職員が私のもとへ来る時に、フロントデスクはまだ私が誰かまでは把握してないのだ。

:::note info
5 を認可と呼ぶ
:::

アクセストークンAを所持しているアプリは、無条件で私のTwitterのアカウント情報を取得することができる。ただし、このアクセストークンではツイートの取得まではできない。ツイートの取得にはアクセストークンBが必要になる。

:::note alert
もしQiitaが悪い奴なら、私のTwitterに登録した個人情報を悪用できる
:::

OAuthを用いると完璧に私のデータが守られる、わけではないのだ。
ただ、私の恥ずかしいツイートに比べれば、Twitterに登録してある私の個人情報は流出してもたかが知れてるのである。
これはホテルの場合でも述べたのと同じで、OAuthの価値は「Qiitaに与える権限を限定できる」ところにある。

# さいごに

この記事を通して、OAuthの理解に少しでも繋がれば幸いである。
次は[こちら](https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f)の記事に記載されているリクエスト・レスポンスのコードの各パラメータの意味や存在理由について解説する記事を書く予定である。


# 参考

https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f

https://qiita.com/sgmryk/items/0f5734a2a6991a6a1873

https://zenn.dev/ayumukob/articles/640cbf4a1ff3ed