【AWS】IDプロバイダーで外部連携経由のログイン（SAML認証）

# SAML認証とは
SAML認証（Security Assertion Markup Language Authentication）は、オンラインサービスやアプリケーションのユーザー認証に使用されるセキュリティプロトコルの1つです。

SAML認証は、ユーザーの認証情報（ユーザー名やパスワードなど）を安全に伝送するために、XMLベースのセキュリティトークンを使用します。このセキュリティトークンは、ユーザーの認証情報を含むだけでなく、認証情報が正当であることを証明する署名も含んでいます。

SAML認証は、クラウドベースのサービスやアプリケーションの認証に特に適しており、多数のユーザーを管理する場合にも有効です。また、SAML認証を使用することで、ユーザーが同じ認証情報を複数のアプリケーションで使用することができます。


SAML プロトコルは、次の 3 つの主要なパートから構成されています。

1. 認証要求 (Authentication Request): SAML 認証フローの開始を要求するメッセージです。このメッセージには、リクエストの ID、SAML バージョン、およびリダイレクト先などの情報が含まれます。

1. 認証応答 (Authentication Response): IdP (Identity Provider) がユーザーを認証した後に生成されるメッセージで、ユーザーが SSO に成功したかどうかを示します。このメッセージには、セキュリティトークンが含まれており、これを SP (Service Provider) に渡すことで、ユーザーが SP アプリケーションにアクセスできるようになります。

1. ログアウト要求 (Logout Request) およびログアウト応答 (Logout Response): ユーザーが SSO セッションを終了するために使用されるメッセージです。


# 用語
## IDP(identity provider)​
ユーザーの認証および認可情報を管理するオンラインシステムです。(認証情報を提供する側​)

## SP(service provider)​
SAML認証を利用する際に、認証情報を提供するためのサービスを提供するオンラインシステムです。(認証情報を要求、ユーザへサービスを提供する側​)

# SSO(Single Sign-On)の実現方式​
- エージェント方式​
    - Webシステムのサーバにエージェントソフトを導入し、エージェントが認証情報を管理​

- リバースプロキシ方式​
    - 端末とWebサーバ間にリバースプロキシサーバを設置し、エージェントソフトを導入​

- 代理認証方式​
    - 代理で認証を行うサーバを設置する方式​

- フェデレーション方式​
    - 異なるドメインのサービスでも認証情報を連携可能​
    - クラウドサービスの設定だけで実現可能だが、対応しているサービスは限られる​
    - 主に**SAML**が用いられる​
- 透過型方式​

https://it-trend.jp/single_sign_on/article/75-0015#chapter-2

https://it-trend.jp/single_sign_on/article/75-0028


 # 実現方法
AWSにSAMLの認証フローがあり、そこは詳しく書いてあります。

https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-saml-idp-authentication.html

![スクリーンショット 2023-03-24 16.40.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/ddd1471e-01ce-6872-8506-a6634207132d.png)

1. アプリはユーザーに AWS によってホストされる UI を指定することで、サインアップとサインインを開始します。モバイルアプリはウェブビューを使用して **AWS UI** のウェブページを表示することができます。
![hostedUI-login-IdP-selection.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/47ec4d64-820a-8f6c-c495-4effcc2517bb.png)


1. 通常、ユーザープールはユーザーの E メールアドレスからユーザーの IdP を判断します。
また、アプリがユーザーをユーザープールにダイレクトする前に情報を収集した場合は、クエリパラメータを通じてその情報を Amazon Cognito に提供することができます。

1. ユーザーは、SAML リクエストを使用して **IdP（認証情報を提供する側）** にリダイレクトされます。

1. 必要に応じて IdP がユーザーを認証します。IdP がユーザーにアクティブなセッションがあると認識している場合、IdP で認証がスキップされ、シングルサインイン (SSO) が提供されます。

1. IdP は Amazon Cognito サービスに SAML アサーションを POST します。

