SAMLが何をやっているのかできるだけ分かりやすく図説してみた(つもり)

# はじめに
この記事は「なんかSAMLっていう・・・シングルサインオンするための仕組み？があるらしいけど、導入以前に何なのかイマイチよく分からんし、何やってるのか分からん」というような方向けです。
ただ、概念的な理解に関して言えば、この記事以前に多くの先人による記事が既にありますので、そちらを確認していただく方が分かりやすいかもしれません。

ここでは主に、「SAMLを使ったログインがどのように行われているのか」について解説していきます。
私自身OASIS(SAMLを策定した標準化団体)のドキュメントの隅々にまで目を通したわけではないので、誤情報があればぜひご指摘ください。
Done is better than perfect.

# SAMLとは
とはいえ、やはりSAMLとは何かについても軽く触れておきます。
SAMLとは、『認証・認可情報を異なるサーバーやドメイン間でやりとりするために策定された、マークアップ言語』です。
・・・と書くと若干分かりにくいかもしれませんね。

「色んなサービスで別々にアカウントやパスワード管理するのってめんどくさいから、一元管理しちゃおう」という考え自体は昔からありましたが、SAML以前はその方法がバラバラでした。（今でもバラバラな方法でシングルサインオンを実現しているサービスは多々あります）
なので、「やりとりする情報のフォーマットを決めて、サービスはそのフォーマットに従って認証・認可できるようにしよう！」というフォーマットが生まれました。
それがSAMLです。

## SAMLのメリット
フォーマットが決まっているということは、シングルサインオンの実装・導入が簡単になるということです。
例えば、シングルサインオンを実現するために専用のプロキシサーバーを立てて、そこで特定のURLへのリクエストに対してあるCookieを付与して・・・などといった仕組みを構築する必要はありません。
フォーマットに則ってログインできるようにサービスを提供すれば、サービス提供者側がそれらの仕組みに対して個別に対応する必要はなくなりますし、利用者は独自にシングルサインオンを実現するための仕組みを構築しなくて済みます。

また、XML署名を利用するため、公開鍵暗号方式によってセキュリティも担保されます。(XML署名は利用しないようにもできますが、セキュリティ観点からほぼ必須であると言えると思います)

# SAMLを使ったシングルサインオンの流れ
ここからがこの記事の本題です。

では、SAMLを利用したシングルサインオンは、どのように実現されているのでしょうか？
サービスへアクセスし、シングルサインオンでのログインが行われるまでの流れを理解するためには、前提として、2つの言葉と2つの方式を知っておく必要があります。

## 前提知識
- SP(Service Provider)
  サービス・プロバイダーは、シングルサインオンを利用してログインしたいサービスです。
  SAMLによるシングルサインオンを行うためには、ログインしたいサービスがSAMLによる認証に対応している必要があります。

- IdP(Identity Provider)
  アイデンティティ・プロバイダーは、認証を行うサーバー(サービス)です。
  SAMLによるシングルサインオンを行うためには、認証サーバーがユーザー情報を保持している必要があります。
  オンプレミスであればKeycloakやOpenAM、クラウドであればOneLoginなどが挙げられるかと思います。

## SAMLで実現できるシングルサインオンの種類
SAMLは、「どちらからシングルサインオンを開始するか」によって2種類に分けられます。

- SP-initiated SAML
  文字通り、SPからシングルサインオンを開始する方式です。
  ブラウザ上では、「サービスへアクセスすると認証サーバーのログイン画面が出てきて、ログインすると利用できる」というような挙動になります。

- IdP-initiated SAML
  こちらも文字通り、IdPからシングルサインオンを開始する方式です。
  「認証サーバーのログイン画面からポータルサイトにログイン後、そのポータルサイトからサービスに遷移する」ような形は、こちらになります。

IdP-initiatedの場合、サービスへアクセスするためにポータルサイトなどを経由する必要があるため、一般的にはSP-initiatedを利用される場合の方が大多数だと思います。

## SP-initiated SAMLで行われる処理
では、一般的なSP-initiated SAMLではどのように認証が行われるのでしょうか。

- クライアント
- SP(ログインしたいサービス)
- IdP(サービスに設定された認証サーバー)

が存在し、それぞれが適切にシングルサインオンのために設定されているとします。
また、クライアントはサービスにも認証サーバーにもログインしていないとします。

この時、「サービスへアクセスすると認証サーバーのログイン画面が出てきて、ログインすると利用できる」を実現する処理の流れは以下のようになります。

**1. クライアントからサービスへログインしていない状態でサービスにアクセスする**
![saml-Page-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655411/da819ad9-21cd-4245-82e4-81b8ee8eb81b.png)
当然ながら、サービスへHTTPリクエストが送信されます。

**2. SPが設定に従ってSAMLリクエストを作成する**
![saml-Page-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655411/4680ce8c-5b4d-3f8e-cd9f-8f38a8912d37.png)
SPはクライアントが非ログイン状態であることを判断し、SAMLリクエストを作成します。

**3. SPがSAMLリクエストを付与したHTTPレスポンスを返却する**
![saml-Page-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655411/fbd0f87f-f607-4555-1f0d-1bcdbf9e5f7d.png)
SPはSAMLリクエストを付与したリダイレクトのHTTPレスポンスを返却するため、クライアントは自動的にIdPへリダイレクトされます。つまり、IdPへSAMLリクエストが付与されたHTTPリクエストが送信されます。

**4. IdPがセッション状態を確認する**
![saml-Page-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655411/0bbdd54d-0e6f-b8c4-8168-9f491d63e57c.png)
IdPはクライアントがIdP側でログイン済みかどうかを確認します。

**5. IdPがログイン画面を表示させる**
![saml-Page-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655411/5a46ea92-a69d-7d36-28a1-1e8d54fb66ca.png)
IdPにログインしていない状態なので、ログイン画面を表示させるためのHTTPレスポンスを返却します。

**6. クライアントでユーザーがID・パスワードなどを入力し、IdPへ送信する**
![saml-Page-6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655411/0d3f7c8e-9458-0007-7269-aa49563b3405.png)
IdPにログインするための情報が、HTTPリクエストとして送信されます。

**7. IdPが認証情報を確認し、正しければSAMLレスポンスを作成する**
![saml-Page-7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655411/5bcad983-0b65-e103-677f-f918a7a15920.png)
認証が失敗すればIdPがログイン失敗画面を返却します。

**8. IdPがSAMLレスポンスを付与したHTTPレスポンスを返却する**
![saml-Page-8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655411/104b9d8f-f8b5-5884-22e8-98273583b728.png)
IdPはSAMLレスポンスを付与したリダイレクトのHTTPレスポンスを返却するため、クライアントは自動的にSPへリダイレクトされます。つまり、3とは逆にSPへSAMLレスポンスが付与されたHTTPリクエストが送信されます。

**9. SPが受け取ったSAMLレスポンスからユーザー情報を取り出し、ユーザーをログイン状態にする**
![saml-Page-9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655411/054b4dd1-3aca-bcdb-ddc8-c2fb51e29f71.png)
通常のログインと同じく、セッションが作成されます。

**10. SPがログイン後の画面を表示させる**
![saml-Page-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655411/f8e60cb5-b553-794e-2f81-a1706e23e5bf.png)
SPがクライアントへログイン後の画面を表示させるためのHTTPレスポンスを返却します。
これで晴れて、クライアントでログイン画面が表示されました！

ブラウザ画面上での動きとしては、
「1.サービスにアクセスする」と、「5.IdPがログイン画面を表示させる」ことになります。
その画面上で、「6.クライアントでユーザーがID・パスワードなどを入力し、IdPへ送信する」と、「10.SPがログイン後の画面を表示させる」ことになります。
文字で書くと複雑そうに感じるかもしれませんが、実際のブラウザ画面上ではログイン画面がIdPのものにすり替わっただけです。

また、「IdPにはログイン済みだがSPではログインしていない」という状態だと、IdP側で再度ログインする必要はないので、4.～6.が省略されます。
便利ですね。

# おわりに
SAMLを使えば、このようにSPとIdPの間でSAMLリクエスト・SAMLレスポンスという形を使って認証・認可情報をやりとりすることによって、シングルサインオンを実現できます。
より詳細な理解のためには「Security Assertion Markup Language」という言葉が示す通り『アサーション』についてであったり、セキュリティを担保するXML署名のことについて知る必要があると思います。
それらについては追々書いていきたいです。