ラベル Visual Studio の投稿を表示しています。 すべての投稿を表示
ラベル Visual Studio の投稿を表示しています。 すべての投稿を表示

2015年8月14日金曜日

[Azure AD]App Model v2.0を利用して組織内外共用のアプリケーションを開発する

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

近年は企業の働き方の変革が進み、業務アプリケーションについても企業組織内だけでなくグループ企業間で共用したり、サプライチェーンの中の関連企業との間で同じアプリケーションを共同利用するようなケースが増えてきています。

そうなってくると、Azure Active Directory(Azure AD)を使って認証するようなアプリケーションについても単一テナントに紐づけるのではなく、他のAzure ADテナントやマイクロソフトアカウントを使ってログインできるようにしたくなるところです。

これまでもマルチテナントアプリケーションとしてAzure AD上にアプリケーションを登録することは可能でしたが、Azureの管理ポータルのようにマイクロソフトアカウントを使ったログインまではできませんでした。今回Public Previewとして登場したApp Model v2.0ではその部分を補完してくれています。尚、中身はOAuth2.0およびOpenID Connectです。

 Active Directory Teamのブログでのアナウンス
  Now in public preview: The Converged Microsoft Account and Azure Active Directory Programming Model
   http://blogs.technet.com/b/ad/archive/2015/08/12/azure-ad-microsoft-account-preview-sign-in-personal-and-work-accounts-using-a-single-stack.aspx

 開発者向けドキュメント
  App Model v2.0 Preview: Auth Protocols - OAuth 2.0 & OpenID Connect
   https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-protocols/


早速、どのように動くのか確認してみます。

今回は、Azure ADの組織アカウントでも個人のマイクロソフトアカウントでもログインでき、個人のユーザ情報を表示するアプリケーションを作ってみます。尚、アプリケーションはAzure上にホストするWebアプリをVisual Studio OnlineでPHPを使って書いてみます。
ブラウザだけで何でもできるので非常に簡単です。


必要な作業は以下の通りです。
1. Azure App ServiceよりWebアプリを作成する
2. クライアントをアプリケーション登録ポータルから登録する
3. Visual Studio Onlineでアプリケーションを書く


では、準備を進めます。

1. Azure App ServiceよりWebアプリを作成する

この作業の最大の目的はURLを確定させることです。
ここで取得したURLをredirect_uriとしてアプリケーション登録するためです。

中身はなんでもいいので、とりあえずAzureの管理ポータルから簡易作成します。


URLのうちホスト名は[任意の名前].azurewebsites.netとなるので、他と重複しない名前を付けてください。


2. クライアントをアプリケーション登録ポータルから登録する

次にアプリケーションを登録します。
以下よりアプリケーション登録ポータルにログインし、アプリケーションを登録します。尚このポータルへはAzure ADの組織アカウントもしくはマイクロソフトアカウントでログインします。

 https://apps.dev.microsoft.com/

ログインすると右上に[アプリの追加]というボタンがあるので、ここをクリックして登録を開始します。


アプリケーションID(client_id)は自動的に割り当てられるので、まずは任意のアプリケーション名をつけます。



次にプラットフォームの項目より[プラットフォームの追加]をクリックしてアプリケーションのプラットフォームを選択します。今回はWebアプリケーションを選択します。



次にリダイレクトURIの設定を行うので、先に登録したアプリケーションのURLを使います。今回はこの後作成するPHPページをindex.phpとしたので、ここでは先ほどのホスト名+index.phpをリダイレクトURIとして登録しています。



次はアプリケーションシークレットを生成します。これがclient_secretになります。
アプリケーションシークレットの項目の[新しいパスワードを生成]をクリックし、生成されるclient_secretをメモしておきます。




最後にページ下部にある、[保存]をクリックしてアプリケーション登録は完了です。



3. Visual Studio Onlineでアプリケーションを書く

では、実際に動くアプリケーションを書いてみます。
先ほど書いたように今回はVisual Studio Onlineを使ってPHPアプリケーションを書きます。

まずは、先ほど作成したアプリケーションをAzure管理ポータル(今回は新ポータルを使っています)から開き[ツール]-[拡張機能]-[Visual Studio Online]の順に開きます。
※あらかじめ拡張機能としてVisual Studio Onlineを追加しています。