1. SAML アサーションが検証され、アサーションからユーザー属性 (クレーム) が収集されると、Amazon Cognito がユーザープールのユーザープロファイルを内部で作成または更新します。Amazon Cognito が、サインインされたユーザーの OIDC トークンをアプリに返します。

***シーケンス図***
![スクリーンショット 2023-03-24 17.33.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/0f82bb79-892c-5bf1-a245-47e77fa11505.png)


# ユーザープールの SAML ID プロバイダーの作成
## AWSコンソールで作成
下記のaws コマンドでID プロバイダーを作成する
```sh
aws cognito-idp create-identity-provider
```
頂いた***IDPメタデータ***をID プロバイダーにアップロードします。
- メタデータファイルの例:
```sh
aws cognito-idp create-identity-provider --user-pool-id <user_pool_id> --provider-name=SAML_provider_1 --provider-type SAML --provider-details file:///details.json --attribute-mapping email=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
```
- メタデータ URL の例
```sh
aws cognito-idp create-identity-provider --user-pool-id <user_pool_id> --provider-name=SAML_provider_1 --provider-type SAML --provider-details MetadataURL=<metadata_url> --attribute-mapping email=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
```

結果:
```sh
{ 
    "MetadataFile": "<SAML metadata XML>"
}
```
***SAML IDPメタデータ XML に引用符が (") 含まれる場合は、エスケープ (\") する必要があります。***

## Cognitoのコンソール画面から作成
Cognito コンソール>user pool>フェデレーション>ID プロバイダー
![スクリーンショット 2023-03-26 13.39.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/24e291ad-d0ca-55d2-1c69-bdd8107b503b.png)

プロバイダー作成後、その有効化を忘れないように
>Cognito>User Pool>アプリの統合>アプリクライアントの設定から設定できます。


![スクリーンショット 2023-03-28 14.37.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/d2555cd6-63a9-83ca-9c63-cbc411a5e124.png)


# SP メタデータ
IDP側がメタデータを提供していて、SP側もメタデータを提供しないといけません。
IDPメタデータはAWS Cognitoの属性を基に作成されています。
サンプルは下記です。

https://signin.aws.amazon.com/static/saml-metadata.xml

## entityID
SAML IdPs によっては、SP urn / Audience URI / SP エンティティ ID を次の形式で指定する必要もあります。
```xml
urn:amazon:cognito:sp:<yourUserPoolID>
```

```xml
<?xml version="1.0"?>
<EntityDescriptor entityID="urn:amazon:cognito:sp:ap-northeast-********"
                  xmlns="urn:oasis:names:tc:SAML:2.0:metadata">
```
`ap-northeast-********` はプールIDです。

>Cognito>User Pool>全般設定
プールIDを確認できます。


![スクリーンショット 2023-03-27 10.46.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/0502c636-6b80-06fa-55d8-b67fbd042cc9.png)

## デジタル署名証明書
SAML 2.0 IDP に提供するデジタル署名用証明書は`フェデレーション>ID プロバイダー!`から確認できます。
![a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/b559d305-863e-17f3-e729-cf81b7c1b41c.png)

```xml
<ds:X509Data>
<ds:X509Certificate>MIIDbTCCAlWgAwIBAgIEeICyWTANBgkqhkiG9w0BAQsFADBnMR8wHQYDVQQDExZ1.... 
</ds:X509Certificate>
</ds:X509Data>
```

## binding endpoint
IdP は次の POST binding endpointを IdP-to-SP 応答メッセージに使用します。`https://Your user pool domain/saml2/idpresponse`

```sh
https://Your user pool domain/saml2/idpresponse
With an Amazon Cognito domain:
https://<yourDomainPrefix>.auth.<region>.amazoncognito.com/saml2/idpresponse
With a custom domain:
https://Your custom domain/saml2/idpresponse
```

>Cognito>User Pool>アプリの統合>ドメイン名
ドメイン名を確認できます。
```xml
<AssertionConsumerService index="1" isDefault="true" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://user-pool-domain.auth.ap-northeast-1.amazoncognito.com/saml2/idpresponse"/>
```

# マッピング
>Cognito>User Pool>フェデレーション>属性マッピング
属性マッピングを設定できます。

![スクリーンショット 2023-03-27 13.30.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/10c61a35-bf4a-cd1e-a3d2-35c925d6cb0b.png)

![b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/2a8aedc7-e248-bedb-fbd1-9829f8485381.png)


# 認証

SAML認証は下記のURLでリダイレクトします。

>https://your_Amazon_Cognito_userpool_domain/authorize?response_type=code&identity_provider=your-SAML-IdP-name&client_id=your-client-id&redirect_uri=https://your_application_redirect_url

リクエストで適切なパラメータを使用すると、/authorize では、Amazon Cognito は SP が開始したサインインフローをサイレントに開始し、IdP でサインインするようにユーザーをリダイレクトします。

- SP側から認証の場合
    1. ユーザーが SP(サービス提供側) にアクセスする
    2. SP がSAML認証要求を作成する(AWSのログインUI画面)
    3. SP から受け取ったSAML認証要求を IDP に送信する
    4. IDP の認証画面が表示される
    5. ユーザーはIDPの認証情報を入力して IdP との間で認証処理を行なう
    6. 認証が成功すると、IdP からSAML認証応答が発行される
    7. ユーザーは IDP から受け取ったSAML認証応答を SP に送信する
    8. SP にSAML認証応答が届くとログインができる

- IDPから認証の場合
    1. ユーザーが IDP(認証側・会社のポータルサイトなど) にアクセスする
    2. IDP の認証画面が表示される
    3. ユーザーはIDP認証情報を入力して認証処理を行なう
    4. 認証が成功すると、IDPにログインできる
    5. IDP の画面からアプリケーションを選択する
    6. IDP 側でSAML認証応答が発行される
    7. ユーザーは IDP から受け取ったSAML認証応答を SP に送信する
    8. SP にSAML認証応答が届くとログインできる
![スクリーンショット 2023-03-27 18.48.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/89e8ea70-409d-0247-e310-576c2c521df3.png)

## TOKEN発行

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/token-endpoint.html

認証で発行した`code`を用いて、TOKENを発行します。
```sh
POST https://mydomain.auth.us-east-1.amazoncognito.com/oauth2/token&
                            Content-Type='application/x-www-form-urlencoded'&
                            Authorization=Basic ZGpjOTh1M2ppZWRtaTI4M2V1OTI4OmFiY2RlZjAxMjM0NTY3ODkw
                            
                            grant_type=authorization_code&
                            client_id=1example23456789&
                            code=AUTHORIZATION_CODE&
                            redirect_uri=com.myclientapp://myclient/redirect
```

レスポンス例：
```sh
HTTP/1.1 200 OK
                        
                           Content-Type: application/json
                           
                           { 
                            "access_token":"eyJra1example", 
                            "id_token":"eyJra2example",
                            "refresh_token":"eyJj3example",
                            "token_type":"Bearer", 
                            "expires_in":3600
                           }
```

- redirect_uri
Amazon Cognito がユーザーを認証した後、認証サーバーによってブラウザがリダイレクトされる URL です。URI を事前にクライアントに登録しておく必要があります。
    1. 「アプリケーションの統合」タブ
    1. アプリケーションクライアント
    1. ホストされた UI
    1. 許可されているコールバック URLを追加する


## UserInfo エンドポイント
https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/userinfo-endpoint.html

リクエスト例:
```sh
GET /oauth2/userInfo HTTP/1.1
Content-Type: application/x-amz-json-1.1
Authorization: Bearer eyJra12345EXAMPLE
User-Agent: [<replaceable>User agent<replaceable>]
Accept: */*
Host: auth.example.com
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
```
上記例のAuthorizationの　**eyJra12345EXAMPLE** はaccessTokenです。
# 参考
https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pools-identity-federation.html