Keycloak と Microsoft Entra ID (旧称 Azure Active Directory) とをSAML連携してみる

**本記事は以下のような読者を想定しています**

- Microsoft Entra ID (旧称 Azure AD) をIdPとした、KeycloakのSAML連携をお手軽に試してみたい方

はじめに
---
Keycloakは認証認可のOSSソフトウェアです。

Keycloakのみを利用してユーザ認証を構築できますが、この記事ではKeycloakの外部ID連携機能を利用してMicrosoft Entra IDと連携し、Microsoft Entra ID側にユーザ認証を委任してみます。

Microsoft Entra IDとの外部ID連携では、OIDCとSAMLの両方が利用可能ですが、この記事ではSAMLを用いて連携を試みます。

## 1. 環境構築
### 1.1 Keycloakを起動
テスト用の Keycloak環境をお手軽に準備する為に、Dockerを使います。
Dockerを使えない方は、まずはDocker Desktopをセットアップしてください。
個人利用では無償で利用可能です。

https://matsuand.github.io/docs.docker.jp.onthefly/get-docker/

以下のコマンドで、Docker版のKeycloakを起動します。

```bash
docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:23.0.3 start-dev
```
https://www.keycloak.org/getting-started/getting-started-docker

### 1.2 Keycloakの初期設定

Keycloak起動後は、Keycloakの初期設定を行います。

* ブラウザで http://localhost:8080/admin にアクセスします。
* ユーザ名: admin、パスワード: admin　で管理者として管理画面にサインインします。
* 画面左上の master > Create realmをクリックし、レルム(realm)としてmyrealm を作成します。

<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/2c5c6331-b78c-ee30-f53b-c5f638807881.png">

以後、myrealmで設定を続けます。

:::note info
レルム(realm) とは？
Keycloakは、realm ごとに独立してユーザアカウントや設定を持つことができます。
adminとしてサインインした直後は、masterという管理用のrealmがアクティブになっています。
セキュリティの関係上、アプリケーション用に別のrealmを使うのが望ましいので、myrealmを作成します。
:::

### 1.3 Keycloak へ Entra ID の設定を追加
Keycloak のサインイン画面で、Entra IDを選択できるようにするため、IDプロバイダーを作成します。
管理者画面から Identity Providers > SAML v2.0 を選択します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/495f9de3-1507-3867-ca02-8260ad4721af.png)

IDプロバイダーを作成すると設定画面が出てくるので適当にAlias名を設定します。ここでは以下のように設定します。

Alias : MicrosoftEntraID

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/b085077e-3430-4e4c-bbbf-29867feaf061.png)

以下の値は、後で使うのでメモしておきます。
* Redirect URI: http://localhost:8080/realms/myrealm/broker/MicrosoftEntraID/endpoint
* Service provider entity: http://localhost:8080/realms/myrealm

SAML entity descriptor の欄を埋めないとAddできません。
一旦このままにしておいてブラウザの別タブを開き、Entra ID側　の設定に移ります。

### 1.4 Entra ID でユーザの作成

Azureにサインインします。
まだAzureのアカウントを作ってない方は、作成してください。

Azureにサインイン後、Keycloakとの連携を実験するためのユーザを作成します。
適当なユーザを作成済みの場合は、ここの手順は省略してOKです。

ホーム > Microsoft Entra ID > ユーザー > 新しいユーザー > 新しいユーザーの作成

ここでは以下のように設定し、ユーザーを作成します。
ユーザープリンシパル名: test1
表示名: test1

パスワード、ユーザープリンシパル名は後で使うのでメモします。

### 1.5 Entra ID でエンタープライズアプリケーションを作成

ホーム > Microsoft Entra ID > エンタープライズアプリケーション > 新しいアプリケーション > 独自のアプリケーションの作成 をクリックします。

アプリ名は、なんでも良いのですが ここでは Keycloak とします。
アプリケーションでどのような操作を行いたいですか? では、「ギャラリーに見つからないその他のアプリケーションを統合します (ギャラリー以外)」を選択して、作成をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/984108e6-764a-b578-fcfe-e8801ac114eb.png)

しばらく待てば作成されます。

#### 1.5.1 ユーザーとグループの割り当て

Keycloakと連携可能なユーザをここで選択します。

ユーザーとグループ > ユーザーまたはグループの追加　から、ユーザーを選択してください。
ここでは、事前に作成していた test1 を選択し、割り当てます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/75fe6bca-5d7f-9891-2a21-c1b37c6a7f60.png)


