2014年5月7日水曜日

[AAD/ASP.NET] OpenID Connectを使ってAADでログオンする

先日の#idcon vol.18でAzure Active Directory(AAD)のOpenID Connect対応について話をしました。
遅ればせながらフォローアップをしていきたいと思います。

当日の資料はこちらです。



概要としては、OpenID Connectに対応(Preview)したAADに対して、こちらもPreview公開されたOWINのOpenID Connectセキュリティ・ミドルウェアを使ってASP.NETのMVC5のWebアプリケーションで接続(ログイン)してみる、ということをやってみます。
また、その過程でAADのOpenID Connect対応の特異点となっているresponse_modeパラメータの動きについても解説していきます。

少々長くなりそうなので、

  • 今回)素直にOWIN/OpenID ConnectでAADを使ってログオンする方法
  • 次回)一般的なOpenID Connect OPが対応しているresponse_mode="fragment"の場合に必要な工夫

という2回でお伝えしていきます。
また、その後になると思いますが、AAD以外のOpenID Connect OPへOWIN/OpenID Connectを使って接続する例についても解説していければと思います。


■設定方法と基本的な動作
一旦、基本的な設定と動作について確認していきます。中身のプロトコルの解説を細かくする前にまずは動きをつかんでもらうことが目的です。

◇Webアプリケーションの作成(ASP.NET MVC5)
・Visual Studioを起動し、ASP.NET/MVC5のWebアプリケーションを新規に作成します。

・NuGetを使いMicrosoft.Owin.Security.OpenIdConnectをインストールします。
 現状プレビュー版のパッケージなのでリリース前のパッケージを含めて、「openidconnect」で検索すると出てきます。


・SSLを有効にします。
 これはAADに設定するアプリケーションにはHTTPSが必須なため必要な設定です。
 ソリューションエクスプローラでプロジェクトのプロパティを開き、[SSL有効]をTrueに設定します。結果、自動的にSSLのURLが生成されます。


 次に、メニューより[プロジェクト]を選び[プロパティ]を開き、WebメニューからプロジェクトのURLを先ほど割り当てられたSSLのURLを設定し、保存します。



◇AADにアプリケーションの登録
・AADの管理画面へアクセスしアプリケーションの新規作成を行います。
 以下の設定を行います。
 ・実行する作業:組織で開発中のアプリケーションを追加


 ・アプリケーション情報  名前:任意
  種類:WEBアプリケーションやWEB API


 ・アプリケーションのプロパティ  サインオンURL:先ほどVisual Studioで作成したアプリケーションのURL(HTTPS)
  アプリケーションID/URI:任意(テナント内で一意)


 作成が完了したら構成メニューより割り当てられたクライアントIDをコピーしておきます。



◇Webアプリケーションの実装
・コードを書きます。
 ソリューションエクスプローラから、App_Start\Startup.Auth.csを開き、以下を追記します。
 usingディレクティブ
using Microsoft.Owin.Security.OpenIdConnect;

 Startupクラス
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions()
{
   Client_Id = "AADに登録したアプリケーションのクライアントID",
   Authority = "https://login.windows.net/テナント名.onmicrosoft.com",
   Description = new Microsoft.Owin.Security.AuthenticationDescription()
   {
      Caption = "Azure Active Directory",
      AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType
   }
});



 以上で準備は完了です。

◇Webアプリケーションの実行
・F5を押してアプリケーションを実行するとASP.NET MVC5のアプリケーションが起動しますので、右上のログインをクリックします。


・ログイン画面に遷移するので、右側に表示される[OpenIdConnect]をクリックします。


・AADのログイン画面に遷移するので組織のアカウントでログインします。


・ASP.NETアプリケーションにアカウントが存在しないので関連付けを行います。


 ちなみにこのアカウントはAspNetUsersテーブルに格納されますので、サーバエクスプローラから確認・編集ができます。

・ログインが完了し、ユーザ名が表示されます。



以上がとりあえず動くところまで、の手順です。
少ないコード量で実現できることがわかります。



■内部での通信シーケンスの解説
では、内部の動作について確認してみます。
細かく解説すると非常に面倒なフローなので、ざっくり解説すると以下の様になっています。

  1. AADの認可エンドポイントがws-federationのRPになっている
  2. 認証に関してはws-federationのSTSになっているhttps://login.microsoftonline.comで行われる(Federationされている)
  3. 認証が完了するとcodeとid_tokenが払い出される
  4. codeとid_tokenはUserAgent(ブラウザ)を経由し、HTTP POSTでclient(Webアプリケーション)へ渡される


この4番目のcodeやid_tokenのクライアントへの渡し方がOpenID Connectのresponse_modeというパラメータで決まってくるのですが、この流れを見るとAAD/OWINではform_postというタイプを使っていることがわかります。この部分がAADの特徴的な部分となっているので後ほど解説します。

以下が通信シーケンスの全体像です。




■response_modeとは
OpenID Connectの仕様を見ると本パラメータは以下の様に定義されています。

OPTIONAL. Authorization Endpoint からパラメータを返すために Authorization Server が使用する方法を通知する. 要求されるレスポンスモードがレスポンスタイプで指定されるデフォルトモードである場合, このパラメータの使用は推奨されない (NOT RECOMMENDED).
参考)http://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html


OpenID Connectにおいて、id_tokenをクライアント(Webアプリケーション)へ渡す方法は、Authorization Codeフローではcodeと引き換えにresponse bodyで、Implicitフローではフラグメント(URLの後の#以下)でというのが標準的?です。
しかし、先の通信シーケンスをみるとわかるようにAAD/OWINではid_tokenをJavaScriptで自動的にクライアントへPOSTするようなフォームを含むHTMLをUserAgentへ返すことで、id_tokenをクライアントへ渡しています。この方法(response_mode)をform_postと呼びます。
現状、AADにおけるresponse_modeはこのデフォルトのform_postおよびfragment(Implicitで使われる)およびquery(GETパラメータで渡す)の3つがサポートされます。
一般的にサーバサイドでパラメータの横取りをすることが出来ないfragmentを使ってtokenを渡すのが基本なのですがOAuthにおけるaccess_tokenのようにBearer Token(トークンを持っている人なら認可する)ではなくHok Token(トークンを持っている人が本当に持ち主なのかを確認してから認可する)であるOpenID Connectのid_tokenならそこまで気を遣わなくても大丈夫、という考え方なのかも知れません。
また、これは#idconでも話したように推測でしかないのですが、これまでASP.NETがサポートしてきたws-federationやSAML-Pなどのプロトコルと同じ動きをさせることでミドルウェアの実装を簡素化する、というのが真相なのかも知れません。(ws-federationやSAML-PではSAMLトークンをUserAgentを経由してRPへPOSTして渡すのが最近の主流なので)


ただ、現在のOWINの実装では他のOpenID Connect OP(現状あまりresponse_mode自体をサポートしているOPが少なく、かつform_postをサポートしているところはほぼない)との相互接続に問題が起きやすいので、response_mode="fragment"で構成されたOPをASP.NET/OWINで使う場合にどうすれば良いのか、について次回解説したいと思います。


0 件のコメント: