ServiceNowにSAML SSOを設定する

ServiceNowは様々なログイン方法に対応しており、その一つにSAML認証よるSSOがあります。この記事では、ServiceNowにSAML SSOできるようになるまでの環境構築手順を1から紹介します。
（全てDeveloper用の無料枠で再現可能です。）

SAMLそのものの説明はしませんが、各設定項目の説明は公式ドキュメント等を元に自分の理解を所々記載しています。とはいえSAML初心者が勉強しつつ設定をやってみた際の理解ですので、その点はご了承ください。

# 概要
SAMLにおいては、認証を行う側のシステムをIdP (Identity Provider) と呼び、IdPの認証情報を利用する側のシステムをSP (Service Provider) と呼びます。

今回の場合は、ServiceNowがSPということになります。
IdPは、実際には社内で使われているIDaaSなどで連携させることになりますが、ここではIdPとして[Auth0](https://auth0.com/)を使用します。

ServiceNowにおけるSSOのフローは、以下の公式ドキュメントにある概要図でおおよそのイメージを掴めるかと思います。
図の右側のCustomer Portal / SSO Solutionが今回でのAuth0に該当するような形です。
[Typical SAML process flow (diagram)](https://docs.servicenow.com/bundle/rome-platform-administration/page/integrate/single-sign-on/concept/c_TypicalProcessFlowDiagram.html)


### 手順概要
おおまかな構築の流れは以下の通りです。

1. Auth0の設定
    1. ServiceNowをSPとして登録
    1. テスト用ユーザーの作成
1. ServiceNowの設定
    1. Multi-Provider SSO Pluginのインストール
    1. ACR (Account Recovery) の有効化 (**追記**)
    1. Multi-Provider SSOの設定
    1. Auth0をIdPとして登録
    1. テスト用ユーザーの作成
    1. Connection Test & Activate
1. 動作確認

今回ServiceNowは無料のDeveloperインスタンスを、Auth0はFreeプランを使用することとします。それぞれを使用できるまでの手順はここでは省かさせていただきます。


# 1. Auth0の設定
## 1-1. ServiceNowをSPとして登録
左側のメニューからApplications > Applications を開いて、Create Applicationを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/70289f14-41da-eb96-069e-a0718d8d14ba.png)

Nameには任意の名称を入力して、Application Type は `Regular Web Applications`を選択します。
その後、Createを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/4afa8c39-4848-dae7-2964-bbad73f1dfa0.png)

Addonsのタブを開き、SAML2 WEB APPのトグルスイッチを押下します。(ポップアップが開きます。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/3a81717b-3913-930f-646b-af2e31a51216.png)

Usageのタブにて、IdPのMetadataをダウンロードしておきます。ダウンロードしたMetadataは、後のServiceNow側の設定で使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/b5f89086-b05a-e471-44c2-56568549f9ee.png)

続いてSettingsのタブを開きます。
Application Callback URLには以下を入力します。ここの内容は後ほど説明します。
(`[instance-name]`は自身のものに置き換え)

```
https://[instance-name].service-now.com/navpage.do
```

Settingsには以下のJSONを入力します。こちらも、内容は後ほど説明します。
(`[instance-name]`は自身のものに置き換え)

```settings.json
{
  "nameIdentifierFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
  "nameIdentifierProbes": [
    "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
  ],
  "logout": {
    "callback": "https://[instance-name].service-now.com/navpage.do"
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/ae154389-0a3f-31d6-cb88-e0b1db58a63b.png)

入力を終えたら、そのまま一番下までスクロールし、Enableを押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/424671dd-a606-d653-e28c-8eff442c898e.png)

## 1-2. テスト用ユーザーの作成
ユーザーストアはIDaaSには直接持たせずADなどと連携するケースも多いかと思いますが、今回は小規模なテスト環境なので、Auth0のビルトインDBに直接ユーザーを作成します。

User Management > Usersのメニューを開き、Create Userを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/76d4bea8-0da4-a196-1347-e2c8d54c6569.png)

EmailとPasswordを入力し、Createを押下します。
（今回は適当な自前のGmailアドレスで登録しています。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/e2f4d129-59aa-5b77-aa04-04ed1b114d61.png)

その後、設定したメールアドレス宛に、Verificationのメールが届くので、メールに記載されているリンクにアクセスしてVerificationを済ませます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/d8d424b9-f38a-7b94-06cf-f5e53cd726ce.png)

(Optional)
Nameにはデフォルトでメールアドレスがそのままセットされますが、任意の名前に変更しておきます。
なお、下図を見ると分かりますが、Auth0上でのuser_idは自動的に割り振られるようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/f39430f1-6b49-a994-f158-15330873ab8b.png)

# 2. ServiceNowの設定
## 2-1. Multi-Provider SSO Pluginのインストール
ServiceNowに、SSO機能を導入するためのPluginをインストールします。

System Application > All Available Application > Allのメニューを開き、"Integration - Multiple Provider Single Sign-On Installer" をインストールします。
![Screenshot 2021-07-19 at 15-30-43 Applications ServiceNowのコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/1c12973a-b0ef-0c19-f30a-a486dfca8fa4.png)

Activateを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/78e96753-4ed2-1328-c649-acbc844f496a.png)

完了したらClose & Reload Formを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/83d80fd7-a52e-72b5-64bc-763b46e66349.png)

## 2-2. ACR (Account Recovery) の有効化
San Diego以降のバージョンにおいては、SSOを使用するにあたっては、ACR (Account Recovery) の機能を有効化することが必須となっています。
ACRは、例えばIdPの証明書切れなどの問題によってSSOログインが成立しなくなってしまった場合に備え、管理者のみローカル認証によるログイン (SSOバイパス) ができるようにする機能です。
なお、ACRを有効化すると次の変化があります。
* ACR Userとして登録した管理者ユーザー以外は、ローカル認証でのログインは出来なくなります。
* ACR Userがローカル認証でログインをした場合は、特殊なロールが割り当てられた状態でのログインとなり、操作できるのは基本的にSSO周りの設定のみに限定されます。adminロールを持つユーザーであっても、ACR Userとしてログインする場合はSSO周りの設定のみにしか権限が与えられません。

ACRのセットアップを行うには、Navigation BarからMulti-Provider SSO > Account Recovery > Propertiesのメニューを開きます。
下図の画面が現れますので、Step2.の「Click here」の部分を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/87d2711f-22ab-463a-e9c5-9ae13d074d0f.png)

ACR Userのローカル認証はローカルパスワード + MFAですので、MFAの登録を促されます。
[Google Authenticator](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2)などのTOTPアプリを自身のスマートフォンにインストールした上で、画面のナビゲーションに従いデバイス登録をします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/b61dc2f8-dfb8-a18c-d0a6-d1b57e786097.png)

デバイス登録が完了するとEnable account recoveryボタンを押せるようになります。
ボタンを押下してACRの有効化は完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/8351645d-110f-9642-4770-1f1df650450e.png)


## 2-3. Multi-Provider SSOの設定
Navigation BarからMulti-Provider SSO > Administration > Propertiesのメニューを開きます。下図の通り設定し、Saveを押下します。
![Screenshot 2021-10-03 at 16-45-29 Multiple Provider SSO Properties ServiceNow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/52645cc5-0ac8-adf4-3882-d4462c6c13a6.png)


**各項目の詳細**

詳しくは以下の公式ドキュメントが参考になります。
[Configure Multi-Provider SSO properties](https://docs.servicenow.com/bundle/rome-platform-administration/page/integrate/single-sign-on/task/t_ConfigureMultiProviderSSOProps.html)

- Enable multiple provider SSO
  - Yesを選択します。
  - 有効にすることで、ServiceNowのログインページ (`/login.do`) に、Use External Loginのリンクが現れ、ユーザーがSSOログイン用のページへ遷移できるようになります。

- Enable Auto Importing of users from all identity providers into the user table
  - SAML User Provisioningの機能を有効化するか否かの設定です。  
  - SAML認証でSSOするには、対象のユーザーがUser Table `[sys_user]` に存在していることが前提です。ただし、User Provisioning機能を使うと、User Tableに存在しないユーザーがSAMLでの初回ログインを行ったときに、そのユーザーが自動的にUser Tableに登録されます。これによりUser Tableに存在していなかったユーザーも最初からSAMLでのログインが可能になります。  
  - 今回はいったん、事前にUser Tableに存在しているユーザーのみをSAML認証の対象とするため、Noを選択します。
  - SAML User Provisioningについては、別途下記の記事を作成しました。よろしければこちらもご参照ください。
      - [[ServiceNow] SAML SSOしたとき自動でユーザーレコードの作成/更新をする (SAML User Provisioning)](https://qiita.com/tajioka/items/ddda2cd30f58011c460c)

- Enable debug logging for the multiple provider SSO integration
  - debuggingを有効化するか否かの設定です。
  - 今回はNoを選択します。
  - 通常運用時に有効化しているとパフォーマンスやディスク容量に影響を及ぼす可能性もあるので注意が必要です。


- The field on the user table that identifies a user accessing the "User identification" login page
  - SSOログインのページ(`/login_locate_sso.do`)で、ユーザーに自分自身のIDとして入力させる値を設定します。
  - ServiceNowは、ここで入力された値を元にSSOをリクエストしたユーザーが誰なのかUser Tableからレコードを検索して特定します。
  - 設定できるのはUser Tableで定義されているフィールド名です。(ラベル名ではない。)
  - デフォルトは`user_name`ですが、ユーザー側の視点だとメールアドレスのほうが扱いやすいと思うので、`email`に変更します。  
  - ここで設定したFieldは、User Table上で一意の値を持つフィールドであることが保証されている必要があります。(例えば`email`にする場合、同じメールアドレスを持つ異なるユーザーをUser Tableに作成することは可能ではあるが、SSOの観点ではそのようなユーザーが存在しないようにしなければならない。)
     
## 2-4. Auth0をSAML IdPとして登録
Multi-Provider SSO > Identity Providersのメニューを開きます。Newを押下して、新しいIdPの登録を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/9074a03e-591b-605a-1f25-b751e7fed3e1.png)

`SAML`を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/82329f7f-a6a3-22dc-1b3c-37356f612dcf.png)

ポップアップが現れますので、`XML`を選択します。
テキストエリアには先の手順 (1-1) でAuth0から入手したMetadataの中身をコピペししてimportを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/1b4b71b3-a0d9-6a47-329f-843695902265.png)

インポートしたMetadataに沿って各フィールドに値がセットされます。内容を確認したらUpdateを押下して保存します。
なお、IdPのMetadataにはX.509証明書も含まれていますので、X.509 Certificateの関連リストにも自動的にレコードが作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/7c0d1d3c-3674-f966-f97f-ef5cafbf6e36.png)
(補足)
各項目の詳細はここでは省かさせていただきますが、以下の公式ドキュメントに各項目の説明があります。
[SAML 2.0 configuration using Multi-Provider SSO](https://docs.servicenow.com/bundle/rome-platform-administration/page/integrate/single-sign-on/task/t_CreateASAML2Upd1SSOConfigMultiSSO.html)

保存した後、先程の画面でGenerate Metadataを押下するとMetadataを出力できます。
今回はIdP (Auth0) 側の設定は先に手動入力で行いましたが、本来であればこのSPのMetadataを読み込ませて設定をすることが多いかと思います。(Auth0ではSPのMetadataを読み込ませる方法が見当たらなかったため、手動で設定しました。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/ae7e55d0-026d-7a94-8e46-a09c166f5fd3.png)

Metadataから読み取ることのできる値には、例えば下記のものがあります。
| 項目名   |  記載場所  |  上記Metadataにおける値  |
| ---- | ---- | ---- |
|  SP Entity ID  |  `<Entity Descriptor>`タグ内の`EntityID=`に続く値  |  `[instance-name].service-now.com`  |
|  ACS URL  |  `<AssertionConsumerService>`タグ内の`Location=`に続く値  |  `[instance-name].service-now.com/navpage.do`  |
|  SLO URL  |  `<SingleLogoutService>`タグ内の`Location=`に続く値  |  `[instance-name].service-now.com/navpage.do`  |
|  NameID Format  |  `<NameIDFormat>`タグに囲まれた値  |  `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`  |


## Auth0の設定内容の振り返り
Auth0側にSPとしてServiceNowを登録する作業は先に済ませていましたが、ここで改めて内容を確認し、ServiceNow側の設定値とどのように対応させていたのか見てみたいと思います。

#### Application Callback URL

以下の通り設定していました。

```
https://[instance-name].service-now.com/navpage.do
```

- Application Callback URLは、IdPが認証後にSPへAssertion (認証情報やユーザーの属性値などをXML形式で表したもの) を送る宛先のURLです。
- ServiceNowにて出力したMetadataにおける`<AssertionConsumerService>`タグ内の`Location=`に続く値を設定します。

(補足)
IdPによってはApplication Callback URLではなくて、ACS URL、Post-back URLなど別の呼び方かもしれません。
> This may be called Assertion Consumer Service URL, Post-back URL, or Callback URL.

[Manually configure SSO integrations](https://auth0.com/docs/configure/saml-configuration/configure-auth0-saml-identity-provider#obtain-the-application-callback-url-from-the-service-provider)

#### settings

以下の通り設定していました。
settingsの書式やデフォルト値は以下の公式ドキュメントに説明があります。
[Customize SAML Assertions](https://auth0.com/docs/configure/saml-configuration/customize-saml-assertions#saml-assertion-attributes)

```settings.json
{
  "nameIdentifierFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
  "nameIdentifierProbes": [
    "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
  ],
  "logout": {
    "callback": "https://[instance-name].service-now.com/navpage.do"
  }
}
```

- `nameIdentifierFormat`
  - NameID Formatの指定です。
  - ServiceNowにて出力したMetadataにおける`<NameIDFormat>`タグに囲まれた値を設定します。
  - 今回はNameIDに`email`を使用するので、NameID Formatもそれに合わせています。(NameIDについては後述)
  - ちなみに指定しない場合、Auth0はデフォルト値のunspecifiedになります。
  - ServiceNow側はNameID PolicyフィールドにてNameID Formatを変更できます。デフォルトでemailAddressに設定されているため変更しませんでした。
![Screenshot 2021-10-03 at 22-25-19 urn dev-lgtdrcx9 us auth0 com Identity Provider ServiceNowのコピー3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/4918fe49-e562-fd93-5d75-93be594aa368.png)
  - なお、他にどんな種類のNameID Formatがあるかは「[SAML ~ NameID Format をご紹介 ~](https://qiita.com/samiii/items/9b14967764d095cc5120)」が参考になりました。



- `nameIdentifierProbes`
  - ユーザーのどの属性値をNameIDとしてSPへ提示するかの指定です。
  - NameIDとは、ざっくりいうとIdPとSPの間で共有するユーザーの識別子です。
  - Auth0はデフォルトだと`user_id`をNameIDとしてSPに提示します (*1)
  - 今回は`email`をSPに提示するように変更をしました。 
  - ServiceNow側ではAdvancedタブにあるUser Fieldフィールドにて、IdPから提示されたNameIDをUser Table上のどのフィールドで検証するか指定できます。デフォルトで`email`に設定されているため変更しませんでした。
![スクリーンショット 2021-10-23 16.45.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/6fbbb9aa-46c8-4391-024a-44e125dd5013.png)

(*1) より正確には、Auth0は`user_id` → `email` → `name`の順に見ていき、最初に値があったものをNameIDとしてSPに提示するようです。

> Auth0 will try each of the attributes of this array in order. If one of them has a value, it will use that for the Subject/NameID.The order is: `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier` (mapped from user_id), `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress` (mapped from email), `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name` (mapped from name).

[Customize SAML Assertions](https://auth0.com/docs/configure/saml-configuration/customize-saml-assertions#saml-assertion-attributes)

- `logout.callback` (*2)
  - Single Logout (SLO) のRequest, ResponceをSPへ送る宛先のURLです。
  - ServiceNowsにて出力したMetadataにおける`<SingleLogoutService>`タグ内の`Location=`に続く値を設定します。

(*2) SLOの設定は必須でないようですので、不要な場合は`logout`のキーごと消します。なお、その場合はServiceNow側でもIdentity Provider's SingleLogoutRequestフィールドを空欄にします。

## 2-5. テスト用ユーザーの作成
ServiceNowのUser Tableにユーザーを作成します。

前準備として、先ほど登録したIdPの`sys_id`を取得しておきます。
Multi-Provider SSO > Identity Providersのメニューを開き、先ほど登録したIdPのレコードで右クリックをし、copy sys_idで`sys_id`をクリップボードにコピーしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/03178bdc-7424-0b5c-25a7-6813996038d9.png)

User Administoration > User を開き、Newを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/b522ef48-da9e-2a4e-5b1c-a3ef085b45b0.png)

ユーザーを作成します。ポイントは次の2点です。

- EmailはAuth0で作成したユーザーのメールアドレスと一致させる
    - (補足:Auth0はNameIDとしてメールアドレスを提示するよう設定し、ServiceNowは受け取ったNameIDの値をEmailフィールドの値で検証するよう設定したため)
- SSO Sourceには先ほどコピーした`sys_id`を使って、`sso:[sys_id]`と設定する
    - (補足:SSO Sourceのフィールドが見えない場合はForm Layoutを変更して追加)
 
それ以外の項目は、(SAML SSOの観点では) 特に何でも良いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/745fedbf-636f-0bb0-e4de-73a87014d9d6.png)

## 2-6. Connection Test & Activate
これにて準備が整いましたので、実際にSAML連携ができるかServiceNow側からテストします。
(このテストに成功しないとIdPとの連携をActiveにできません。)

Multi-Provider SSO > Identity Providersのメニューにて、先ほど登録したIdPのレコードを開きます。上部のTest Connectionを押下してテストを実施します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/6e8c1a02-4104-f8ee-7734-e4fc3cb2b33d.png)

正しく設定ができていれば、Auth0の認証画面が現れます。
Auth0にて作成したユーザーのメールアドレスとパスワードを入力してLOG INを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/dac1baa6-2497-8a92-6d97-83407450d44f.png)

LOG IN押下後、SSO Login/Logoutのテスト結果が表示されます。全て問題なければ下図の通りとなります。
内容を確認したらActivateを押下して、IdPの連携を有効化します。
(SLOの設定を省略した場合はSSO Logout Test Resultsにバツが表示されますが、Activateすることは可能です。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/f491f260-ef31-934c-5f05-5a0e5b3a1eb8.png)

以上で設定は完了です。

# 3. 動作確認
実際にSAMLでログインが出来るか確かめます。

Private Windowなどで新しいブラウザを開き、ServiceNowのログインページにアクセスします。
Use external loginを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/d0a645cc-c95d-7f2e-639f-e2dfc0afb0c6.png)

メールアドレスを入力してSubmitを押下します。
(ここで入力欄にEmailと表示されているのは、2-2.にてThe field on the user table...を`email`にしたためです。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/4d1dd190-a15e-fb5f-362c-6d3a34cce8b4.png)

Submitを押下すると、入力されたメールアドレスに基づいてUser Tableから該当するユーザーのレコードが特定されます。特定したユーザーのレコードからSSO Sourceの値を読み取って、そのIdPのLogin画面へリダイレクトします。

Auth0の認証画面が現れるはずですので、Auth0にて登録したユーザーのメールアドレスとパスワードを入力しLOG INを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/8c5c07ea-7787-4ab9-22ee-7993a5c494e3.png)

上手く認証が通れば、実際にTEST USERとしてログイン出来ているのが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923007/ffa27b8e-0514-ddf2-834e-88a857bb689c.png)

# おわりに

業務アプリをServiceNowへ移行するお仕事をやっていてServiceNowを触りはじめましたが、そのとき社内のSSOシステムとServiceNowのSAML連携も初めて行いました。本記事はそのときに調べたことをまとめる形で書きました。

慣れていればどのIdP/SPを使っていてもどこに何を設定すれば良いかすぐに分かると思いますが、はじめての場合は苦戦するかもしれません。本記事が少しでもその理解の助けになれば幸いです。


# 参考
[SAML 認証はどのように機能するか？](https://auth0.com/blog/jp-how-saml-authentication-works/)