#### 1.5.2 シングルサインオンの設定

シングルサインオン > SAMLを選択します。

基本的な SAML 構成 で編集をクリックします。

識別子 (エンティティ ID) には、Keycloak側でService provider entityとしてメモしていた`http://localhost:8080/realms/myrealm` を設定します。

Redirect URI には、Keycloak側でRedirect URI としてメモしていた` http://localhost:8080/realms/myrealm/broker/MicrosoftEntraID/endpoint`を設定します。

前後に空白があるとうまく設定できないので注意してください。

最後に、保存を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/07494fd1-3c96-91a1-61d0-897bfeb84a56.png)

シングルサインオンをtestするか聞かれますが、いいえ、後でtestしますをクリックしてください。

属性とクレーム > 一意のユーザー識別子 (名前 ID) > ソース属性　を、
user.principalname から user.mailに変更し、保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/c03845f9-eb30-a041-3192-5c7e1d8a4356.png)


SAML証明書の、アプリのフェデレーション メタデータ URL　をコピーしてメモしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/141a886a-81f1-212f-6b99-287753cfb6d7.png)


### 1.6 Keycloak 側の設定に戻る
#### 1.6.1 Keycloak側でIdPの追加を完了させる

ここで、完了させずにいたKeycloak側の設定に戻ります。

KeyCloak のSAML entity descriptor に メモしておいたアプリのフェデレーション メタデータ URL を設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/a145efc0-c0cc-39e4-f5b7-ba0633e63b10.png)

これにより、 IdP のSAML証明書の公開鍵など、必要な情報が一度にセットされるので便利です。


Addをクリック して、KeycloakへのIdPの追加を完了します。
もしAddを押す前にKeycloakのログイン画面に戻ってしまった場合は、admin / adminでログインしてください。その際は、Aliasが初期値に戻ってしまっているので、設定し直してください。(例：MicrosoftEntraID)

:::note info
* フェデレーション メタデータ URLを貼り付けた際にネットワーク絡みのエラーが出る場合は、Keycloak から Microsoft Entra ID への443ポートアクセスが疎通しているか確認してください。
* どのような内容がIdPからKeycloakに渡されるか知るためには、ブラウザで アプリのフェデレーション メタデータ URL を直接開いてみてください。

:::

#### 1.6.2 Clientの作成

Keycloak でclientを作成します。
clients > Create client をクリックします。
以下のように設定します。

Client type: OpenID Connect
Client ID: myclient
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/82bc6653-4a39-0186-7d3f-8f6339750322.png)

Nextをクリックして、　Standard flow にチェックが入っていることを確認し、Nextを押します。

以下のように設定します。

Valid redirect URIs　: https://www.keycloak.org/app/*
Web origins : https://www.keycloak.org

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/67397da6-c9c9-bd19-eeaa-464ec1e34f61.png)

最後に、Saveをクリックします。

#### 1.5.3 MicrosoftEntraID の設定変更
Identity Providers > MicrosoftEntraID > NameID policy format で、NameID policy format を persistent から email に変更し、Saveを押します。

## 2. サインインテスト

ここまでで環境構築が終わりました。サインインのテストを行います。

ブラウザで、 https://www.keycloak.org/app/ にアクセスしてください。
ここで、ブラウザのサインインのキャッシュにヒットすると誤動作することがある為、プライベートウィンドウを使った方が確実です。

以下のように設定されていることを確認し、
* Keycloak URL: http://localhost:8080
* Realm: myrealm
* Client: myclient

Saveを押し、Sign inを押します。

するとKeycloakのサインイン画面が表示され、MicrosoftEntraIDが選択できるようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/1164284f-aaa9-28b1-a5b8-6b02f76a2121.png)

MicrosoftEntraID をクリックしてください。

Entra IDにて設定した、test1ユーザーの ユーザープリンシパル名、パスワードを使ってサインインしてください。

成功すると、Update Account Information の画面隣、Username, EmailにAzure Entra Idのtest1ユーザーのメールアドレスが入った状態になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/a48c72cd-96e3-ff44-ba1c-ffa675ccb393.png)

First Name, LastNameに適当な値を入れて、Submitすると、サインインが成功しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/83b204a7-9970-7028-f871-af585c4086ca.png)

