JWTによる認証の分散化

# Cookie OAuth2 OIDC（JWT）にみる認証の分散化

近年、HTML5が登場し、ブラウザがローカルコンピューターを操る自由度が増した結果、HTMLが複数のクラウドバックエンドAPI呼び出しの中心として役割を変化させてきました。
ここで、認証サービスに関してもクラウドバックエンドの機能のひとつとして、バックエンドAPIとして分離される事が可能となったので、今までのトレンドからこの傾向を解明していきます。
※尚、本稿はSAMLには触れていません。

# １．Cookieによるクライアント・サーバー型認証

旧来のクッキーによる認証は、セッションIDをクッキーに持たせるというもので、アプリケーションサーバーがその機能を包含しています。
但しログインユーザーの権限等の仕様はクッキー自体が持っていない為、アプリケーション側でDBにテーブルを作るなどしてアクセス権限の操作をする必要がありました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/192988/8df60b24-c7f3-6d33-509a-76310cedd628.png)

| 項番 | 内容 |
|------|:--------------|
| ①    | 認証リクエスト  |
| ②    | 認証       |
| ③    | セッションIDの付与と保管      |
| ④    | クッキー付与レスポンスの返却（Set_Cookie）       |
| ⑤    | クッキーの保管とセッションID付きリクエスト    |
| ⑥    | WEBサーバー側でのセッションIDの検証 |
| ⑦    | 処理結果の返却 |

ここで問題となるのは、各々のアプリケーションサーバー側がセッションIDを管理する仕組みを持っているという事です。複数のアプリケーション間でログインしているか、ログインしていないかを共有するには、このセッションIDで判断する必要があり、これを共有するか、または前段で統一する仕組を入れないと複数アプリケーションサーバーでのシングルサインオンができません。
また、クッキーはドメイン名によってその対象を区別しており、ドメイン名をまたいでセッションを共有する事が困難です。

# ２．OAuth2

ここで、認証をサービス提供側に委任するOAuth2という仕様ができます。
これは、クッキーではなくトークンを使用し、使いまわしをし易いようにしたのがポイントで、スコープ　という権限を与えるサービスの範囲を指定できるようにしたのも一つの進歩です。
　ここでは、許可コードグラントによるOAuth2フローを参考に、SNS（Facebookに該当）に写真をアップロードする画像加工サービスを考えてみます。


![image.png](https://qiita-image-store.s3.amazonaws.com/0/192988/5d99eeea-3bd0-5399-017a-b36a8ef4a0f1.png)

| 項番 | 内容 |
|------|:--------------|
| ①    | 画像加工サービスに対するログイン  |
| ②    | ログインのSNSへのリダイレクト       |
| ③    | SNS側の許可サーバーでのログインフォームによる認証      |
| ④    | 許可コード返却と、アクセストークン取得リクエスト       |
| ⑤    | アクセストークンをつかったSNSのAPI実行   |

この例では、画像加工サービスは完全にログイン処理をSNS側に委任します。
つまりSNS側のログインをもって画像加工サービスのログインを操作します。
ここで問題となるのは、Facebook側は認証サービスをつかさどるものの、Facebook側に問い合わせないとログインした人物のプロファイルがわからない部分です。これには、許可されたトークンを使用してFacebook APIを実行します。また、プロファイルの形式はFacebookの持つものに限定されます。

# ３．OIDC（JWT）

ここで、さらに許可された人のプロファイル等までまたいだ情報を共有する仕組みが標準化されました。この共有に使用されるのがJWTです。JWTを使用すると、認証サーバ側に保証されたログインユーザーのプロファイル（IDトークン）が取得できます。
ここでは、簡単にJWTによるOAuth2の発展させたフローを考えて見ます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/192988/6751e5bf-810c-406e-770a-c6c3b1d0a15e.png)

| 項番 | 内容 |
|------|:--------------|
| ①    | JWT発行元に対するログイン  |
| ②    | JWT発行元でのログインフォームによる認証       |
| ③    | JWTの発行      |
| ④    | JWTを利用したAPIの実行       |
| ⑤    | JWTの検証（復号後のハッシュ検証）   |
| ⑥    | 検証後のAPI実行   |

JWTは、ヘッダーにIssuer（JWT発行元）を含んでいます。このIssuerを信頼して、その有効性を署名とハッシュ復号の突合せにより検証します。検証が終了すると、APIの実行を許可します。

ここでさらに、近年IDプロバイダとして有名になりつつあるAuth0とAWSのLambdaを、
Angular.jsを使ったSPAで連携して認証する具体例を考えて見ます。これは、先程の例とほぼ同じです。※特にAngular.jsでなくても構造は同じです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/192988/15dc03b7-f743-ce8e-daac-e483f368acc1.png)

| 項番 | 内容 |
|------|:--------------|
| ①    | SPAリソースの取得  |
| ②    | JWT発行元でのログインフォームによる認証       |
| ③    | JWTの発行      |
| ④    | JWTを利用したAPIの実行       |
| ⑤    | JWTの検証（復号後のハッシュ検証） |
| ⑥    | 検証後のAPI実行   |
| ⑦    | 他API実行   |
ここで、Angular.jsを使ったSPAでは、HTML5のローカルストレージを使用してJWTを保管します。⑤のJWT検証は、API GatewayのCustomAuthenticatorで宛先をLambdaにし、何らかの形（javascriptのライブラリ等）でJWTを検証する事により実装します。
ここでポイントとなるのは、JWTの検証部分はRFCで仕様化されていると言う事と、それに従った検証用のライブラリが世の名に出回っている事です。これを利用する事により、認証の中枢（プロファイルの保管を含む）を別のサービスに譲る事が楽にできます。
このことにより、コーディングの分業がしやすくなります。

# ４．まとめ

結局のところ、IDProviderを使用してサービスのプロトタイプを一刻も早く作成したい場合に、サーバーレスやJWTを使用する事はある程度有効です。
但し、そのサービスが別サービスに依存してしまったり、構造が複雑で全体像が見えづらくなりますので、図等を使用してわかり易く管理するのが重要です。