Check! Node.js で Azure AD を！ ～ passport-azure-ad の導入とトラブルシューティング

こんばんは、[cloudpack][] の @dz_ こと[大平かづみ][]です。

Prologue - はじめに
====
念願だった [Bluemix](https://console.ng.bluemix.net "IBM Bluemix - 次世代のクラウド・アプリ開発プラットフォーム") で [Azure AD](http://azure.microsoft.com/ja-jp/services/active-directory/ "Active Directory - アクセス &amp; ID - IDaaS | Microsoft Azure") によるシングルサインインを実現する方法を見つけました！青いクラウドをつなぐコラボ！ヽ(≧∀≦)ﾉ

Bluemix シングルサインインサービス実装時に使った [passport](http://passportjs.org/ "Passport") モジュールの Strategy として Azure AD サインインを実装したものがあるとの情報を得ました。

それは [passport-azure-ad](https://github.com/AzureAD/passport-azure-ad "AzureAD/passport-azure-ad · GitHub") !

わーい！！

と言いながらも、結構つまづいたので、皆さまが少しでも安全に実装できるように、微力ながらメモを残させていただきます。m(_ _)m

※と言いましても、本記事は特に Bluemix に限ったものはなく、一般的な Node.js アプリケーションでの実装です。ご了承くださいませ。

実装の準備
====

ドキュメント
----
基本的には以下のドキュメントを参照して進めました。

### サンプル
Node.js で `passport-azure-ad` を利用するサンプルコードです。今回はこちらを参考に進めました。

- [Azure-Samples/AzureAD-WebApp-OpenIdConnect-nodejs](https://github.com/Azure-Samples/AzureAD-WebApp-OpenIdConnect-nodejs "Azure-Samples/AzureAD-WebApp-OpenIdConnect-nodejs · GitHub")

### 参考ドキュメント
上記サンプルを用いた解説の和訳版です。

- [Azure AD を使用した Web アプリのサインインおよびサインアウト](https://azure.microsoft.com/ja-jp/documentation/articles/active-directory-devquickstarts-openidconnect-nodejs/ "node.js を使用する Azure AD へのサインインおよびサインアウトの概要")

ベース環境
----

- Azure Active Directory
- Bluemix
- Node.js
    - Express 3.x
    - passport
    - passport-azure-ad

※上記以外にも適宜利用しています。

実装のポイント
====
私の奮闘記のすべてを書いてると長いので、私が迷ったポイントに絞って記載します。

ディレクトリ(テナント)の作成
----
ディレクトリ(テナント)を作成します。Azure管理ポータルで、「新規作成」>「ACTIVE DIRECTORY」>「ディレクトリ」>「カスタム作成」をクリックしてください。
![20150923_azuread_007.png](https://qiita-image-store.s3.amazonaws.com/0/3812/4835006e-df47-a1ad-17be-136130bcdadf.png)

「名前」「ドメイン名」「国/リージョン」を設定します。このとき「ドメイン名」は後の設定で使いますので覚えておいてください。
![20150923_azuread_008.png](https://qiita-image-store.s3.amazonaws.com/0/3812/be789222-2a7b-d4b2-ca3b-407d7e491ff9.png)


アプリケーションの追加
----
アプリケーションの追加をします。作成したディレクトリのアプリケーション一覧を表示し、画面下部の「追加」をクリックしてください。
![20150923_azuread_003.png](https://qiita-image-store.s3.amazonaws.com/0/3812/54336c55-006a-9bb7-edde-8e165b4992cd.png)

「組織で開発中のアプリケーションを追加」をクリックしてください。
![20150923_azuread_004.png](https://qiita-image-store.s3.amazonaws.com/0/3812/a73f618e-ac69-b40c-3fef-e8613f230f22.png)

「名前」を入力し、「種類」は「WEBアプリケーションやWEB API」を選択して次へ進みます。
![20150923_azuread_005.png](https://qiita-image-store.s3.amazonaws.com/0/3812/b6bab899-604d-e061-389b-07dfe5536d57.png)

「サインオンURL」には、サインイン後に戻るURL (上記サンプルを使用した場合は `<アプリケーションのURL>/auth/openid/return` ) を設定します。「アプリケーションID/URI」には、ディレクトリ(テナント)を作成した際に指定した「ドメイン名」をもとに、 `https://<テナント名>.onmicrosoft.com/<任意の文字列(アプリ名など)>` と設定してください。
![20150923_azuread_006.png](https://qiita-image-store.s3.amazonaws.com/0/3812/e9082d01-1035-84da-dea9-202d711c5b55.png)

### マルチテナントの有効化
上記ドキュメントに記載されていないのですが、これをしないとサインインループ(後述)になったので、設定しておくと吉です。

Azure管理ポータルで、 Azure AD の「アプリケーション」の「構成」にて設定します。マルチテナントを有効にするには、「アプリケーションID/URI」が正しく設定されている必要があります。(上記参照)
![20150923_azuread_002.png](https://qiita-image-store.s3.amazonaws.com/0/3812/ff68c1c8-0be9-162d-4073-b5ee6c19f161.png)

クライアントIDとクライアントシークレット
----
Azure管理ポータルで、 Azure AD の「アプリケーション」の「構成」にて確認、設定できます。
![20150923_azuread_001.png](https://qiita-image-store.s3.amazonaws.com/0/3812/cfbf956d-e8a8-093d-9bcd-7bb959d914c6.png)


トラブルシューティング
====
コールバックURLに戻ってこない
----
Microsoftの認証画面でサインインのリンクを押すも、またその画面に戻ってしまい、一向に自分のアプリに戻ってこない場合は、「 **マルチテナントを有効** 」に設定してみてください。設定方法は上記をご参考ください。

アクセストークンの取得に失敗する
----
> InternalOAuthError: failed to obtain access token

このようなエラーになるとき、一つの原因は **Microsoftアカウントでログインしようとている** 場合です。

この場合、実は内部で以下のエラーによりトークンが取得できていません。

> AADSTS90019: No tenant-identifying information found in either the request or implied by any provided credentials

Stackoverflow にも [この問題](http://stackoverflow.com/questions/31132413/aadsts90019-no-tenant-identifying-information-found-in-either-the-request-or-im "azure active directory - AADSTS90019: No tenant-identifying information found in either the request or implied by any provided credentials - Stack Overflow") があがっていました。この回答を参考に説明します。

`config.js` の `identityMetadata` で設定しているエンドポイントは、"共通" のものであり、Azure AD に登録したユーザーアカウントが「組織アカウント」か「Azure AD アカウント」である場合に利用できます。

```js:config.js
...
identityMetadata: 'https://login.microsoftonline.com/common/.well-known/openid-configuration',
...
```

ただし、Azure AD に登録したユーザーアカウントが「Microsoft アカウント」の場合はこの共通のエンドポイントは使えませんので、アプリケーションのエンドポイントをしっかり設定しなければなりません。

アプリケーションのエンドポイントは、管理ポータルの Azure AD の「アプリケーション」で、画面下部の「エンドポイント」から表示できます。
![20150923_azuread_009.png](https://qiita-image-store.s3.amazonaws.com/0/3812/c5890fe1-369e-1d6c-e46b-da481b5b9ab3.png)

ここには `identityMetadata` に設定できるURLは記載されていないので、以下のようにURLを作って設定してください。

まず、アプリケーションのエンドポイントから任意のエンドポイント、ここでは「OAUTH 2.0 承認エンドポイント」をコピーします。

`https://login.microsoftonline.com/xxx-xxx-xxx-xxx-xxx/oauth2/authorize`

これの `xxx-xxx-xxx-xxx-xxx` の部分を、`identityMetadata` のURLの `common` の部分と置き換えてください。

```js:config.js
...
// identityMetadata: 'https://login.microsoftonline.com/common/.well-known/openid-configuration',
identityMetadata: 'https://login.microsoftonline.com/xxx-xxx-xxx-xxx-xxx/.well-known/openid-configuration',
...
```

これで、Microsoftアカウントを利用したログイン時のアクセストークンが取得できないのエラーは解消すると思われます。

どのURLでもトップページに戻ってしまう
----
どのURLに遷移しようとしても、トップページ(認証成功後のページ)に戻ってしまうが、ログインはされていない状態の場合、 **セッションが効いていない** ことが考えられます。

その Node.js のアプリケーションは、 `https` でのアクセスでしょうか？

それであれば、セッションに `cookie: {httpOnly:false}` とセッションクッキーの設定をすることで、https でのアクセスでもセッションクッキーを利用できます。

```js:app.js(Express3.xの例)
...
// app.use(express.session({secret: 'keyboard cat', resave: true, saveUninitialized: false}));

// session に cookie: {httpOnly: false} を追加する
app.use(express.session({secret: 'keyboard cat', resave: true, saveUninitialized: false, cookie: {httpOnly: false}}));
...
```

セッションの使い方は [expressjs/session](https://github.com/expressjs/session "expressjs/session · GitHub") をご参照ください。なお、Express のセッションは、 3.x と 4.x で変更があるので、別途ご確認ください。

Epilogue - おわりに
====
大きな課題を一つクリアすることができました！

途中、エラーで困った時に @kazumihirose さんにアドバイスいただきました。ありがとうございます！

シルバーウィーク最終日の今は 3:00 。起きたらお仕事はPHPです。がんばろー！

ちなみに、大変恐縮ですが、この内容をもとに [Comm Tech Festival - こみゅぷらす (COMU+)](https://comuplus.doorkeeper.jp/events/30403 "Comm Tech Festival - こみゅぷらす (COMU+) | Doorkeeper") でお話しさせていただく予定です。まだ未熟者ですが、よろしくお願いいたします！

近況
====

最近
----
- [第4回 ICTトラブルシューティングコンテストの熱い戦いを拝見させていただきました！](http://qiita.com/dz_/items/979dc8e45e8bf5b66122 "第4回 ICTトラブルシューティングコンテストの熱い戦いを拝見させていただきました！")
- [Check! cmder ～ Windows に Monokai スタイルのコンソールを導入 (実行ポリシーのトラブルシューティング)](http://qiita.com/dz_/items/68de2b07aab9ef4aab68 "Check! cmder ～ Windows に Monokai スタイルのコンソールを導入 (実行ポリシーのトラブルシューティング)") ← :new: :sparkles:
- [Check! Node.js で Azure AD を！ ～ passport-azure-ad の導入とトラブルシューティング](http://qiita.com/dz_/items/df31be6639fbe3b51df7/ "Check! Node.js で Azure AD を！ ～ passport-azure-ad の導入とトラブルシューティング") ← :new: :sparkles:

Bluemix 関連
----
- [Check! Bluemix で Node.js をスピーディーに開発したい！](http://qiita.com/dz_/items/50949f47ad8e09e491a3/ "Check! Bluemix で Node.js をスピーディーに開発したい！")
- [Check! DBaaS ってなんだろう? IBM Cloudant を Bluemix で使ってみる。](http://qiita.com/dz_/items/ee02ec8144784cface95/ "Check! DBaaS ってなんだろう? IBM Cloudant を Bluemix で触ってみる。")
- [Check! Bluemix でユーザー定義の環境変数を利用する (Node.jsによる利用例)](http://qiita.com/dz_/items/ae282228e68bd50cfc42/ "Check! Bluemix でユーザー定義の環境変数を利用する (Node.jsによる利用例)") ← :new: :sparkles:

Webフレームワーク関連
----
### PHP
#### Phalcon PHP Framework
- [爆速フレームワーク!! Phalcon PHP Framework](
http://qiita.com/dz_/items/55ae18f7fe3a873bace1 "爆速フレームワーク!! Phalcon PHP Framework")
- [Meet Volt ! Phalcon 謹製テンプレートエンジン Volt - from 第71回 PHP勉強会](http://qiita.com/dz_/items/974a90b32ba6a6e38182 "Meet Volt ! Phalcon 謹製テンプレートエンジン Volt - from 第71回 PHP勉強会")
- [Meet Phalcon! - Phalcon PHPフレームワークを Nginx on Amazon EC2 にインストール！](http://qiita.com/dz_/items/e70d61487224b854b971 "Meet Phalcon! - Phalcon PHPフレームワークを Nginx on Amazon EC2 にインストール！")

#### CakePHP
- [Check! はじめての CakePHP2 ポイントまとめ 〜 Blog Tutorial を動かそう](http://qiita.com/dz_/items/2dd4c68f43484f0f78da "Check! はじめての CakePHP2 ポイントまとめ 〜 Blog Tutorial を動かそう")
- [Check! はじめての CakePHP2 〜 bake でプロジェクトスケルトンを利用する](http://qiita.com/dz_/items/3fa2a7a184de645f71d4/ "Check! はじめての CakePHP2 〜 bake でプロジェクトスケルトンを利用する")

### その他
#### Node.js - Express
- [Check! Node.js Express でウェブ開発 with Visual Studio Code](http://qiita.com/dz_/items/f24b66f9f464b054dd1b/ "Check! Node.js Express でウェブ開発 with Visual Studio Code")

クラウド・インフラ関連
----
### Amazon Web Services
- [Check! はじめての fluentd を Amazon Linux で動かしてみる (ログを PHP から送る)](http://qiita.com/dz_/items/960319ffffa0d7355f8c "Check! はじめての fluentd を Amazon Linux で動かしてみる (ログを PHP から送る)")
- [Check! はじめての Fluentd 〜 IAMロールで Amazon EC2 と S3 間をセキュアに](http://qiita.com/dz_/items/224607c84112ccafa2d6 "Check! はじめての Fluentd 〜 IAMロールで Amazon EC2 と S3 間をセキュアに")
- [Check! はじめての Zabbix インストール on Debian x Amazon RDS](http://qiita.com/dz_/items/3e85dd369c8053ceffe8 "Check! はじめての Zabbix インストール on Debian x Amazon RDS")

### Azure
- [Check! chocolatey で Azure に Windows 作業環境を手早くつくる！](http://qiita.com/dz_/items/6630bca65f312284e70f "Check! chocolatey で Azure に Windows 作業環境を手早くつくる！")
- [Check! Azure オートメーション ランブックで OMS Search API を利用する (和訳)](http://qiita.com/dz_/items/7de3c282adef71aae7c6/ "Check! Azure オートメーション ランブックで OMS Search API を利用する (和訳)")


技術ブログ寄稿
----

[cloudpack技術ブログ][cloudpack-blog]でも記事を書いています。

- <a href="http://blog.cloudpack.jp/author/oohira/" title="大平 かづみ の記事一覧 | cloudpack技術情報サイト"　target="_blank" rel="nofollow">大平 かづみ の記事一覧</a>

[cloudpack]:https://cloudpack.jp/ "cloudpack（クラウドパック） 400社を超える導入実績! EC2などAWSクラウドの導入設計、構築、運用保守サービス"
[cloudpack-blog]:http://blog.cloudpack.jp/ "cloudpack技術情報サイト"
[大平かづみ]:http://blog.cloudpack.jp/author/oohira/ "大平かづみの記事一覧"