2012年3月25日日曜日

WIF Extension for OAuth を使って OpenID Connect を体験


CTP版 がリリースされてから1年が経過して全然本リリースの気配が見えない WIF Extension for OAuth ですが今更ながらちゃんと触ってみました。
前回はサンプルプロジェクトを使って動きを少し解説しましたが、今回はスクラッチで作ってみます)

で、今回はせっかくなので OAuth2.0 がベースとなっている OpenID Connect の RP を WIF Extenstion で作ってみます。
(ちょっと微妙ですが Google の OP / Authorization Code Flow でテストしてみます)

また、WIF Extension がカバーしている範囲が access_token を取得するところまでなので、Userinfo Endpoint へのアクセスは手動です。OAuth の Protected Resource へのアクセスと全く同じです。。


尚、OAuth や OpenID Connect そのものの仕様に関しては私も人に説明するほど理解していないので
 http://oauth.jp/
 とか
 http://d.hatena.ne.jp/ritou/
 とか
 http://www.sakimura.org/
なんかを参考にしてください。


■はじめに

まず始める前に、今回の流れと WIF Extension for OAuth のカバー範囲を解説します。

まず、やることですが「Googleアカウントに保持しているアカウント情報をユーザ(オーナー)同意の元、Webアプリケーションに代理取得させる」という仕組みを作ってみます。

処理の流れ、および WIF Extension for OAuth がやってくれる範囲は以下の通りです。

























次に、WIF Extension for OAuth の仕組みですが、カスタムハンドラを定義することにより AuthZ Code や access_token の要求・受信に関するイベントを拾ってアプリケーションへ渡す、という形になっています。

























■事前準備

