API ConnectとAzure AD間でSAML連携を実装してみた

#はじめに
この記事では、API呼び出しの際の認証方法として、Azure ADとSAMLプロトコルで連携する方法を紹介します。
API Connectの基本的なセキュリティー機能では、Rest APIでよく使われるClientID/ClientSecretや、Basic認証、OAuth認可等の仕組みを提供していますが、SAML認証は含まれていません。しかし、API ConnectのAPI Gatewayコンポーネントとして同梱されているDataPowerではSAMLプロトコルもサポートしますので、今回は、DataPowerのカスタム開発する方法を紹介します。


# 実装概要

実装は以下のようなイメージとなります。
Azure ADとのSAML連携を実施するためのマルチ・プロトコル・ゲートウェイの処理ルールとして設定します。
マルチ・プロトコル・ゲートウェイは、新規にカスタムドメインを作成して設定するか、API Connect用のドメインであるapiconnectドメインに設定することもできます。今回は、簡易的な手順とするため後者で設定しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/8cc42c82-fa87-29d6-fad0-b99b92b823e7.png)

フローは、以下のような流れとなります。

①. ClientからService Provider(SP)であるAPI ConnectへのAPIリクエスト

①. Identity Provider(IdP) であるAzure ADへリダイレクトし、エンドユーザー認証の要求

③. エンドユーザーが認証されると、IdPからSPへSAML Assertionを応答

④. SPからClient（エンドユーザー）へAPI応答（Assertionを応答することが一般的ですがこの例では、APIコールまで進めています）

この記事では、上記のAPI ConnectとAzureAD間の初回認証連携のみを検証しているため、以下の処理は含まれていません。

-  ①のAPIリクエストでSAML Assertionが含まれているかどうかのチェック（含まれていたらリダイレクトはせずAssertion検証へ）
-  ①のAPIリクエストでSAML Assertionが問題ないかの検証（検証が問題なければAPIコールへ）
-  ②の/redirectエンドポイントに対する要求元の制限するフィルター設定


# 実装手順

## 1. Azure AD設定

### 1.1 Azure Portalアクセス


Microsoft Azureの[Portalサイト](https://portal.azure.com/#home)にアクセスします。
Azure Active Directoryサービスにアクセスします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/479ec424-1672-dac7-2155-cf374ed09af4.png)

以下のようなテナントの管理メニューが表示されます。
テナント登録がまだの場合は、登録から実施する必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/0b36b042-461e-f136-43b2-d53bc91c4c51.png)

また、ドメイン登録がまだの場合は、ドメイン作成から実施する必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/693b56cd-6304-6bb4-258b-ec1bfdc226da.png)



### 1.2 アプリケーション登録

API Connectとの連携に使用するアプリケーションを登録します。
以下は一例ですので、お使いの環境に合わせて設定してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/18f7e5d1-0f28-0d38-2393-1249f1ed5ad6.png)

独自のアプリケーションを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/3eed6a08-c09f-875b-d99d-965fc332c1e7.png)


アプリケーションが作成されたら、管理メニューのシングル サインオンからSAMLを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/387f0a82-7590-eb6a-b501-e0133e6abfc6.png)

お使いの環境に合わせてSAMLのセットアップを実施します。
基本的なSAML構成で識別子（エンティティーID）と、応答URLが必須のパラメーターとなります。
この例では、応答URLにAPI ConnectのSAML Assertion受け取りURL（/redirect）を設定しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/0786af52-1ae1-c3ce-a18f-619c17224aac.png)


## 2. DataPowerカスタム設定

DataPowerのWeb UIにアクセスします。
今回は、apiconnectドメインにマルチ・プロトコル・ゲートウェイを構成します。


### 2.1 マルチ・プロトコル・ゲートウェイ設定

マルチ・プロトコル・ゲートウェイの稼働に必要となる各種設定を実施します。
フロント・サイド・プロトコルとして、apiconnectのHTTPSサービスと異なるポートを利用してハンドラーを設定します。
また、バックエンドは、リクエスト内容に応じて変わるため、Typeとして、dynamic-backendsを指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/d4f1cf7b-b16f-380b-f255-234d1b60504f.png)