するとエディタが開くので右クリックで新規にindex.phpという名前でファイルを作成し、コードを書いていきます。


実際の使ったコードは以下の通りです(かなり手抜きですが・・・)。単純なcodeフローですね。ちなみにcodeからid_tokenを取得するところでcurlのエラー60番(SSLのVerificationエラー)が出るので、CURLOPT_SSL_VERIFYPEERをfalseにセットしています。マイクロソフト内部の通信のはずなんですが・・・。
<?php

// パラメータ類
$authorization_endpoint = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize';
$token_endpoint = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
$client_id = '1ae7xxxxxxxxxxxx2b99';
$client_secret = 'ErxxxxxxxxxxxxxxW0jp';
$redirect_uri = 'https://pharaohphp.azurewebsites.net/index.php';
$response_type = 'code';
$state =  'hogehoge'; // 手抜き
$nonce = 'fogafoga'; // 手抜き

// codeの取得(codeがパラメータについてなければ初回アクセスとしてみなしています。手抜きです)
$req_code = $_GET['code'];
if(!$req_code){
    // 初回アクセスなのでログインプロセス開始
    // GETパラメータ関係
    $query = http_build_query(array(
        'client_id'=>$client_id,
        'response_type'=>$response_type,
        'redirect_uri'=> $redirect_uri,
        'scope'=>'openid',
        'state'=>$state,
        'nonce'=>$nonce
    ));
    // リクエスト
    header('Location: ' . $authorization_endpoint . '?' . $query );
    exit();
}

// POSTデータの作成
$postdata = array(
    'grant_type'=>'authorization_code',
    'client_id'=>$client_id,
    'code'=>$req_code,
    'client_secret'=>$client_secret,
    'redirect_uri'=>$redirect_uri
);

// TokenエンドポイントへPOST
$ch = curl_init($token_endpoint);
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt( $ch, CURLOPT_POSTFIELDS, http_build_query($postdata));
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
$response = json_decode(curl_exec($ch));
curl_close($ch);

// id_tokenの取り出しとdecode
$id_token = explode('.', $response->id_token);
$payload = base64_decode(str_pad(strtr($id_token[1], '-_', '+/'), strlen($id_token[1]) % 4, '=', STR_PAD_RIGHT));
$payload_json = json_decode($payload, true);

// 整形と表示
print<<<EOF
    <html>
    <head>
    <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
    <title>Obtained claims</title>
    </head>
    <body>
    <table border=1>
    <tr><th>Claim</th><th>Value</th></tr>
EOF;
    foreach($payload_json as $key => $value){
        print('<tr><td>'.$key.'</td><td>'.$value.'</td></tr>');
    }
print<<<EOF
    </table>
    </body>
    </html>
EOF;

?>



書いたコード自動的に保存されるので、これで準備はこれで終了です。


◆テストしてみる

いよいよ動かしてみます。
Azure ADの組織アカウントとマイクロソフトアカウントの両方でログインできて、ユーザ情報が取得できていればOKです。


まずはマイクロソフトアカウントでログインしてみます。

アプリケーションのURLを開くと、ログイン画面が出てくるのでログインアカウントの欄にマイクロソフトアカウント(メールアドレス)を入力します。


すると自動的にマイクロソフトアカウントのログインページに遷移が始まります。


ここでパスワードを入力してサインインします。



すると初回アクセスだとアプリケーションによるアクセス許可の同意画面が出てくるのでアクセスを許可します。



上手く動けばアプリケーションに遷移し、id_tokenに含まれるclaim(属性情報)が一覧で表示されます。




次はAzure ADの組織アカウントを使って同じことをしてみます。

先ほどと同じくアプリケーションを開き、今度はAzure AD上のアカウントを入力すると、Azure ADのログイン画面へ遷移しますので、パスワードを入れてサインインします。



すると同じく同意画面が出てきますのでアクセスを許可します。



同じく上手く動けばアプリケーションに遷移し、claim一覧が表示されます。マイクロソフトアカウントとAzure AD組織アカウントでは取得できる情報が若干異なるようです。




いかがでしょうか?
まだPreviewということでscopeが制限されていたりしますが、とりあえずログインまでは問題なく動くようですので、今後、外部アカウントと共同で業務を行う場合などはこのようなモデルでアプリケーションを開発するのも一つの選択肢となるかも知れません。

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