KeycloakでUsersを見ると、test1が追加されていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/0023a373-e80a-658c-7576-b4aff24e76ba.png)

これは、EntraIDでサインインしたときにKeycloak側で対応するユーザがいないので作成されたことを示していて、Keycloak > Authentication > first broker login にてCreate User If Unique によって実現されています。

## おわりに

本記事では、KeycloakとMicrosoft Entra IDとをSAMLで連携させ、EntraIDでサインイン時に自動的にKeycloak側にユーザが作成されるところまでを確認しました。

## 2024/1/21 追記 (IdP initiated SSO について)
本記事で解説した記事は、SP initiated SSO、すなわちSP(ここでは https://www.keycloak.org/app/ )起点で行うSSOです。

最終的にうまくいきませんでしたが、IdP initiated SSO、すなわちMicrosoft Entra ID起点で行うSSOの設定についても調べたので解説します。

IdP initiated SSOでは、IdPが提供するアプリ一覧画面から、SSOします。
Entra IDの場合、アプリ一覧画面のURLは https://myapps.microsoft.com/ です。
本記事の通りに設定をしていれば、エンタープライズアプリケーションに追加したアプリケーション(Keycloak)が表示されているはずです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/5c12cc22-9d32-2815-a5aa-3cdbe945e848.png" width=50%>

しかし、実際にクリックすると、エラーになります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/d7ec140b-5de6-7793-2d17-ca37084248de.png" width=50%>

Keycloak側でもエラーを確認できます。
```error
2024-01-21 03:43:33,126 ERROR [org.keycloak.broker.saml.SAMLEndpoint] (executor-thread-60) SAML RelayState parameter was null when it should be returned by the IDP
```
これは、RelayStateというログイン後にどのURLに遷移するか？というパラメータが不足している為で、Entra IDではリレー状態というパラメータで設定します。[Microsoft 参考URL](https://learn.microsoft.com/ja-jp/power-pages/security/authentication/saml2-settings)
試しに、https://www.keycloak.org/app/ に設定してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/13998da0-f8cf-e43f-1e67-fd8bda160b2c.png)

この設定を行ったとしても、アプリ一覧画面にて再度Keycloakアプリをクリックした時、ブラウザにて
```
Cookie not found. Please make sure cookies are enabled in your browser.
```
というエラーになります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508915/72ef5213-9dfc-c91f-0205-4447c65bc841.png" width=70%>

KeycloakのログレベルをWARNにしておくと、以下WARNINGメッセージを確認できます。
```
2024-01-21 03:48:57,618 WARN  [org.keycloak.events] (executor-thread-61) type=IDENTITY_PROVIDER_LOGIN_ERROR, realmId=0338c1aa-1321-4174-94f8-441964d33131, clientId=null, userId=null, ipAddress=192.168.65.1, error=cookie_not_found
```

これはKeycloakの[既存不具合](https://github.com/keycloak/keycloak/issues/20490)のようで、現時点修正される見込みが立ってません。

では、アプリ一覧(https://myapps.microsoft.com/) 起点でのサインインを諦めるしかないか？というとそんなことはありません。

Entra IDの基本的なSAML構成画面で、サインオン URL(省略可能)という設定項目があります。
これをアプリケーションのURL ( 例： https://www.keycloak.org/app/ )に設定することで、アプリ一覧から、サインオンURLに設定したURLに遷移します。これはブックマークから単にURLを開いたのと同じような状態です。
この画面から、SP Initiated SSOではありますがサインインが可能です。

Auth0によるとセキュリティリスクのあるIdP Initiated SSO は非推奨で、SP Initiated SSOを推奨しているのでこれで良いのかも知れません。[リンク](https://auth0.com/docs/authenticate/protocols/saml/saml-sso-integrations/identity-provider-initiated-single-sign-on#risks-and-considerations)

## 参考

Keycloakチュートリアル
https://www.keycloak.org/getting-started/getting-started-docker

Keycloakを用いて外部ID連携を試してみる
https://qiita.com/t_katsumura/items/472591aaccc8da5d54c3

ハーモス勤怠管理 の Azure Active Directory SAML認証設定方法
https://faq.ieyasu.co/kintai/5796/

OIDCを利用した外部ID連携
https://atmarkit.itmedia.co.jp/ait/articles/1804/14/news004.html

SAMLメッセージをデコードして表示できる、便利なサイト
https://samltool.io/