### 2.2 処理ポリシー設定

処理ポリシーとして、最初の概要図で示したフロー処理を実装する処理ルール・アクションを設定していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/116ea03d-e4ed-cccb-1048-0d3be78146c8.png)

#### 2.2.1 redirect処理

Azure ADからのSAML Assertionを受け取る処理ルールを設定します。
処理ルールのマッチングルールとしては、要求パスに「/redirect」が含まれる場合としています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/2acb5ec0-3a82-c9aa-00ef-dd017fe1dfbd.png)

そのあとのGatewayScriptアクションとして、以下のようなコードを設定します。
AzureADからの応答後にそのままバックエンドのAPIを呼び出す処理になります。

```
var hm = require('header-metadata');
var service = require('service-metadata');

var ContentType = hm.original.get('Content-Type');
service.setVar('var://service/routing-url', 'https://x.x.x.x/v1/test');
```

#### 2.2.2 service処理


Clientからの要求を受け取る処理ルールを設定します。
今回は、マッチングルールとして、簡易的にすべてのパスとしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/e331a60c-0808-a0fe-27ae-fcb7159be676.png)

そのあとのGatewayScriptアクションとして、以下のようなコードを設定します。
バックエンドリクエストをスキップし、Azure ADにリダイレクトさせる処理となります。

```
var sm = require('service-metadata');
var hm = require('header-metadata');

sm.setVar('var://service/mpgw/skip-backside', true);

hm.response.statusCode = '302 Moved Temporarily';
hm.response.set('Location', 'https://login.microsoftonline.com/ef10fbb5-****-****-****-*****/saml2?SAMLRequest=jZFbS8QwEIXf91eUvG8v6T20CwsiFBRExQffZtOsG2iSmpmK%2B%2B%2BtVdEiiPM4zJxzvplN8FkNghlGsZ%2FoZG%2FV86SQglczWGzZ5K1wgBqFBaNQkBR3%2B%2BsrwcNYGEXQAwHbBD%2Bqu2iZ7tO8gKTmh7JO0kOcyTzLy6xPKsmrPitUkdZ5xis4sOBBedTOtmyWZEGHN4CoX1TLjjCgYsGl81It0b5aazvESXUWCSzNGjFPtnGx5el9XIq8FLwO87J4XO8sbGKB%2Fptw9I6cdAPbrfabxdT%2F50YzjPI087HdiWgUUTSfbBwHLeG969WTRvLn0FmjpXfojhRKZyI5ITkzTyK6aPTagD830Yfvd5gm%2Bv253Rs%3D');

```

Azure ADのリダイレクト先のURLには、SAMLRequestというクエリーパラメーターに、最初に設定したAzure ADのアプリに基づいた認証情報が必要になります。
今回は、[こちらの記事](https://qiita.com/shinsaka/items/0b0d126a59021c11da59)を参考に事前に生成したものを設定しています。詳細は、Azure ADのサポートページをご参照ください。

これで設定は完了となります。

# 実施例

フロー動作を確認していきます。

① SPリクエスト

ブラウザーからAPI Connectのマルチ・プロトコル・ゲートウェイのエンドポイントにアクセスします。
serviceの処理ルールに入り、Azure ADにリダイレクトされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/f24cb55a-d0dc-5d62-3ccc-b5d7569691d6.png)

② IdPへリダイレクト、③ IdPからのAssetion応答

アカウントを選択して認証処理を進めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/79ec2d2d-eb4a-f8a8-fb8b-4ec87f8e6634.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/1b5f516b-206a-16b3-d32f-6618653a3077.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/fc0b6f44-2707-efde-6314-662b52aca62f.png)

④ APIリスポンス

redirect処理リールに戻り、バックエンドのAPIが呼び出されその結果が応答されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/a91d612c-95b2-5f2c-4a24-2637e206eb7a.png)


以上になります。