事前準備として Google 側にこれから作るアプリケーションを登録して client_id および client_secret を取得しておく必要があります。
Google API コンソールへアクセスし、API Access メニューからアプリケーションを登録します。
今回作成するアプリケーションの URL を http://rp.adfs20.net/oauth_client/ とする予定なので、Web アプリケーションとしてその URL を登録すればよいのですが、一つ注意点です。
先のダイアログに書いたように実際に AuthZ Server や Resource Server からの通信を受け付けるのはカスタムハンドラなので、カスタムハンドラの URL が End Point として登録すべき URL になります。
(今回は http://rp.adfs20.net/oauth_client/OAuthHandler.ashx を登録します)

登録が終わると client_id および client_secret が表示されるので、保存しておきます。



■実装~テスト

では、さっそく実装してみます。

1.Visual Studio で ASP.NET 空のアプリケーションを作成する

 まずは Visual Studio で ASP.NET のアプリケーションを作ります。



























 作成したら、プロジェクトに以下を新規追加します。
 ・Web フォーム( Default.aspx とします)
 ・グローバル アプリケーション クラス( Global.asax )


2.WIF Extension for OAuth のライブラリを参照する

 ライブラリは以下の URL からダウンロード出来るのでダウンロードおよび解凍をしておきます。

  ダウンロード URL :
   https://connect.microsoft.com/site1168/Downloads

 解凍したディレクトリにある「 Microsoft.IdentityModel.Protocol.OAuth.dll 」を参照します。




























3.実装

 各モジュールに以下のコードを実装します。

 ・web.config
  ここにはカスタムハンドラの設定を記載します。
   名称:OAuthHandler
   パス:OAuthHandler.ashx (先に Google API Console に登録したパスと同じである必要があります)
  このパスに対する HTTP 要求があった場合に WIF が動く、という具合です。
<system.webServer>
  <handlers>
    <add name="OAuthHandler"
          verb="*"
          path="OAuthHandler.ashx"
          type="Microsoft.IdentityModel.Protocols.OAuth.Client.EndUserAuthorizationResponseHandler, 
                Microsoft.IdentityModel.Protocols.OAuth" />
  </handlers>
</system.webServer>


 ・global.asax
  ここでは、OAuthClient の設定およびカスタムハンドラ内のロジック実装を行います。

  まずは、AuthZ Server の登録ですが、Google OP の AuthZ Server のエンドポイントは
  ・Authorization : https://accounts.google.com/o/oauth2/auth
  ・Token : https://accounts.google.com/o/oauth2/token
  ですので、あらかじめ取得しておいた client_id および client_secret と合わせて設定をしておきます。

// AuthZ Server登録
InMemoryAuthorizationServerRegistry serverRegistry = new InMemoryAuthorizationServerRegistry();
// For Google
AuthorizationServerRegistration registrationInfo = new AuthorizationServerRegistration(
  new Uri("https://accounts.google.com/o/oauth2/token"),
  new Uri("https://accounts.google.com/o/oauth2/auth"),
  [取得した client_id], [取得した client_secret);

serverRegistry.AddOrUpdate(registrationInfo);
OAuthClientSettings.AuthorizationServerRegistry = serverRegistry;


  次に、Resource Scope Mapping の登録です。
  ここでは、指定した Scope パラメータに応じた権限を持つ access_token を発行するためのエンドポイントおよび Scope パラメータそのものを指定します。Google の場合は Authorization と同じエンドポイントになるため、
  ・Token : https://accounts.google.com/o/oauth2/token
  ・Resource Mapping : https://accounts.google.com/o/oauth2/auth
  を指定します。
  また、OpenID Connect を利用するときの scope は通常「openid」を指定しますが、Google の場合は
  ・https://www.googleapis.com/auth/userinfo.email
  ・https://www.googleapis.com/auth/userinfo.profile
  を指定します。

// Resource Scope Mapping 登録
InMemoryResourceScopeMappingRegistry resourceRegistry = new InMemoryResourceScopeMappingRegistry();
// For Google
resourceRegistry.AddOrUpdate(
  "token",
  new Uri("https://accounts.google.com/o/oauth2/token"),
  new Uri("https://accounts.google.com/o/oauth2/auth"),
  new string[] { "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile" });

OAuthClientSettings.ResourceScopeMappingRegistry = resourceRegistry;


  global.asax 最後はイベントハンドラの登録とロジックの実装です。
  WIF Extension for OAuth で拾える OAuth 関係のイベントは以下の4つです。
  ・access_token を要求(RequestingAccessToken)
  ・AuthZ code を受信(AuthorizationCodeReceived)
  ・access_token を受信(AccessTokenReceived)
  ・エンドユーザの同意(認可)が得られなかった(EndUserAuthorizationFailed)

  それぞれについてハンドラの登録とメソッドの実装を行います。
  まずはハンドラの登録です。
// Event Handler登録
OAuthClientSettings.RequestingAccessToken += new EventHandler<RequestingAccessTokenEventArgs>(OAuthClientSettings_RequestingAccessToken);
OAuthClientSettings.AuthorizationCodeReceived += new EventHandler<AuthorizationCodeReceivedEventArgs>(OAuthClientSettings_AuthorizationCodeReceived);
OAuthClientSettings.AccessTokenReceived += new EventHandler<AccessTokenReceivedEventArgs>(OAuthClientSettings_AccessTokenReceived);
OAuthClientSettings.EndUserAuthorizationFailed += new EventHandler<EndUserAuthorizationFailedEventArgs>(OAuthClientSettings_EndUserAuthorizationFailed);


  次にそれぞれメソッドを実装します。
  今回は AuthZ code および access_token を受信したら Session に取得したデータを保持することします。
  (RequestingAccessToken 及び EndUserAuthorizationFailed については何も実装せず空のメソッドを作ります)

  それぞれイベントが発生するとイベントの引数(**EventArgs)に受信したトークンなどが入ってくるので取り出して Session へ Add します。
// アクセストークン要求イベント
void OAuthClientSettings_RequestingAccessToken(object sender, RequestingAccessTokenEventArgs requestingTokenEventArgs)
{
}

// AuthZ code 受信イベント
public void OAuthClientSettings_AuthorizationCodeReceived(object Sender, AuthorizationCodeReceivedEventArgs codeReceivedEventArgs)
{
    string code = codeReceivedEventArgs.AuthorizationCode.ToString();
    codeReceivedEventArgs.HttpContext.Session.Add("code", code);
}

// アクセストークン受信イベント
public void OAuthClientSettings_AccessTokenReceived(object Sender,AccessTokenReceivedEventArgs tokenReceivedEventArgs)
{
    string accessToken = tokenReceivedEventArgs.AuthorizationResponse.Parameters[OAuthConstants.AccessToken];
    tokenReceivedEventArgs.HttpContext.Session.Add("at", accessToken);
    string TokenType = tokenReceivedEventArgs.AuthorizationResponse.Parameters[OAuthConstants.TokenType];
    tokenReceivedEventArgs.HttpContext.Session.Add("tokenType", TokenType);
    string idToken = tokenReceivedEventArgs.AuthorizationResponse.Parameters["id_token"];
    tokenReceivedEventArgs.HttpContext.Session.Add("idToken", idToken);
            
}

// 認可失敗イベント
void OAuthClientSettings_EndUserAuthorizationFailed(object Sender,EndUserAuthorizationFailedEventArgs authorizationFailedEventArgs)
{
}



  これで global.asax は終わりです。次は実際のロジックを実装する部分(Default.aspx)です。

 ・Default.aspx

  ここは非常にシンプルで、WIF Extension for OAuth のメソッドをコールするだけです。
  コールするのは、OAuthClient.RedirectToEndUserEndpoint というメソッドです。
  引数は、
  ・リソース名(global.asax の Resource Scope Mapping で指定したリソース名)
  ・Authorization Response Type : Authorization Code Flow の場合は Code を指定
  ・Redirect URL : リダイレクト先の URL。Google API 側に設定した URL と同じもの
  です。
OAuthClient.RedirectToEndUserEndpoint(
  "token",
  AuthorizationResponseType.Code,
  new Uri("http://rp.adfs20.net/oauth_client/OAuthHandler.ashx"));



4.デプロイ

 実装が終わったらビルドしてデプロイします。
 IIS側の設定としては特に気を付ける必要はありませんが、.NET Framework 4.0 で動きますのでアプリケーションプールは .NET Framework 4.0 で動かしている物を割り当てる必要があります。


5.実行

 さて、実行してみましょう。

 作成した Web アプリケーションへアクセスします。



























 access token を取得しようとするとますは認証が走ります。


























 認証が終わるとリソースへのアクセスへの同意を求められます。




















 同意すると、
 ・AuthZ Code
 ・access_token
 ・token type
 ・id token
 が取得できているのがわかります。























 この状態で Userinfo エンドポイントに access_token を渡してやります。
 エンドポイントアドレスは「https://www.googleapis.com/oauth2/v1/userinfo」ですから、ここへ HTTP GET で access_token を渡してやります。
Response.Redirect("https://www.googleapis.com/oauth2/v1/userinfo?access_token=" + Session["at"] as string);


 すると、json 形式でユーザの情報が表示されます。
























■終わりに

いかがでしたでしょうか?
元々非常にシンプルな実装を目指している仕様だけあって本当は WIF の様なライブラリは不要なのかも知れませんが、WebRequest などを自分で実装する手間は省けるのでコーディング量は減らせるかも知れません。
今後の認証プロトコルの本命は OpenID Connect になるとも言われているのでまずは仕組みを勉強するきっかけになれば良いかな?と思います。

2012年3月17日土曜日

[FIM2010] R2 で強化されたパスワード管理機能 - その2


前回から時間があいてしまいました。前回は Web ベースでの QA ゲート(秘密の質問と答え)によるパスワードリセット機能を紹介しましたが、 FIM2010 R2 からは他にも、
・ワンタイム パスワードの電子メール ゲート
・ワンタイム パスワードの SMS ゲート
が新たに追加されています。

簡単に言うとあらかじめ登録した電子メールや SMS(携帯電話のテキストメッセージ)へワンタイムパスワードを通知し、そのワンタイムパスワードを利用してパスワードをリセットする、という機能です。

しかし、通常パスワードを忘れている環境では電子メールを見ることが出来ないことが多いと思いますので、今回は SMS への通知の機能の使い方を紹介します。
※参考URL)http://technet.microsoft.com/en-us/library/hh824692(v=ws.10).aspx

通常、SMS は携帯電話キャリアのネットワークに依存する世界なので電子メールと同じようにインターネット経由でメッセージを送信することが出来ません。
やり方としては、サーバにモデムを接続して公衆回線経由でメッセージを送信する、という方法と SMS プロバイダと呼ばれるインターネット経由での SMS 送信サービスを提供している業者のサービスを使う方法が考えられます。
今回は後者の SMS プロバイダのサービスを利用することにしました。

尚、FIM2010 R2 から SMS へメッセージを送信するには SMS プロバイダが公開しているメッセージ送信 API を利用することになるので SMS プロバイダを選択するときは API を提供しているかどうかを調べる必要があります。
また、先にも書きましたが携帯キャリアのネットワークに依存する話なので、 SMS プロバイダが利用したい携帯キャリアに対応しているかどうかについても調べる必要があります。

今回は「イージー SMS」という SMS プロバイダを試しに使ってみました。
ここは API を公開していますし、国内の携帯キャリアでは Docomo と Softbank に対応しています。
(1通のメッセージを送るのに50円ほどかかります)

さて、早速手順を紹介します。
(イージー SMS への登録手順は省略します。詳細は Web サイトを参照してください)


■準備作業
手順1.SmsServiceProvider.dllを作成する

 FIM2010 R2 から SMS メッセージを送信するには SMS プロバイダが提供している API を呼び出すためのクラスライブラリを作成する必要があります。

 FIM2010 R2 では SmsServiceProvider.dll という名前で DLL を作成し、そのライブラリが SendSms というメソッドを公開していれば良いので、まずはクラスライブラリを作成します。(今回はVisual Studio 2010 を使って作成しました)

 Visual Studio 2010 を開いてプロジェクトを作成します。
 プロジェクトは以下の様に作成します。
 ・.NET Framework 3.5(FIM2010 R2 が .NET Framework 3.5 で開発されているので)
 ・クラスライブラリ
 ・名称:SmsServiceProvider
 ・参照設定:FIMインストールメディア内に存在するMicrosoft.IdentityManagement.SmsServiceProviderContract.dll
  ※Service and Portal\Program Files\Microsoft Forefront Identity Manager\2010\Service\GAC以下に存在

 [プロジェクトの作成]



























 [参照設定]
































 イージー SMS を使う場合のコードは以下の様な形になります。
using System;
using System.Collections.Generic;
using Microsoft.IdentityManagement.SmsServiceProvider;
using System.Net;
using System.IO;

namespace SmsServiceProvider
{
    public class SmsServiceProvider : ISmsServiceProvider
    {

        public void SendSms(string mobileNumber,
                            string message,
                            Guid requestId,
                            Dictionary<string, object> deliveryAttributes)
        {
            StreamWriter writer = new StreamWriter("c:\\tmp\\SMSLog.txt");
            writer.WriteLine("開始");

            System.Collections.Specialized.NameValueCollection ps = new System.Collections.Specialized.NameValueCollection();
            ps.Add("recipient", mobileNumber);
            ps.Add("sender", "+81901234567"); // 送信元
            ps.Add("message", message);
            ps.Add("password", "xxxxx"); // イージー SMS の契約パスワード
            WebClient wc = new WebClient();

            try
            {
        // API KEY はイージー SMS 契約時に入手できる
                byte[] resData = wc.UploadValues(@"https://api.ezsms.biz/{API KEY}/messages/send/", ps);
                wc.Dispose();
                string[] a = System.Text.Encoding.UTF8.GetString(resData).Split('\n');
                Boolean flg = false;
                foreach (string str in a)
                {
                    writer.WriteLine(str);
                    // 結果取得
                    if (str.Contains("OK") == true)
                    {
                        flg = true;
                        break;
                    }
                }
                if (flg == false)
                {
                    // エラー
                    throw new Exception("Could not send SMS message.");
                }
            }
            catch (System.Net.WebException ex)
            {
                //HttpWebResponseを取得
                System.Net.HttpWebResponse errres =
                    (System.Net.HttpWebResponse)ex.Response;
                writer.WriteLine("SMSプロバイダへ接続できない");
                throw new Exception("Could not connect to the SMS Provider.");
            }
            finally
            {
                wc.Dispose();
                writer.Close();
            }
        }
    }
}


後はビルドして完成した DLL ファイルを FIM2010 R2 サーバの Service 以下に配置すれば OK です。
(デフォルトインストールで C:\Program Files\Microsoft Forefront Identity Manager\2010\Service 以下)


手順2.SMS Gateの有効化

 次は SMS ゲートを使う様に FIM2010 R2 を設定します。

 設定を行うのは管理ポリシールール(MPR)の Password Reset AuthN Workflow です。
 管理ポリシールールのワークフローから当該のワークフローを探して編集します。




























 アクティビティとして「ワンタイム パスワードの SMS ゲート」を追加します。SMS ゲートだけを使うのであれば他のゲートは消してしまっても構いません。


































 追加したアクティビティの設定を行います。今回はテストなのでそのまま保存をしました。


































これでサーバ側の設定作業は完了です。次はユーザ自身による通知先の設定です。


手順3.通知先携帯電話番号の登録(ユーザ自身による作業)

 ユーザ自身によりパスワード登録画面を開き、通知先の携帯電話番号を登録します。(イージー SMS の場合、国番号から電話番号を登録する必要があるので +81 から登録します)





















■リセットの実行
では、実際にリセットをしてみます。

パスワードリセット画面へアクセスし、ユーザ名を「ドメイン名\ユーザ名」という形で入力します。































次へ、をクリックすると携帯電話へメッセージが送信されます。












































メッセージ内のワンタイムパスワードを画面に入力します。































正しいコードを入力するとパスワードのリセット出来ます。

2012年3月1日木曜日

[Windows Server 8]ベータ版上の AD FS を見てみる


2/29 にリリースされた Windows 8 Consumer Preview / Windows Server 8 beta ですが、どうやら周りを見ていると Windows 8 Consumer Preview ばかりが目立ってしまっているので、あえて Server 8 を触ってみました。

・ダウンロードURL
 http://technet.microsoft.com/ja-jp/evalcenter/hh670538.aspx

ちなみに MVP Global Summit の会場ではこんな USB メモリが配布されていました。
(中に ISO を入れて配っていたところもあったようですが、私がもらったのは空でした。。。)



















さて、さっそく簡単に確認をしてみたところ、 Windows Server 2008 / R2 の時の AD FS / AD FS 2.0 との差は以下の通りでした。
・役割の追加で AD FS 2.0 と同等のものが入る(ただし、「2.0」という番号はついていない)
・サーバだけでなく、AD FS Web Agents( Claims-aware Agent / Windows Token-based Agent )および Federation Service Proxy も追加できる
・Windows Identity Foundation は自動的には追加されない(追加しなくても AD FS は動作する)
・機能の追加から Windows Identity Foundation 3.5 を追加できる

まだ機能面は試せていませんが、軽く Claim Rule の記述部分などを見てみても変わっている雰囲気がなかったので、現行の AD FS 2.0 Update 1 をベースに組み込まれているのだと思われます。
今後、追って検証結果なども紹介していこうと思います。

簡単に画面ショットを載せておきます。

[役割の追加]





























[管理コンソール]



























[サーバマネージャのダッシュボード]

























[サービス一覧]
























[メトロスタイルのスタート画面]


[FIM2010]ECMA2.0 対応のLotus Domino 8.x コネクタがリリース


昨日もよく似たようなポストをしましたが、今回はベータではなく RTM です。
そして対象が Lotus Domino 8.x です。

・ダウンロードURL
 http://www.microsoft.com/download/en/details.aspx?id=29038
・ドキュメントURL
 http://technet.microsoft.com/en-us/library/hh859750(v=ws.10).aspx

まぁ海外製品の純正アダプタなので国内でよく使われる DJX テーブルへの対応はしていませんが、一通りの機能はそろっています。


先日の Update 2 以降、本格的に ECMA 2.0 対応のコネクタがリリースされてきていますし、今後は既存の接続先システムへの接続もこの新しいフレームワークで開発がされていくことになると思われますので、今のうちに ECMA 2.0 でのコネクタ開発に慣れておきましょう。
関連する技術リソースもそれなりに出てきていますし。
・Extensible Connectivity 2.0 Management Agent Reference
 http://msdn.microsoft.com/en-us/library/hh859557.aspx
・API リファレンス
 http://msdn.microsoft.com/en-us/library/microsoft.metadirectoryservices.aspx
・以前紹介した ECMA 2.0 のイントロ
 http://idmlab.eidentity.jp/2012/01/fim2010-r2.html