連載:Microsoft技術におけるアイデンティティ連携開発のいま(4)
Azureの認証におけるその他サービス ~ Azureモバイルサービス、多要素認証
最終回。Azureモバイルサービスや、多要素認証など、これまでの連載で取り上げなかった、認証に関わる先進的な技術を簡単に紹介する。
第3回までの連載を通し、マイクロソフト技術を使ったアイデンティティ連携開発の観点で、主要なサービスやライブラリなどを紹介した。
今回の最終回では、認証に関わるその他の先進的なトピックとして、Microsoft Azure(旧称:Windows Azure)モバイル・サービス(Azure Mobile Services)、多要素認証(Multi-Factor Authentication)など、第3回までで取り上げなかった技術を簡単に紹介しておきたい。
Azureモバイル・サービスの認証 - なぜ、モバイル・サービスか?
昨今、スマートフォンをはじめとするモバイル・アプリのプログラミングは、開発全体の大きなウェイトを占めてきている。そして皆さんが持っているモバイル・アプリを見ていただくと分かるが、そのほとんどは、デバイスの中の閉じた世界で動作するのではなく、ネットワーク上のリソースと連携して動作するアプリではないだろうか?
Azureモバイル・サービス(Azure Mobile Services)は、こうしたバックエンドのサービスと連携して動作するモバイル・アプリを、あらかじめ用意されたサービスやライブラリと組み合わせて効率的に開発(プログラミング)するためのAzure上のサービスだ。本稿では、特に、このモバイル・サービスで使用されている「認証」の考え方にフォーカスを当てて解説するが、他にも、「データ連携」「通知」「バックエンド・ジョブ」など、モバイル・アプリのビルディングに必要ないくつかの要素をあらかじめ提供している。
例えば、よくありがちなモバイル・アプリのシナリオとして、FacebookやGoogleなどのソーシャル・アイデンティティでユーザー登録を行い、登録したユーザーのみが決められたデータを更新できるようなアプリケーションを考えてみよう。
これを最初から全てプログラミングする場合、ソーシャル・アイデンティティとのフェデレーションのためのプログラミングはもちろん、データ・アクセスも、Web APIなどサーバー側のサービスを構築してアクセス制御を実装する必要があるだろう。また、クライアント(モバイル・アプリ)と、このサービス(Web APIなど)の間で受け渡すトークンのような仕組みも設計しておく必要がある(さらに、このトークンの有効期限の管理なども必要になるだろう)。
これをAzureモバイル・サービスで実装する場合には、「連載:Azureモバイルサービスで作る簡単スマートフォン・アプリ - 認証機能とプッシュ通知をしよう!」で紹介されているように、簡単な事前設定と、(Windows Azureポータルから)生成されるコードのほんの少しの変更で十分だ。
Azureモバイル・サービスの認証 ― その動作
では、実際に、どのような方法で認証を行っているかを見てみよう。できるだけ多くの方に理解していただけるよう、本稿では、実際のモバイル・アプリではなく、WebアプリケーションからアクセスするJavaScriptのコードを例に見ていこう。
まず、以下は、Facebookにログインをして、AzureのSQL Databaseからデータを取得するサンプルだ(事前にFacebook側とAzureモバイル・サービス側に設定が必要だ。詳細は「連載:Azureモバイルサービスで作る簡単スマートフォン・アプリ - 認証機能とプッシュ通知をしよう!」を参照していただきたい)。
<script src='https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.min.js'></script>
<script src='https://tsmatsuz-mob01.azure-mobile.net/client/MobileServices.Web-1.0.0.min.js'></script>
<script>
var client = new WindowsAzure.MobileServiceClient(
'https://tsmatsuz-mob01.azure-mobile.net/',
'bMuOaOJDrFHyAJLUAR...');
var testTable = client.getTable('TestTbl');
if (client.currentUser == null) {
client.login("facebook").then(authSucceeded, handleError);
}
function authSucceeded() {
var query = testTable.where({ complete: false });
query.read().then(function(testItems) {
// 抽出結果(testItems)の描画処理(省略)
……省略……
}, handleError);
}
function handleError(error) {
// エラー処理 (省略)
……省略……
}
</script>
|
このコードそのものについては特に解説の必要はないだろう(認証に成功すると、SQL Databaseに作成された「TestTbl」というテーブルにクエリを行うサンプルだ)。上記のコードが実行されると、下図のようなFacebookのログイン画面が表示され、ここに正しいID(メールアドレス)とパスワードを入力すると認証に成功し、以降は、SQL Database上のデータの検索や更新が可能になる。
以降では、この内部でどのように処理されているか解説しておこう。
Azureモバイル・サービスの認証時の内部処理
まず、Facebookから見ると、アプリケーションとして登録されているのは、このアプリケーションそのもの(上記のJavaScriptコードを含んだアプリケーション)ではなく、Azureモバイル・サービスのサーバーだ。つまり、今回のアプリケーションが、たとえ「http://contoso.com/」で動いていたとしても、Facebookで認証後にリダイレクトする先は、「https://tsmatsuz-mob01.azure-mobile.net/」などだ。
モバイル・サービスのサーバー(https://tsmatsuz-mob01.azure-mobile.net/)は、Facebookが渡した認証用のcode(以下、認証コード)を検証し、問題がなければAzureモバイル・サービス独自の認証チケットを発行して、そのチケットをアプリケーション(今回の場合、上記のJavaScriptコード)に渡す。例えば、今回のサンプルの場合は、モバイル・アプリではなく、HTMLで構築したWebアプリケーションのため、JavaScriptを使ってフレーム間でこの認証コードを渡すことになる(モバイル・アプリの種類に応じ、この受け渡しの方法は異なる)。
以降、アプリケーションは、Azureモバイル・サービスのRESTサービスを通してSQL Databaseのデータに対するCRUD操作を行うが、この際、上記で渡されたAzureモバイル・サービスの認証コードをHTTPヘッダー(X-ZUMO-AUTH)に設定してAPIを呼び出す。すなわち、Azureモバイル・サービスから渡される認証コードがなければ、データにアクセスすることはできない(もちろん、データを匿名アクセス可能にしている場合は、このチケットは必要ない)。
第2回でAccess Control(アクセス制御、ACS)を例に「ハブ」のアーキテクチャによる利点を解説したが、Azureモバイル・サービスも、同様のアーキテクチャによって、モバイル・アプリでありがちなシナリオを解決しているのが分かる。
なお、ここではSDKが提供するJavaScriptのAPIを使用しているが、他にも、.NET、Xcodeなどのモバイル環境に応じたAPIを提供している。また、Azureモバイル・サービスへのアクセスはREST形式で提供されているため(APIでは、最終的に、このRESTのエンドポイントを呼び出す)、サード・パーティが提供するさまざまな言語環境からもアクセスが可能だ(もちろん、RESTのAPIドキュメントも提供されている)。
Azureモバイル・サービスの認証 - 実シナリオへの柔軟な対応
しかし、このアーキテクチャを採用した場合、犠牲も生じる。例えば、認証プロバイダー(Facebook/Google/Microsoftアカウントなど)を使用している他のアプリケーション(例えば、Microsoftアカウントを使用しているSkyDriveなど)との連携は、このアーキテクチャでは不可能だ。Facebookとの間のコード(=認証トークン)はAzureモバイル・サービスのサーバーで処理(隠ぺい)されており、アプリケーションそのものはあずかり知らないからだ。
そこで、こうした点を解決するのが、Azureモバイル・サービスの「Client-directed Login」と呼ばれる認証方式だ(逆に、上述の方式を「Service-directed Login」と呼ぶ)。Client-directed Loginでは、Facebookへの認証とコードの取得は、全てアプリケーション側(モバイル・アプリ側)で実施し、認証の結果得たコードをAzureモバイル・サービスに渡して処理する方式だ(ここでは詳細のサンプル・コードを省略するが、前述のコードのように、Azureモバイル・サービスが提供するAPIを使用して記述できる)。この方法の場合、Facebookへの認証コードはアプリケーションそのものが管理しているため、例えば、Facebookのソーシャル・グラフにAPIを経由してアクセスする場合など、Facebookとの疑似的なシングル・サインオン(SSO)環境もモバイル・アプリ上で実現することが可能だ(もちろん、従来どおり、認証されたユーザーのみがSQL Databaseなどのデータにアクセスすることが可能だ)。
また、他のシナリオとして、Facebookでログインした全てのユーザーを許可するのではなく、登録されている決められたユーザーのみにテーブルへのアクセス許可を行う場合を考えてみよう。この場合、Azureモバイル・サービスでは、テーブルごとに追加(insert)/変更(update)/削除(delete)/抽出(read)のサーバー・スクリプトをカスタマイズできるため、登録されてないユーザーの場合にはエラーを返す(=処理しない)などの実装も可能だ。
下記のコードは、あるテーブルにデータを追加(insert)する際、ユーザーが、「UserTbl」というテーブルに登録されている場合のみ処理を行い、それ以外の場合はHTTPエラーを返すサーバー・スクリプトのサンプル・コードだ(下記のコードでは、UserTblテーブルのuserid列で検索を行っている。この列には、「Facebook:100001430652719」などの一意なIDが入っている)。この手法と組み合わせて、例えば、[サインアップ]ボタンなどを付けてユーザー登録を行う機能なども追加できるだろう。
function insert(item, user, request) {
var usertbl = tables.getTable('UserTbl');
usertbl.where({ userid : user.userId }).read({
success: function(results) {
if (results.length === 0) {
request.respond(401, "User is not registered");
}
else {
request.execute();
}
},
error: function(err) {
request.respond(401, "User validation failed");
}
});
}
|
【補足】サーバー・スクリプトの言語
サーバー・スクリプトは、将来、C#も対応される予定である。
このサーバー・スクリプトによる応用の幅は広く、その他の応用例として、データ更新(insert/update)の際に更新内容をカスタマイズしたり、読み込み(read)の際に読み込むデータを絞り込んだり(条件追加)することも可能なので、例えば、データの作成時に作成者(ユーザー)の情報を設定し、データの抽出時には、この作成者の情報を基に絞り込むなどの実装も可能だ。また、Azureテーブル(Table)との連携や、HTTPリクエスト(RESTによる処理)も扱えるため、カスタム・リポジトリと連携するソリューションなども構築できる(HTTPヘッダーの情報も設定できるので、認証が必要なサービスなどとの連携も実装できるだろう)。
サーバー サイドということでいくつかの制限はあるものの、このサーバー・スクリプトを使うことで、こうしたさまざまなシナリオに柔軟に対処できるようになっているのだ。基本的な動作だけでなく、うまく組み合わせることで、こうした現実の多くのシナリオに対応可能だ。
サーバーサイドということでいくつかの制限はあるものの、このサーバー・スクリプトを使うことで、こうしたさまざまなシナリオに柔軟に対処できるようになっているのだ。基本的な動作だけでなく、うまく組み合わせることで、こうした現実の多くのシナリオに対応可能だ。
もちろん、「第3回 カスタム・アプリケーションによる認証フローとプログラミング」で紹介したフローを使えば、モバイル・アプリでも近い実装は可能だが、例えば、サーバー側のデータを保護するWeb API(REST)のサービスも必要となるなど、いくつかの事前準備や設計が必要となるだろう。Azureモバイル・サービスは、モバイル・アプリでありがちなこうしたシナリオをより簡単に実装するための部品(単なるクライアント側の部品ではなく、サービス側と一体化された複合のビルディング・ブロック)として利用できるように設計されている。
プログラマー(開発者)にとっての多要素認証(Multi-Factor Authentication, MFA)
本連載の最後に、Azure Active Directoryの多要素認証(Multi-Factor Authentication: MFA)について、プログラマーの観点で見ていきたい。
まず、この認証は、単なる「多段階」の認証ではない。例えば、パスワードによる認証で、悪意ある人物が入力している内容を閲覧する場合(ショルダー・ハック)を想像してほしい。悪意のない人物が、たまたま入力している行為を見かけるケースも日常では起こり得るため、そのハッキング行為自体をすぐに「犯罪」として特定するには、現実には曖昧さも多いだろう。このように「情報」とは、そもそも物質的なものと異なり、ある側面で、容易に移動や搾取の対象となりやすい性格を持っており、例えば、パスワードを推測されない文字列に強制したり、定期的な変更を強制したりするなどの対策を行っても、情報そのもののこうした性質から逃れることはできない。
これに対し、例えば、電話を使用した多要素認証では、ユーザーがログインを行う際、アプリケーションはユーザーの所有するスマート・フォンなどに対して確認を行う。つまり、「ユーザーが所有するデバイス」という物質的なものを所有していなければ認証は困難だ。多要素とは、こうした「性質の異なる組み合わせ」を意味する(Azure Active DirectoryではPhoneFactorをベースとしているため、あくまでも「電話」がベースとなっている。他社の多要素認証のプラットフォームでは、専用デバイスを使用する方法などもある)。
すでに、アマゾン(Amazon)、グーグル(Google)など、クラウド・ベンダーの多くが採用しており、特にクラウドの世界では、一般的な方法となりつつある。
本稿では、この多要素認証の構成やセットアップ方法などは省略し、プログラマーにとってどのような影響があるか考えてみよう(今回は説明を省略するが、この多要素認証は、PhoneFactorのサービスと連携するサーバーを立てることで、オンプレミス環境でも使用できる)。
まず、Azure Active Directoryで多要素認証を設定しているユーザーの場合、Webブラウザーから(パスワードを入力して)ログインを行うと、下図のように画面は待機状態になり、デバイスからの入力を受信しないと先に進むことはできない。
この動きはログイン画面を提供するプロバイダー(すなわち、今回の場合、Azure Active Directory)が行っている。このため、Webアプリケーションを構築してシングル・サインオン(SSO)を構成する場合、プログラマーは、この多要素認証を意識したプログラミングや設定は一切必要ないのだ。カスタム・アプリケーションの場合でも、「第3回 カスタム・アプリケーションによる認証フローとプログラミング」で紹介したように、Webブラウザー用のコンポーネントなどを使用してログインを行う場合には、多要素認証は問題とはならないだろう。つまり、モバイル・アプリなどのネイティブ・アプリケーションから認証を行う場合も、可能な限り、ブラウザーを使用した方法を組み合わせて認証を行う方がよいだろう。
一方、例えば、WS-Trustを使用した実装など、Webブラウザーを使用せずにネイティブ・アプリケーションなどからカスタムにログイン処理を実装している場合は、現在のところ、多要素認証は対応していないので注意してほしい(同様に、既存のOutlook、Lyncなど、マイクロソフトが提供するこうしたリッチ・クライアントでも、まだ対応できていない)。
また、プログラマーにとって、多要素認証のカスタマイズの余地も、多少は用意されている。Azure Active Directoryの多要素認証はPhoneFactorをベースとしており、プログラマーは、XMLのHTTPリクエストを使った認証の呼び出しが可能だ。この呼び出しを行うには、PhoneFactorのサイトから証明書をダウンロードし、リクエストに証明書情報の添付が必要だが、PhoneFactorが提供するSDKを使うと、こうした処理を簡単な関数の呼び出しのみで実装可能だ(現在、.NET、Java、PHP、Ruby、PerlのSDKがダウンロード可能。PhoneFactorのサイトをログインすると左側にある[SDK]メニューからダウンロードできる)。
例えば、下記のコードは、このSDKを使用して、SMSによる認証の呼び出しを行うC#のコードだ(もちろん、音声や、PINを使ったコード確認など、その他の認証方法も指定可能だ)。
static void Main(string[] args)
{
string otp = "";
int callStatus = 0;
int errorId = 0;
PfAuthParams pfAuthParams = new PfAuthParams();
pfAuthParams.Username = "demouser01@demodomain.onmicrosoft.com";
pfAuthParams.CountryCode = "81"; // Japan
pfAuthParams.Phone = "9011112222";
pfAuthParams.Mode = pf_auth.MODE_SMS_TWO_WAY_OTP;
pfAuthParams.SmsText =
"<$otp$>¥nWelcome to Build Insider ! Reply with this one-time passcode.";
pfAuthParams.Hostname = "TestHostName";
pfAuthParams.IpAddress = "255.255.255.255";
pfAuthParams.CertFilePath = "C:¥¥Demo¥¥cert_key.p12";
bool res = pf_auth.pf_authenticate(
pfAuthParams, out otp, out callStatus, out errorId);
Console.WriteLine("Result: {0}", res.ToString());
Console.WriteLine("Opt: {0}", otp);
Console.WriteLine("Call Status: {0}", callStatus.ToString());
Console.WriteLine("Error Id: {0}", errorId.ToString());
Console.ReadLine();
}
|
上記のpf_authenticateメソッドでPhoneFactorのサービスに要求が飛び、指定した電話番号のスマートフォンには、下の画面のようなSMSメッセージが送信される。今回の場合、このSMSメッセージにコードを入力して返信すると認証は成功し、成功すると、上記コードのResultとして「True」、Call Statusとして「19」(Text Message Authenticated)が返ってくる(一方、認証に失敗するとResultとして「False」が返る)。
このSDKを使って、例えば、特定の重要な処理を実行する際に、再度、電話による認証を要求するなどのカスタムな実装が可能となるだろう。
1. .NETで使えるアイデンティティ連携のためのライブラリまとめ(前編)
Webアプリなどで、Facebook、Google、Twitter、企業内のActive Directoryなどの各アイデンティティ基盤と連携するための各技術(ライブラリやサービス)を、開発者視点で整理する連載スタート。
2. .NETで使えるアイデンティティ連携のためのライブラリまとめ(後編)
アイデンティティ連携で使用可能なライブラリやサービスなどをまとめる記事の後編。今回はAzure Active Directoryのアイデンティティ連携を中心に解説。
3. カスタム・アプリケーションによる認証フローとプログラミング
ネイティブ・アプリのプログラム・コードから認証を行う方法とは? Azure Active Directoryを例に、OAuth 2.0をベースにした、プラットフォームに依存しない新しい手法を考察する。