2015年8月3日月曜日

[Azure AD/ASP.NET]ユーザ属性を取得する2つの方法


こんにちは、富士榮です。

7/29のWindows10のリリースで盛り上がっていますが、その直前にひそかにリリースされていたVisual Studio 2015ではASP.NETのアイデンティティ関連機能が色々と拡張されています。

概要はVisual Studioの公式blogでアナウンスされているので、そこはそちらに任せておくとして、今回はASP.NET MVC5のプロジェクト・テンプレートが拡張されてGraph APIを使ってAzure ADのユーザ属性の取得がされるように変わっているのでその辺りの解説をしたいと思います。また、以前のバージョンから仕えましたが、SSO時のJWT(JSON Web Token)からClaim(属性)を取り出す方法について解説します。

 参考)Visual Studioの公式blog
  Identity Management Features in Visual Studio 2015
  http://blogs.msdn.com/b/visualstudio/archive/2015/07/22/identity-management-features-in-visual-studio-2015.aspx


◆ASP.NETでAzure ADからユーザ情報を取得する方法

先にも書きましたが、ASP.NETでAzure ADと連携するアプリケーションを開発する場合、以下の2つの方法で認証済みユーザの属性を取得することができます。

1.認証時にAzure ADで発行されるJWTに含まれるClaim(属性)を取得する
2.認証後、Graph APIを使ってディレクトリ(Azure AD)上のユーザ属性を取得する


早速それぞれの方法について解説します。


◆JWTに含まれるClaimを取得する

ASP.NETアプリケーションのプロジェクトを作成する際、認証の構成を設定できます。



ここでAzure ADのテナント情報を入れるとアプリケーション起動時にAzure ADを使って認証が行われるようになります。
その際、内部的な動作としてはOpenID Connectを使ったID連携が行われ、Azure ADからアプリケーションにJWTがPOSTされてきます。

POSTされるid_tokenの中身をJWT Decoderでデコードしてみると、以下のようなClaimが含まれていることがわかります。

{
  "aud": "0897xxxxxxxxx65c",
  "iss": "https://sts.windows.net/2c6xxxxxxxxxxxx1bfff/",
  "iat": 1438609784,
  "nbf": 1438609784,
  "exp": 1438613684,
  "ver": "1.0",
  "tid": "2c62f1axxxxxxxxxxxbb1bfff",
  "oid": "457ca53xxxxxxxxxe363dc73c",
  "upn": "nfujie@xxxxxxxxx.onmicrosoft.com",
  "sub": "ILvM1kZdv7kxxxxxxxxiPrw2fPusCk",
  "given_name": "Naohiro",
  "family_name": "Fujie",
  "name": "Naohiro Fujie",
  "amr": [
    "pwd"
  ],
  "unique_name": "nfujie@xxxxxxxx.onmicrosoft.com",
  "nonce": "63574206878.....snip....LTg0YmMtOGFkYmMyZTcwY2Yz",
  "c_hash": "QIsD2...snip...fYgA"
}


これを見るとユーザのUPN(userPrincipalName)や姓(surName)、名(giveName)などもid_tokenの中に含まれていることがわかります。
これらの属性をアプリケーションから取得することが出来れば、アプリケーション上でログインIDだけでなく、ユーザ表示名を表示してあげることが出来るようになるはずです。

では、早速取得してみます。
まず、Azure ADを使った認証がプロジェクト・テンプレート上でどのように実装されているのかを見てみます。
ソリューション・エクスプローラからApp_Startフォルダの中のStartup.Auth.csを開くと、Microsoft.Own.Security.OpenIdConnectを使ってAzure ADとのOpenID ConnectでのID連携を実装していることがわかります。


と、いうことは認証後、id_tokenに関する情報はOwinContextに入れられるはずなので、アプリケーションからはOwinContextの中身を検索することでClaim情報が取得できるはずです。

RequestのGetOwinContext()メソッド使ってContextを取得し、その中の認証およびユーザに関する情報をAuthenticationプロパティ/Authentication.Userプロパティから取得します。
あとは、取得したUserオブジェクトのClaimsコレクションからClaimを順番に取り出せばid_tokenの中身が取得できます。

具体的には以下のようなコードを書きます。

var ctx = Request.GetOwinContext();
var user = ctx.Authentication.User;
var response = ctx.Response;
response.ContentType = "text/html";
if (user.Identity.IsAuthenticated){
   foreach(var claim in user.Claims){
      response.Write(claim.Type + "=" + claim.Value + "<BR>");
   }
}




◆Graph APIを使ってAzure ADから属性を取得する

次はGraph APIを使って認証済みユーザの属性をAzure ADへ問い合わせ、取得する方法です。
プロジェクトの認証構成を行う際に、ディレクトリ情報の読み取り許可を与えることでGraph APIを使ってユーザ情報を取得できるようにプロジェクトが構成されます。


アプリケーションを起動し、Azure ADで認証後、画面右上に表示されるログインIDをクリックするとUserProfileが表示されます。



この部分の実装を見るとGraph APIを使ってユーザ属性を取得しています。
対象のソースはControllers以下のUserProfileControllers.csです。GraphClientが使用されていることがわかります。



実際はこのControllerから呼び出されるViewにuserオブジェクトを渡しているので、表示項目の見せ方については対象のViewであるViews/UserProfile/Index.cshtmlを見てみます。ここでは@Model.[属性名]という形で属性を取得してテーブルに埋め込んでいますので、好きな属性を指定してテーブルを拡張していくことで任意の属性を表示させることが可能です。(今回はJobTitle属性を追加しています)




どちらの方法が適しているのかは、どんな属性が必要なのかをベースに考えれば良いと思います。例えば役職や組織情報を使ってアクセス権制御を行いたければGraph APIを使う方法になると思いますし、単に名前を表示させるだけならJWTから取得する方が通信量が少ないので実装としては軽くなります。
※現状のAzure ADではid_tokenに含めるClaimのカスタマイズができないので、id_tokenにない属性を取得したければGraph APIを使うしか方法がありません。


いずれにしてもAzure ADを使った認証や属性取得がかなり楽に実装できるようになっていますので、あまりカスタムで頑張った実装をするよりもテンプレートにある仕組みを使ってアプリケーション開発をしていった方が今後はよさそうですね。

0 件のコメント: