JBoss7.xとAzureADで、SAMLを使ってシングルサインオン

JBoss EAP 7.xとAzureADで、SAMLを使ってシングルサインオンをする手順を記載します。
Azure AD自体の設定については、記載していません。

本手順は、下記の記事をベースにしたものになります。

https://access.redhat.com/solutions/3321901

過去のJBossでは、Picketlink を使用して SAML v2 でシングルサインオンを設定していましたが、JBoss7.xから非推奨となりました。下記の手順は、非推奨となっています。

https://access.redhat.com/documentation/ja-jp/red_hat_jboss_enterprise_application_platform/7.4/html-single/how_to_set_up_sso_with_saml_v2/index

## 手順の流れ
手順の流れは以下です。
手順はJBoss EAP 7.3、zipインストールベースで記載しています。

JBossとSAMLアダプターの設定
・JBossのインストール
・SAMLアダプターのインストール

Webアプリの設定
・keycloak-saml.xmlの設定
・Azure ADの公開鍵のインポート
・web.xmlの設定

## 1. JBossのインストール

[Red Hat カスタマーポータル](https://access.redhat.com/)にログインし、 Red Hat JBoss Enterprise Application Platform をダウンロードします。
※本来はパッチもダウンロードして、パッチを当ててください。
JBossのzipインストールは、zip解凍すれば終わりです。配置場所は、任意です。

```
$ unzip jboss-eap-7.3.0.zip
```

zipを展開して作成したディレクトリーは、EAP_HOMEとします。
環境変数にしておきます。

```
$ export EAP_HOME="zipを展開して作成したディレクトリー"
```

・参考手順

https://access.redhat.com/documentation/ja-jp/red_hat_jboss_enterprise_application_platform/7.3/html/installation_guide/product_installation#zip_installation

## 2. SAMLアダプターのインストール

[Red Hat カスタマーポータル](https://access.redhat.com/)にログインし、SAMLアダプターをダウンロードします。
SAMLアダプターは、Red Hat Single Sign-Onの箇所にRed Hat Single Sign-On 7.4.0 SAML Adapter for JBoss EAP 7のダウンロードファイル名で掲載してあります。
今回、ダウンロードしたファイル名は「rh-sso-7.4.0-saml-eap7-adapter.zip」です。
※本来はパッチもダウンロードして、パッチを当ててください。

EAP_HOMEに、SAMLアダプターを展開します。

```
$ mv rh-sso-7.4.0-saml-eap7-adapter.zip $EAP_HOME
$ cd $EAP_HOME
$ unzip rh-sso-7.4.0-saml-eap7-adapter.zip
```
JBossを起動します。

```
$EAP_HOME/bin/standalone.sh
```

別コンソールでSAMLアダプターをインストールします。

```
$EAP_HOME/bin/jboss-cli.sh -c --file=$EAP_HOME/bin/adapter-elytron-install-saml.cli
```

実行結果は、以下となります。

```
{"outcome" => "success"}
{
    "outcome" => "success",
    "response-headers" => {
        "operation-requires-reload" => true,
        "process-state" => "reload-required"
    }
}
{
    "outcome" => "success",
    "response-headers" => {"process-state" => "reload-required"}
}
{
    "outcome" => "success",
    "response-headers" => {"process-state" => "reload-required"}
}
{
    "outcome" => "success",
    "response-headers" => {"process-state" => "reload-required"}
}
{
    "outcome" => "success",
    "response-headers" => {"process-state" => "reload-required"}
}
{
    "outcome" => "success",
    "response-headers" => {"process-state" => "reload-required"}
}
{
    "outcome" => "success",
    "response-headers" => {"process-state" => "reload-required"}
}
{
    "outcome" => "success",
    "response-headers" => {"process-state" => "reload-required"}
}
```

以上で、SAMLアダプターのインストールは終了です。

この手順は、JBoss側ではなく、Red Hat Single Sign-on側のマニュアルに記載があります。

https://access.redhat.com/documentation/en-us/red_hat_single_sign-on/7.3/html/securing_applications_and_services_guide/saml_2#saml_jboss_adapter

## 3. keycloak-saml.xmlの設定

　Azure ADとの連携設定は、keycloak-saml.xmlで実施します。
　keycloak-saml.xmlは、WEB-INFフォルダ配下に格納します。

```xml:keycloak-saml.xml
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="urn:keycloak:saml:adapter https://www.keycloak.org/schema/keycloak_saml_adapter_1_10.xsd">
 
   <SP entityID="spn:0b832ef3-2594-49a4-b4ba-xxxxxxxxxxxx"
       sslPolicy="EXTERNAL"
       nameIDPolicyFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
       logoutPage="/logout.jsp"
       forceAuthentication="false"
       isPassive="false"
       turnOffChangeSessionIdOnLogin="false"
       autodetectBearerOnly="true">
 
       <PrincipalNameMapping policy="FROM_ATTRIBUTE" attribute="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"/>
 
       <RoleIdentifiers>
           <Attribute name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/department"/>
       </RoleIdentifiers>
 
       <IDP entityID="idp"
           signaturesRequired="true">
           <Keys>
               <Key signing="true">
                   <KeyStore resource="/WEB-INF/server.keystore" password="mykeystorepass">
                       <Certificate alias="azure"/>
                   </KeyStore>
               </Key>
           </Keys>
           <SingleSignOnService requestBinding="POST"
               bindingUrl="https://login.microsoftonline.com/d2895165-6747-442a-xxxx-xxxxxxxxxxxxxx/saml2"
               signRequest="true"
               validateAssertionSignature="true"
               validateResponseSignature="true"/>
 
           <SingleLogoutService
               requestBinding="POST"
               responseBinding="POST"
               postBindingUrl="https://login.microsoftonline.com/d2895165-6747-442a-xxxx-xxxxxxxxxxxxxx/saml2"
               redirectBindingUrl="https://login.microsoftonline.com/d2895165-6747-442a-xxxx-xxxxxxxxxxxxxx/saml2"
               signRequest="true"
               signResponse="true"/>
       </IDP>
   </SP>
 
</keycloak-saml-adapter>
```

Azure ADというかIdPのどの設定値をkeycloak-saml.xmlのどこに設定するかについて記載します。

・SP要素のentityID属性
entityIDは、Azure ADで登録したアプリケーションのアプリケーションIDを登録します。
spn:[Azure ADで登録したアプリケーションID]
先頭に「spn:」をつけてください。

・PrincipalNameMapping要素
policyは、FROM_NAME_IDとFROM_ATTRIBUTEの２つがあります。ここでは、FROM_ATTRIBUTEで指定しています。

```xml:keycloak-saml.xmlの抜粋
< PrincipalNameMapping policy="FROM_ATTRIBUTE" attribute="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"/>
```
FROM_ATTRIBUTE指定時のattribute値は、Azure ADのユーザ属性とクレーム画面に表示されているクレーム名を利用します。
間違えて、値の方を指定しないようにしてください。

・RoleIdentifiersの設定
　web.xml等で設定するロールマッピング用のロールを指定します。複数設定できます。
指定するAttributeは、PrincipalNameMappingの設定で記載したAzure ADのクレーム名を指定します。

```xml:keycloak-saml.xmlの抜粋
       <RoleIdentifiers>
           <Attribute name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/department"/>
           <Attribute name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"/>
       </RoleIdentifiers>
```

なお、IdP(Azure AD)から取得したAttributeの一覧は、HttpServletRequest.getUserPrincipal()をorg.keycloak.adapters.saml.SamlPrincipalにキャストして、getAttributeNames()の一覧をgetAttribute()に渡せば確認できます。　

・IdP要素のKeystoreの設定
IdP(Azure AD)で署名されたデータの検証用公開鍵をkeystoreに格納します。
Azure ADの公開鍵のインポートの箇所で公開鍵の取得方法と、keystoreへの格納方法を後述します。

```xml:keycloak-saml.xmlの抜粋
       <IDP entityID="idp"
           signaturesRequired="true">
           <Keys>
               <Key signing="true">
                   <KeyStore resource="/WEB-INF/server.keystore" password="mykeystorepass">
                       <Certificate alias="azure"/>
```

・SingleSignOnService要素のbindingUrlの設定
Azure ADで表示されているログイン URLを指定します。
私の環境では、ログアウトURLも同じ値でした。

```xml:keycloak-saml.xmlの抜粋
           <SingleSignOnService requestBinding="POST"
               bindingUrl="https://login.microsoftonline.com/d2895165-6747-442a-xxxx-xxxxxxxxxxxxxx/saml2"
               signRequest="true"
               validateAssertionSignature="true"
               validateResponseSignature="true"/>
```

以上で、keycloak-saml.xmlの設定は終了です。

keycloak-saml.xmlの説明は、マニュアルの下記にあります。

https://access.redhat.com/documentation/en-us/red_hat_single_sign-on/7.3/html/securing_applications_and_services_guide/saml_2#java_adapters_2

## 4.Azure ADの公開鍵のインポート

Azure ADから「証明書(Base64)」をダウンロードします。
keytoolを使い、keystoreにインポートします。

```sh:
$keytool -importcert  -file <Azure ADの公開鍵ファイル(.cer)>  -alias root -keystore <jboss.jksのファイルパス:今回はserver.keystore>
キーストアのパスワードを入力してください:   (キーストアのパスワードを入力ください：今回はmykeystorepass)
所有者: CN=Microsoft Azure Federated SSO Certificate
発行者: CN=Microsoft Azure Federated SSO Certificate
シリアル番号: 68c7d3fe30c26c8b46f2977159ed98f9
有効期間の開始日: Thu Jun 17 22:19:11 JST 2021終了日: Mon Jun 17 22:19:11 JST 2024
証明書のフィンガプリント:
	 SHA1: D3:3C:EF:D0:28:6F:D7:C8:02:69:B0:18:3C:D6:67:FB:21:8E:EE:0A
	 SHA256: 7D:84:1A:C2:84:35:F2:64:54:3D:86:65:D2:B4:C7:89:85:69:9F:D5:A6:F2:9A:F1:8F:42:9C:33:5D:9D:90:04

署名アルゴリズム名: SHA256withRSA
サブジェクト公開鍵アルゴリズム: 2048ビットRSA鍵
バージョン: 3
この証明書を信頼しますか。 [いいえ]:  はい
証明書がキーストアに追加されました
```

なお、公開鍵は有効期間がありますので、更新を考慮しておく必要があります。

## 5.web.xmlの設定
web.xmlの設定は、通常のsecurity-constraintやsecurity-roleの設定と同じです。
　login-configの箇所のauth-methodをKEYCLOAK-SAMLにします。

```xml:web.xmlの設定例
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
 
   <security-constraint>
       <web-resource-collection>
           <web-resource-name>Customers</web-resource-name>
           <url-pattern>/customers/*</url-pattern>
       </web-resource-collection>
       <auth-constraint>
           <role-name>user</role-name>
       </auth-constraint>
       <user-data-constraint>
           <transport-guarantee>NONE</transport-guarantee>
       </user-data-constraint>
   </security-constraint>
 
   <login-config>
       <auth-method>KEYCLOAK-SAML</auth-method>
       <realm-name>this is ignored currently</realm-name>
   </login-config>
 
   <security-role>
       <role-name>user</role-name>
   </security-role>
 
</web-app>
```

手順は以上となります。

注意点としては、Azure AD側で設定する応答URL(Assertion Consumer Service URL)は、
http[s]://{server}/{client-application-uri}/samlとなります。
URLの末尾の/samlを忘れないようにしてください。