WebSphere＋OpenAMのSAML SSO環境構築（3/3）（SSO実践編）

[前回](https://qiita.com/ch7821/items/b7e55ea2c194a5380f2d)と[前々回](https://qiita.com/ch7821/items/b3311b8c6cfe1fb47b39)で、それぞれSPとIdPの環境を作成しました。今回は、お互いの連携を取り込んで、実際にSAMLによるSSOシナリオを実践してみます。

IdPとSPとのメタデータ交換
===========================================================================================
これまででIdPとSPとを個別に構築してきました。一部、SP側にはIdPの情報を取り込んでいますが（トラステッドレルムとしてIdPの`EntityID`を設定済み）、IdP側はSPの存在を知りませんし、SP側はIdPの証明書や具体的なサービス情報を持っていません。というわけで、メタデータと呼ばれるXML文書を、IdPとSPとで交換します（お互いにエクスポートしたものをインポート）。

IdPメタデータには以下のURLからアクセスできます。
`https://www.alto.net/openam/saml2/jsp/exportmetadata.jsp`
このデータを`idpMetadata.xml`として保存します。

少し中身を見てみます。

```xml:idpMetadata.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<EntityDescriptor entityID="https://www.alto.net/openam" xmlns="urn:oasis:names:tc:SAML:2.0:metadata">
    <IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
        <KeyDescriptor use="signing">
            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <ds:X509Data>
                    <ds:X509Certificate>MIIDvjCCA〜省略〜</ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </KeyDescriptor>
        <ArtifactResolutionService index="0" isDefault="true" Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://www.alto.net:443/openam/ArtifactResolver/metaAlias/idp"/>
        <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://www.alto.net:443/openam/IDPSloRedirect/metaAlias/idp" ResponseLocation="https://www.alto.net:443/openam/IDPSloRedirect/metaAlias/idp"/>
        <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.alto.net:443/openam/IDPSloPOST/metaAlias/idp" ResponseLocation="https://www.alto.net:443/openam/IDPSloPOST/metaAlias/idp"/>
        <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://www.alto.net:443/openam/IDPSloSoap/metaAlias/idp"/>
        <ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://www.alto.net:443/openam/IDPMniRedirect/metaAlias/idp" ResponseLocation="https://www.alto.net:443/openam/IDPMniRedirect/metaAlias/idp"/>
        <ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.alto.net:443/openam/IDPMniPOST/metaAlias/idp" ResponseLocation="https://www.alto.net:443/openam/IDPMniPOST/metaAlias/idp"/>
        <ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://www.alto.net:443/openam/IDPMniSoap/metaAlias/idp"/>
        <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
        <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
        <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
        <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>
        <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName</NameIDFormat>
        <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos</NameIDFormat>
        <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</NameIDFormat>
        <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://www.alto.net:443/openam/SSORedirect/metaAlias/idp"/>
        <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.alto.net:443/openam/SSOPOST/metaAlias/idp"/>
        <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://www.alto.net:443/openam/SSOSoap/metaAlias/idp"/>
        <NameIDMappingService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://www.alto.net:443/openam/NIMSoap/metaAlias/idp"/>
        <AssertionIDRequestService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://www.alto.net:443/openam/AIDReqSoap/IDPRole/metaAlias/idp"/>
        <AssertionIDRequestService Binding="urn:oasis:names:tc:SAML:2.0:bindings:URI" Location="https://www.alto.net:443/openam/AIDReqUri/IDPRole/metaAlias/idp"/>
    </IDPSSODescriptor>
</EntityDescriptor>
```

前々から何度か出てきている、`entityID`属性や証明書（`ds:X509Certificate`要素）に加えて、IdPとして提供するサービス（ログイン、ログアウトページ等）のURLが記載されている他、`NameIDFormat`属性で、IdPがサポートするIDのタイプが明示されています。ちなみに、IdPとのやり取りにおいて、この`NameIDFormat`等がきちんと整合していないと、IdP側で`Bad Request`として処理されてしまいます。

出力した`idpMetadata.xml`をWASコンテナの`/tmp`ディレクトリにコピーします。

```bash
mac:$ docker cp ./idpMetadata.xml was:/tmp
```

コンテナに入ってメタデータをインポートして保存します。メタデータのインポート・エクスポートは`wsadmin.sh`コマンドから行います。

```bash
mac:$ docker exec -it was bash
root:# su - piro
$ /opt/IBM/WebSphere/AppServer/profiles/AppSrv01/bin/wsadmin.sh
wsadmin> AdminTask.importSAMLIdpMetadata('-ssoId 1 -idpId 1 -idpMetadataFileName /tmp/idpMetadata.xml -signingCertAlias MognetIdp')
wsadmin> AdminConfig.save()
```

メタデータ内の証明書を`MognetIdp`というエイリアス名で「トラステッド」としています。CAの証明書がなくてもOKなようです。
続いてSPメタデータをエクスポートします。

```bash
wsadmin> AdminTask.exportSAMLSpMetadata('-ssoId 1 -spMetadataFileName /tmp/spMetadata.xml')
wsadmin> exit
```

コンテナからコピーしてきます。

```bash
mac:$ docker cp was:/tmp/spMetadata.xml ./
```

こちらも中身を確認してみます。

```xml:spMetadata.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns7:EntityDescriptor xmlns="http://www.w3.org/2005/08/addressing" xmlns:ns2="http://docs.oasis-open.org/wsfed/federation/200706" xmlns:ns3="http://docs.oasis-open.org/wsfed/authorization/200706" xmlns:ns4="http://www.w3.org/2001/04/xmlenc#" xmlns:ns5="http://www.w3.org/2000/09/xmldsig#" xmlns:ns6="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ns7="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ns8="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns9="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" entityID="https://www.docker.mognet.net/samlsps/acs">
    <ns7:SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
        <ns7:AssertionConsumerService index="0" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.docker.mognet.net/samlsps/acs"/>
    </ns7:SPSSODescriptor>
</ns7:EntityDescriptor>
```

ほとんどが固定情報です。SAMLの様々なスキーム（バインディングとかプロファイルとかいいます）の中で`HTTP-POST`バインディングをサポートするという表明や、ACSのロケーションURLが定義されています（`ns7:AssertionConsumerService`要素の`Location`属性）。あと、もはやお馴染みになってきた`entityID`属性が、`ns7:EntityDescriptor`要素に入っており、こちらもACSのURLが記載されています。実はこの二箇所を変えてIdPに渡してやるだけで、たいていのシナリオでは動作可能だったりします。

では、SPメタデータをIdPにインポートします。OpenAMのWebコンソールを開き、`amadmin`ユーザでログインし、「Top Level Realm」に入ります。
![スクリーンショット 2018-01-26 22.13.34.png](https://qiita-image-store.s3.amazonaws.com/0/232587/1d85bfa6-3360-1c8a-c64c-e5a210e0f240.png)「Create SAMLv2 Providers」に入ります。![スクリーンショット 2018-01-26 22.13.45.png](https://qiita-image-store.s3.amazonaws.com/0/232587/086d6349-1467-7329-c1da-d4042b16c221.png)「Register Remote Service Provider」に入ります。![スクリーンショット 2018-01-26 22.14.08.png](https://qiita-image-store.s3.amazonaws.com/0/232587/4edbde4d-8c42-ee4e-dc04-f51683b65904.png)メタデータファイルの保存場所に「ファイル」をチェックし、`spMetadata.xml`を「アップロード」して「設定」します。![スクリーンショット 2018-01-26 22.14.28.png](https://qiita-image-store.s3.amazonaws.com/0/232587/c0e9ac89-8433-5bbe-35cb-086aba05a7de.png)

これでメタデータの交換が完了です。

SSO実践！
===========================================================================================
今回のサービスとなるWAS付属サンプルアプリケーションである`snoop`には、セキュリティ制約がかかっています。まずはこれを確認してみます。SAML TAIを有効にするなかで、アプリケーションセキュリティを有効化していますので、今アクセスしても`403 AuthenticationFailed`となるはずです。
`https://www.docker.mognet.net/snoop`
![スクリーンショット 2018-01-26 22.20.29.png](https://qiita-image-store.s3.amazonaws.com/0/232587/d8faf996-69e7-f764-2640-969037053af4.png)
無事に拒否されました。

具体的にどのようなセキュリティ制約がかかっているのか見てみます。WASの管理コンソールにログインし、「アプリケーション」ー「WebSphereエンタープライズ・アプリケーション」を開き、「DefaultApplication」に入ります。![スクリーンショット 2018-01-26 22.22.32.png](https://qiita-image-store.s3.amazonaws.com/0/232587/acc8d9b3-a08d-2170-9505-b48a37d1ca21.png)「ユーザー/グループへのセキュリティー・ロールのマッピング」に入ります。![スクリーンショット 2018-01-26 22.22.42.png](https://qiita-image-store.s3.amazonaws.com/0/232587/2377644d-0698-b16b-fc3e-c76e20a29799.png)「特別な対象（"special subject"）」として「アプリケーションのレルム内で認証済みすべて（`ALL_AUTHENTICATED_USERS`）」のユーザが、`ALL Role`という名前のロールにマッピングされることがわかります。今回は外部レルムで認証したユーザのSSOシナリオなので、認証範囲を広げます。`ALL Role`にチェックして、「特別な対象のマップ」から「トラステッド・レルム内で認証済みすべて（`ALL_AUTHENTICATED_USERS_IN_TRUSTED_REALMS`）」に変更し、「OK」します。ちなみに、一番広い認証範囲は「全員（`EVERYONE`）」で、認証がないものと同然です。デフォルトのロールにマップするときに使えそうです[^1]。![スクリーンショット 2018-01-26 22.25.08.png](https://qiita-image-store.s3.amazonaws.com/0/232587/4b55e26c-e255-3f31-e956-4354dcb3dcc4.png)構成を「保存」して、サーバを再起動します。
![スクリーンショット 2018-01-26 22.25.36.png](https://qiita-image-store.s3.amazonaws.com/0/232587/494f67d2-cbe3-5385-6464-7c3e8f38b486.png)

[^1]: カッコ書き内の`special-subject`の値は、IBM拡張デプロイメント記述子である`ibm-application-bnd.xml`における指定値で、`<special-subject type="ALL_AUTHENTICATED_USERS_IN_TRUSTED_REALMS" />`のように指定


IdP-initiated SAML
-------------------------------------

これで準備は全て整いました。以下のURLにアクセスし、IdP-initiated SAMLによるSSOシナリオを実践します。なお、使用するブラウザはFire Foxを推奨します。Addonとして`SAML tracer`という便利なツールがありますので、これを使ってどのようなリクエストをしているのかを追いかけてみます（ブラウザ標準のトレーサーでもリクエストはキャプチャできますが、`SAML tracer`を使うとBase64されているSAMLアサーションもみることができます。それだけっちゃそれだけですが。。。）。

```
https://www.alto.net/openam/idpssoinit?metaAlias=%2Fidp&spEntityID=https%3A%2F%2Fwww.docker.mognet.net%2Fsamlsps%2Facs&binding=urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Abindings%3AHTTP-POST&NameIDFormat=urn:oasis:names:tc:SAML:2.0:nameid-format:transient&RelayState=https%3A%2F%2Fwww.docker.mognet.net%2Fsnoop
```

クエリ文字列をURLエンコードしているので見辛いですが、エンコード前はこんな形です。

```
https://www.alto.net/openam/idpssoinit?metaAlias=/idp&spENtityID=https://www.docker.mognet.net/samlsps/acs&binding=urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST&NameIDFormat=urn:oasis:names:tc:SAML:2.0:nameid-format:transient&RelayState=https://www.docker.mognet.net/snoop`
```

IdPメタデータに出てきたサポートされるプロトコルスイートとでもいうべきSAMLのバインディングやIDフォーマットを指定しています。最後の`RelayState`はACSアクセス後のランディングポイントです（最終的にここに遷移するという指示ですがこれに従うかどうかはSPの実装に依ります）。

URLを入力して<kbd><kbd>enter</kbd></kbd>するとIdPのログインページが表示されます。
<img width="1070" alt="スクリーンショット 2018-01-26 18.29.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/232587/ab71df71-0b77-3d70-4077-1f9cd43f323b.png">IdP構築時に作成したLDAPユーザでログインすると。。。<img width="1561" alt="スクリーンショット 2018-01-26 18.34.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/232587/a11c6101-1fbe-7028-c039-2713507fe87f.png">
無事に`snoop`サーブレット画面になりました。`LtpaToken2`クッキーがリクエストヘッダについていることに注目です。これはTAIがリクエストをインターセプトしてSAMLを検証し、ACSにディスパッチした時点で生成されています。これは`snoop`ページへ至るまでに、クライアントへ`LtpaToken2`クッキーが落ちてきていることを示唆しています。

もう一つ注目しておきたいのは、`snoop`へのリクエストメソッドが`GET`であること。これはACSサーブレット（/samlsps/acsでアクセスした時のサーブレット）の仕様です。`SAML tracer`を使ったトレースを見てみます。
![名称未設定.png](https://qiita-image-store.s3.amazonaws.com/0/232587/059961fb-a4f7-a4f8-5813-6d13d05f642b.png)
ACSに対するブラウザからのリクエストは`POST`です。これに対してサーバ（サーブレット）は、`302 Found（Location: https://www.docker.mognet.net/snoop）`をクライアントへ返しています（`LtpaToken2`クッキーも`Set-Cookie`内に確認できます）。HTTPレスポンスコード302に関して[W3C（rfc2616）`https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html`](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)から引用します。

> 10.3.3 302 Found
> 
> The requested resource resides temporarily under a different URI. Since the redirection might be altered on occasion, the client SHOULD continue to use the Request-URI for future requests. This response is only cacheable if indicated by a Cache-Control or Expires header field.
> 
> The temporary URI SHOULD be given by the Location field in the response. Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s).
> 
If the 302 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued.
> 
>      Note: RFC 1945 and RFC 2068 specify that the client is not allowed
>      to change the method on the redirected request.  However, most
>      existing user agent implementations treat 302 as if it were a 303
>      response, performing a GET on the Location field-value regardless
>      of the original request method. The status codes 303 and 307 have
>      been added for servers that wish to make unambiguously clear which
>      kind of reaction is expected of the client.

３段落目に、`GET/HEAD`以外（`POST`も）のリクエストで`302`が返されたらブラウザはユーザの許可なく自動リダイレクトしちゃダメとあります。一方で、`Note:`内では実際問題としてほとんどのブラウザは`302`を`303(See Other)`と同じようにあつかって、元々のリクエストメソッドがなんだろうと`GET`しちゃってるんだよね〜（RFC 1945とRFC 2068ではそれダメって言ってんのにぃ）と書いてあります。本来の仕様からすると(・A・)ｲｸﾅｲんでしょうけどとにかくこのような動作になっています。

もうひとつ、`SAML tracer`でキャプチャしたSAMLアサーションを載せます。

```xml:samlAssertion.xml
samlsps
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                ID="s261d5ac6132957c5b0a805d23660a34f7f51b3938"
                Version="2.0"
                IssueInstant="2018-01-26T14:27:16Z"
                Destination="https://www.docker.mognet.net/samlsps/acs"
                >
    <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://www.alto.net/openam</saml:Issuer>
    <samlp:Status xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
        <samlp:StatusCode xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                          Value="urn:oasis:names:tc:SAML:2.0:status:Success"
                          />
    </samlp:Status>
    <saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
                    ID="s2ae3fd88faf755415bcea1c6607e5338187dac51b"
                    IssueInstant="2018-01-26T14:27:16Z"
                    Version="2.0"
                    >
        <saml:Issuer>https://www.alto.net/openam</saml:Issuer>
        <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
            <ds:SignedInfo>
                <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
                <ds:Reference URI="#s2ae3fd88faf755415bcea1c6607e5338187dac51b">
                    <ds:Transforms>
                        <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                        <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                    </ds:Transforms>
                    <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                    <ds:DigestValue>3v/ZkinNrxi8b99CZ9fTrcOQGlg=</ds:DigestValue>
                </ds:Reference>
            </ds:SignedInfo>
            <ds:SignatureValue>E4mFLhn1〜省略〜</ds:SignatureValue>
            <ds:KeyInfo>
                <ds:X509Data>
                    <ds:X509Certificate>MIIDv〜省略〜</ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </ds:Signature>
        <saml:Subject>
            <saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
                         NameQualifier="https://www.alto.net/openam"
                         SPNameQualifier="https://www.docker.mognet.net/samlsps/acs"
                         >amxWs8lXlZwgj+Ai40cSrRIl0Qkt</saml:NameID>
            <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                <saml:SubjectConfirmationData NotOnOrAfter="2018-01-26T14:37:16Z"
                                              Recipient="https://www.docker.mognet.net/samlsps/acs"
                                              />
            </saml:SubjectConfirmation>
        </saml:Subject>
        <saml:Conditions NotBefore="2018-01-26T14:17:16Z"
                         NotOnOrAfter="2018-01-26T14:37:16Z"
                         >
            <saml:AudienceRestriction>
                <saml:Audience>https://www.docker.mognet.net/samlsps/acs</saml:Audience>
            </saml:AudienceRestriction>
        </saml:Conditions>
        <saml:AuthnStatement AuthnInstant="2018-01-26T14:27:16Z"
                             SessionIndex="s272da2db3aa0851a04b36a643b133ffe2df37ce01"
                             >
            <saml:AuthnContext>
                <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
            </saml:AuthnContext>
        </saml:AuthnStatement>
        <saml:AttributeStatement>
            <saml:Attribute Name="Description">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >hi! i&apos;m alto the border collie</saml:AttributeValue>
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >lovely dog</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="MailAddress">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >alto@mognet.net</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="Locality">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >Tokyo</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="EmployeeNumber">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >100</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="GivenName">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >Alto</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="UserPassword">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >{SSHA512}4IQR42cG2N4Wqu/SCFM1eRLwENTFYWWcJoN0DcGjiQz/aZR0eU7csdshMugJqTVbE7KNJ6AcSswHdqkjU1ap2BnwNcd1AY3H</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="DepartmentNumber">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >0</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="TelephoneNumber">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >0120123456</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="BusinessCategory">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >sheep dog</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="UserId">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >alto</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="CarLicense">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >n/a</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="DisplayName">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >alto the sweet</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="SirName">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >Benjiamin</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="MognetId">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >alto1</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="CommonName">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >Jiang Benjiamin Alto of Mountfield</saml:AttributeValue>
            </saml:Attribute>
        </saml:AttributeStatement>
    </saml:Assertion>
</samlp:Response>
```

`saml:AttributeStatement`要素内にIdP構築時にマッピングしたSAML属性が`saml:Attribute`要素の`Name`属性および`saml:AttributeValue`要素として渡っていることが確認できます。アプリケーションからはこの値をTAIが生成した`Subject`オブジェクトから取得できます。

まだ`SP-redirected`をやっていないのと、TAIのカスタムプロパティでいろいろと動きを変えられそうなので、この辺りも検証してきたいところです。それから、実際にアプリケーションからSAML属性を取得するときの実際のコードも確認したいですね。長くなったのでここでひと段落として、次回以降でこれらを見ていきます。