2013年12月31日火曜日

[OAuth]年末大掃除。Facebookへのアクセス許可の整理

番外編です。
年末なので色々とPC環境やFacebook等の設定の整理をしています。

Facebookを使っていると、色々なアプリケーションやWebサイトに「いいね!」をしたり、プロフィール情報の読み取りや投稿許可を与えたりしてしまう(せざるを得ない)事があると思います。(この辺りがOAuthです)

あんまりアクセス許可を与えっぱなしにしておくのも気持ちが悪いのでいい機会なのでまとめて整理をしてしまいましょう。

まずは、「いいね!」をしてしまったWebサイトやFacebookページの整理です。

自分のプロフィールのアクティビティを開きます。

左下に「ページと趣味、関心」があるので、開きます。

色々なページに「いいね!」をしているのがわかります。
「いいね!」を取り消すにはそれぞれにある鉛筆マークをクリックします。
※実際にJICSのサイトの「いいね!」を消したらダメですよ(これ重要)



次に、アプリに与えてしまった許可の整理です。

こちらはプライバシー設定の中にあります。
画面右上のメニューからプライバシー設定を開きます。


続いてプライバシー設定画面の左下のアプリを開きます。

すると許可を与えたアプリの一覧が表示されます。
結構な数のアプリを使ってます。


ここで編集をクリックすると具体的にどんな許可を与えているのかをみることができます。

Amazonアプリ、結構な権限を要求してきています。。。。
ここで不要だと思ったら×をクリックして権限ごとに削除してしまうのも良いですし、アプリケーション単位で許可を取り消してしまうこともできます。

でも、消す前に具体的にアプリがいつ、何をしたのか?について見たければ、最終アクセスの中の「詳細を見る」をクリックすると履歴が見れます。

ちょこちょこ覗かれてますね。

信頼できるアプリやページであればいいですが、間違って許可を与えてしまった場合などはこの辺りで適切に管理をしてあげましょう。



と、いうことで年末の大掃除の一環でFacebookの権限周りを整理してみました。
皆さんもこの機会にぜひ大掃除をして良い年をお迎えください。

今年もお世話になりました。

2013年12月22日日曜日

出ました!「OpenID Connect と SCIM のエンタープライズ利用ガイドライン」

OpenID、OpenID Connect、OAuth、SCIM なんていうキーワードが出てくると中々一般企業での利用のイメージがわかないのが現実だと思います。

OpenIDファウンデーション・ジャパンでは、そんな危機感の元、エンタープライズ・アイデンティティ・ワーキンググループというものを立ち上げて、企業内で OpenID Connect や SCIM の利用を促進するにはどうすれば良いのか?の検討を重ねてきました。
(以前から私も参加している日本ネットワークセキュリティ協会 / JNSA のアイデンティティ管理ワーキンググループとの共同ワーキングなので、私は両方の顔で参加しています)

実は本業が忙しく、それほどどっぶりと成果物を作成する作業には入れなかったのですが、みなさんの努力の結果、「OpenID Connect と SCIM のエンタープライズ利用ガイドライン」という形で資料を公開することが出来ました。

OpenID ファウンデーション・ジャパンの Web ページからダウンロード出来ます。

 OpenID ファウンデーション・ジャパンのお知らせページ


目次を一部ご紹介しますと、、
  • エンタープライズITにおけるフェデレーション標準プロトコルとアイデンティティ・プロビジョニング標準プロトコルの有用性
  • フェデレーション標準プロトコル~OpenID Connect解説
  • アイデンティティ・プロビジョニング標準プロトコル~SCIM解説
  • フェデレーションとアイデンティティ・プロビジョニング標準プロトコルの日本エンタープライズITへの適用
  • OpenID Connect/SCIMユースケース
  • 関連技術/概念

内容的に、日本のエンタープライズITの独自の事情について記載があったり、OpenID Connect / SCIM の技術解説があったり、関連技術としてのトラストフレームワークの話があったり、と盛りだくさんなので技術者の方にも、そうでない方にとっても役に立つ資料になっていると思われます。

OpenIDファウンデーション・ジャパンの会員企業になるともう少し深い内容について情報入手が可能ですが、今回一般に公開されたものでもかなり有用なので是非、ご覧下さい。


2013年12月18日水曜日

[FIM2010]いよいよ次世代へのロードマップが見えてきた

MIIS(Microsoft Identity Integration Server)からILM(Identity Lifecycle Manager)、ILMからFIM(Forefront Identity Manager)へと名前を変えてきたこのプロダクトですが、次の方向性がようやく見えてきました。

参考)FIMの歴史。以前.NETラボのセミナで使ったスライド


2013/12/17付けの Technet の Server & Cloud Blog で次の製品の方向性が発表されています。

 Important Changes to the Forefront Product Line
 http://blogs.technet.com/b/server-cloud/archive/2013/12/17/important-changes-to-the-forefront-product-line.aspx


blog によると、2015年の第1半期にメジャーリリースが出て、

  • Windows Azure Active Directoryとのハイブリッドシナリオ
  • ユーザとアクセス管理
  • 監査とコンプライアンス

というあたりがキーワードになりそうです。

追って詳細情報も出てくると思われるので、続報があれば書いていきたいと思います。


2013年12月10日火曜日

[WAAD/IDMaaS]SaaSアプリケーションのID管理をクラウドで行う「ハイブリッド ID 管理」

# Windows Azure Advent Calendar 向けポストです。

Windows Azure Active Directory(WAAD)のアプリケーション統合の機能を使うと Google Apps や Salesforce.com などの各種 SaaS アプリケーションへのシングルサインオンやプロビジョニングを WAAD 経由で実施することができます。いわゆる、IdMaaS(Identity Management as a Service)というやつです。

WAAD のアプリケーション・パネル(https://myapps.microsoft.com


今回はこの機能をうまく使って、企業などの組織が行ってきた各種サービスへの個別の ID 管理やシングルサインオン環境構築をどこまで省力化できるか、を検証してみたいと思います。


前提となる環境、利用する機能は以下です。

  • WAAD のアプリケーション統合機能
  • 先日発表された WAAD Premium の機能である、アプリケーションへのグループの割り当て機能(現在プレビュー)
  • 社内の ID 情報ソースと WAAD のアカウントおよびグループ同期(ディレクトリ同期ツールや Forefront Identity Manager/FIM の WAAD コネクタなど)もしくは WAAD Graph API によるアカウント・グループ管理(今回は Graph API を利用)



では早速始めます。

◆IdM をオンプレミスで構築する(おさらい)

例えば、Google Apps や Salesforce.com などのクラウド・サービスの ID を社内から管理したいと考えた場合、FIM をはじめとする ID 管理製品の各種サービスとのコネクタを構築(もしくは購入)して、それぞれのサービスと接続して ID 情報を同期する必要がありました。

参考)FIM2010 用の GoogleApps コネクタ
 https://fim2010gapps.codeplex.com/


◆IdMaaS の登場

各種 SaaS サービスと個別に接続する、というアプローチは非常に柔軟ですが、一方で各サービスのインターフェイスや API の変更へ追従していくのが非常に大変、、という側面を持ちます。
そこで、各種 SaaS サービスとの接続自体をクラウド・サービスとして提供しよう、という考え方である、Identity Management as a Service(IdMaaS)という考え方が登場し、Microsoft の Windows Azure をはじめ、PingIdentity の PingOne や Salesforce の Salesdorce Identity や Intel/McAfee の Intel Cloud SSO など各種サービス業者がサービスを展開し始めています。

参考)これまで紹介してきたエントリ
Windows Azure Active Directory(http://www.windowsazure.com
 [WAAD] IDaaS としての機能が充実してきた
 http://idmlab.eidentity.jp/2013/07/waad-idaas.html

PingOne(https://www.pingone.com/
 [IDaaS]PingIdentity Cloud Desktop で便利ライフ
 http://idmlab.eidentity.jp/2013/10/idaaspingidentity-cloud-desktop.html

Intel Cloud SSO(http://www.intelcloudsso.com
 [IDaaS] Intel Cloud SSO を試してみた
 http://idmlab.eidentity.jp/2012/05/idaas-intel-cloud-sso.html


◆IdMaaS で ID 管理を楽にする

WAAD をはじめ、各種 IdMaaS サービスには各種 SaaS サービスへのプロビジョニング機能があるため、こんなことが成立します。



これであれば、社内の ID 管理システムは IdMaaS とさえ接続しておけば、あとは IdMaaS 側でインターフェイスの保証などをしてくれるので、社内の ID 管理システムを個別に各システムと接続する必要も、各サービスのインターフェイスや API の更改に神経をとがらせる必要もありません。


◆IdMaaS の運用をどこまで自動化できるのか

ここまでで勘の良い方ならお分かりだと思いますが、IdMaaS への ID 同期とその先の各種サービスへのプロビジョニングをどこまで細かく制御できるのか?という課題が出てきます。
一般的な ID 管理システムではここでいう IdMaaS への接続と同期はコントロールできても、その先のコントロールについては IdMaaS 側にお任せ、ということになるはずです。

となるとオンプレミスの ID 管理システムや運用システムから IdMaaS の管理をどこまで自動化できるかがポイントになってきます。


◆WAAD の場合。Graph API の活用?

WAAD の場合は個人 ID などのディレクトリ上のオブジェクトの操作をプログラムから行うための API として Graph API を提供しています。

参考)[Office 365 & WAAD] Graph API を利用したアイデンティティ管理
 http://idmlab.eidentity.jp/2012/12/office-365-waad-graph-api.html
 ※ちょうど一年前の Advent Calendar 向けの記事

例えば、特定のユーザに Google Apps など WAAD と統合されたアプリケーションを使わせたい、と考えた場合はこの Graph API で、対象ユーザへアプリケーション割り当てを行うことができれば、オンプレミスの ID 管理ツールから WAAD およびその先にあるサービスへのプロビジョニングまでをコントロールすることが出来そうです。

先月新バージョンがリリースされた Graph API の機能の概要を metadata エンドポイントを探索することで確認してみます。

2013/11/08 版 API)
 http://blogs.msdn.com/b/aadgraphteam/archive/2013/11/14/announcing-the-new-version-of-the-graph-api-api-version-2013-11-08.aspx


要するに WAAD と統合したアプリケーションに Graph API からアクセスできれば良いので、アプリケーションに関するどんな操作が可能かを調べてみます。

アクセストークンを WAAD の OAuth2.0 の Token エンドポイントから取得して Authorization ヘッダにセットした状態で、Graph API の metadata にアクセスしてみます。

metadata の確認は以下の URL へ GET します。
https://graph.windows.net/<テナントドメイン名>/#metadata?api-version=2013-11-08

すると、以下の答えが返ってきます。
{
    "name" : <テナントドメイン名>",
    "services" : [
       "https://graph.windows.net/<テナントドメイン名>/users",
       "https://graph.windows.net/<テナントドメイン名>/applications",
       "https://graph.windows.net/<テナントドメイン名>/contacts",
       "https://graph.windows.net/<テナントドメイン名>/groups",
       "https://graph.windows.net/<テナントドメイン名>/roles",
       "https://graph.windows.net/<テナントドメイン名>/servicePrincipals",
       "https://graph.windows.net/<テナントドメイン名>/tenantDetails",
       "https://graph.windows.net/<テナントドメイン名>/devices",
       "https://graph.windows.net/<テナントドメイン名>/subscribedSkus",
       "https://graph.windows.net/<テナントドメイン名>/permissions",
       "https://graph.windows.net/<テナントドメイン名>/directoryObjects"
    ]
}


Windows Server 2012 R2 から実装された Device Registration Service(DRS)に関するエンドポイントも存在しています。いよいよ WAAD 側でも DRS が実装されるようです。

今回はアプリケーション統合関係なので /appication というエンドポイントを見てみます。
まずは一覧を GET してみます。

https://graph.windows.net/<テナントドメイン名>/applications?api-version=2013-11-08

を GET すると、WAAD に統合したアプリケーションに関する情報が JSON 形式で出てきます。

が、ここで表示・管理できるのは、組織で開発中のアプリケーション、つまり Google Apps や Salesforce.com などの SaaS アプリケーション以外の自作アプリケーションや API に関してのみの様です。

これでは、特定のユーザを WAAD を経由して Google Apps を使わせる、ということが出来ません。


◆WAAD Premium のグループ管理機能の登場

Graph API で個別のユーザに直接 SaaS アプリケーションを割り当てることは難しそうです。
そこで登場するのが、現在プレビュー公開されている WAAD Premium の機能である、SaaS アプリケーションの割り当てを個人単位ではなく、グループ単位で実施する機能です。

これであれば、グループへのメンバ登録は Graph API で実行できるので、あらかじめ特定のグループを SaaS アプリケーションへ割り当てておけば、結果的にユーザに対してアプリケーションを割り当てることが出来そうです。

まずは、グループを作成し、アプリケーションへグループを割り当てます。


すると、グループメンバになっているユーザにアプリケーションが自動的に割り当てられます。(継承としてアサインされる)


どうやらうまくいきました。
Google Apps 上にもユーザが作成されています。

注1)現在の仕様では WAAD から Google Apps 上に新規作成されたユーザは「停止済み」状態になっているので、個別に有効化する必要があります)
注2)現状、グループ単位でのアプリケーション割り当ては Salesforce では使えません。


◆再度自動化にチャレンジ

ここまで管理画面から実行した操作を Graph API で自動化できればオンプレミスの ID 管理システムから WAAD を経由して SaaS アプリケーションを使える状態にする、というところまで持っていけそうです。

ここは実は非常に簡単で、対象のグループの URI に対してメンバとして参加させたいユーザの URI をリンクする、という操作で実現できます。

エンドポイント:https://graph.windows.net/<テナントドメイン名>/groups/<グループのオブジェクトID>/$links/members?api-version=2013-11-08
メソッド:POST
ボディ:
{
"url":"https://graph.windows.net/<テナントドメイン名>/users/<ユーザのオブジェクトID>"
}

これで、先ほど手動で実施したユーザのグループへの追加(結果としてアプリケーションへの割り当て)が自動化できました。
グループへの追加の条件をオンプレミスの ID 管理ツールで調整してやれば、割り当て後のユーザの状態など少々課題は残りますが、かなり柔軟にアプリケーションへのアクセス制御が実現できそうです。

いかがでしょうか?
IdMaaS というと全面的にアイデンティティ情報をクラウドに預けてクラウド上でのみ ID 管理をする、というイメージがあるかもしれませんが、このようにうまくオンプレミスと連携することで、効率の良い環境構築が出来る可能性がある、と言うことが出来ますので、今後 SaaS アプリケーションを企業内から活用する、というシーンを想定する場合はこのようなハイブリッド ID 管理シナリオも検討してみる価値はあるかも知れません。

2013年12月7日土曜日

[WAAD]アップデートラッシュ

最近、Windows Azure Active Directory(WAAD) 周りの機能がどんどんアップデートされています。
例えば、
・アプリケーションアクセス機能の正式リリース
・Graph API のバージョンアップ
・WAAD Premium のプレビュー版のリリース
など、楽しい?機能がたくさん登場してきています。

ちょうど今月は昨年に引き続き Windows Azure Advent Calendar に参加する予定なので、徐々にネタ探しをしているのですが、今回はウォーミングアップということで軽く小ネタを。

先にあげたアップデートのうち、アプリケーションアクセス機能については、以前のポストでも紹介しましたが、2つほど小ネタを紹介します。

一つ目は、アプリケーションパネルへのアクセス URL です。

前回のポストでは以下のような長い URL を紹介していました。
 https://account.activedirectory.windowsazure.com/applications

しかし、結構長いのでエンドユーザに展開するのが面倒、という方には以下の短い URL を使ってもアクセスできるのでこちらがおススメです。
 https://myapps.microsoft.com


次に、利用者がアプリケーションパネルを使っている最中に管理者が新しいアプリケーションを割り当てたらどうなるのか?という話です。
ログアウト~再ログインするまでアプリケーションが使えないのか、単純にページをリロードすれば使えるようになるのか???

答えは単純にリロードすれば OK です。

この状態で、、、


管理者がアプリケーションにユーザを割り当てると、、、


ページをリロードすると新しいアプリケーションが表示されます。



それだけです。
ほんとに小ネタでした。


2013年11月29日金曜日

[FIM2010/WAAD]新管理エージェントがまとめてリリース

ちょうど MVP Global Summit という年一回の世界中の MVP の集いに参加するためにシアトルのマイクロソフト本社を訪問している最中、Forefront Identity Manager 用の新しい管理エージェント(Management Agent / MA)がまとめてリリースされました。



今回リリースされたのは以下の3つです。



Generic LDAP コネクタを使うことで、これまでコミュニティベースで公開されていた ECMA 1.0 ベースの OpenLDAP XMA や、ここにきてようやく MA 名が Sun から Oracle に変更された旧 Sun の Directory Server、NetIQ(Novell)の eDirectory 用の管理エージェントを置き換えることが出来ます。

SharePoint コネクタに関しては SharePoint 標準の User Profile Service では出来ないような複雑な属性制御などが出来るようになります。

そして、やはりこれが目玉だと思いますが、Windows Azure Active Directory(WAAD)コネクタです。ディレクトリ同期ツールでは対応できなかったマルチフォレスト構成のオンプレミス AD との同期や、複数のデータソースからのアイデンティティ情報を WAAD に同期することが出来ます。


2013年11月21日木曜日

[FIM2010]小ネタ:色々な FIM

Forefront Identity Manager(FIM)が MS 製品の色々なところで使われている、という話はこれまでもしてきました。(SharePoint や Office365 のディレクトリ同期)

特に最近はディレクトリ同期ツールのビルド番号が完全に FIM と一致しており、ソースツリーとしては同じものになっていると推測されます。(おそらく SharePoint 2013 も Service Pack 1 が出るタイミングで合わせてくるものと思われます)

 参考)以前のポスト:[FIM2010]ついにディレクトリ同期ツールにビルド番号が追いつきました
 http://idmlab.eidentity.jp/2013/06/fim2010.html


ただ、このような状況だと本当にライセンス提供を受けている本家 FIM を使っているのか、無償ダウンロード可能なディレクトリ同期を使っているのかの区別がつかなくなってしまうため、Product ID というもので区別をしているようです。

Synchronization Service の Identity Manager(miisclient.exe)を起動し、[Help] - [About] で表示されるバージョン情報のダイアログの中の Product ID の 2 つ目のブロックが製品区分です。

- 270 : ボリュームライセンス
- 335 : MSDN
- 849 : 評価版
- 442 : ディレクトリ同期ツール

出典:現 MS Premier Support(元 FIM MVP)の Peter Green 氏の blog 「IdentityUndergroud」
http://identityunderground.wordpress.com/2013/11/20/which-fim-software-version-are-you-running/


実際に見てみました。
これがボリュームライセンス版です。


これがディレクトリ同期ツールです。


おまけですが、SharePoint 2013 です。
Product ID が空白になっています。(まだ FIM 2010 も無印です)


このように、SharePoint やディレクトリ同期ツールも FIM を組み込んで使っているので、FIM の更新情報(バグフィックス)なども見ておくと、色々とヒントがあるかも知れませんね。

[WAAD/Office365]Vittorio Bertocci氏来日

今週はシアトルの Microsoft 本社に来ています。

Windows Azure Active Directory(WAAD) や Active Directory Federation Service(AD FS)、Windows Identity Foundation(WIF) など Microsoft のアイデンティティ・テクノロジーに興味のある人には朗報です。

年明け 1/14 ~ 1/15 に開催される Japan Identity & Cloud Summit(JICS)に Windows Azure Active Directory の Principal Program Manager の Vittorio Bertocci 氏が来日・登壇してくれることになりました。

 おなじみ Vittorio の Blog
 http://www.cloudidentity.com/blog/



テーマや中身の詳細は現在詰めている最中ですが、Identity as a Service(IDaaS)について Microsoft の考えていることなどを中心に話をしてもらう予定です。


すでにイベントへのレジストは開始されているので、早めにお申し込みください。

 Japan Identity & Cloud Summit 公式サイト
 https://jics.nii.ac.jp/HOME/



私も Vittorio のセッションと同日の午前中に企業が ID・アクセス管理を行うための基盤を導入しようと思った際、まず何をすべきなのか?という初めの一歩的なセッションを担当する予定なので、よろしければそちらもどうぞ。

 1/15 10:00 ~ 11:00 「ID 基盤構築 101」
「OpenIDやSAML、プロビジョニングは確かに興味深いが、そもそも自社/自組織にID基盤が存在しないんだよね」「ちょうどこれからID基盤の整備をしようと思っているんだけどどこから手をつけたらいいのやら...」といった声を多く耳にします。本セッションでは、企業にとって必要なID基盤構築のとっかかりから勘所についてご紹介していきます。

繰り返しますが、日本にいて Vittorio から直接話を聞くことができるのは非常にレアな機会ですので、是非お越しいただき、直接色々と話を聞いてみてください。

2013年11月9日土曜日

[WAAD] OpenID Connect Interop 5

Windows Azure Active Directory(WAAD)の OpenID Connect サポートについては 2012 年 6 月のWindows Azure チームの blog 「Reimagining Active Directory for the Social Enterprise」以降、Mike B. Jones さんや Vittorio Bertocci さんによって少しずつ情報が公開され、実装も進んできました。

私の先日の Post でもアクセストークンを取得する時に id_token が取得できていたりしていましたし、OAuth への対応状況の確認の際に responce_type に id_token を指定すると対応の気配が見えたりしていました。

で、遂に公の接続性テストの場に出てきました。某 OP や PingIdentity などに並んで Microsoft AAD が Participant に掲載されています。

 OC5:OpenID Connect Interop 5
 http://osis.idcommons.net/wiki/OC5:OpenID_Connect_Interop_5



尚、Mike Jones さんの公開している WAAD テナントの well-known を見ると OpenID Connect の構成情報やサポート状況がわかります。
https://login.windows.net/kauti.onmicrosoft.com/.well-known/openid-configuration

{
"issuer":"https://sts.windows.net/b4ea3de6-839e-4ad1-ae78-c78e5c0cdc06/",
"authorization_endpoint":"https://login.windows.net/b4ea3de6-839e-4ad1-ae78-c78e5c0cdc06/oauth2/authorize",
"token_endpoint":"https://login.windows.net/b4ea3de6-839e-4ad1-ae78-c78e5c0cdc06/oauth2/token",
"token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt"],
"jwks_uri":"https://login.windows.net/common/discovery/keys",
"response_types_supported":["code","id_token","code id_token"],
"subject_types_supported":["pairwise"],
"id_token_signing_alg_values_supported":["RS256"],
"microsoft_multi_refresh_token":true
}

ちなみに特別なテナントを使っているわけではなさそうなので、WAAD のテナントをお持ちの方は well-known を叩いてみると同様に情報を参照することができます。


今後も引き続き要ウォッチです。

2013年11月5日火曜日

[Office365] DirSync on DC!!

いつの間にか色々な機能がどんどん使えるようになってきたディレクトリ同期ツールですが、これまで何故かドメインコントローラ上へインストール出来ませんでした。

ベースとなっている Forefront Identity Manager(FIM)は普通にドメインコントローラへインストール出来るのに何故?という状態だったのですが、この度の更新(6553.0002)からドメインコントローラへのインストールがサポートされました。

これで小規模環境や検証環境でもサーバを2台用意しなくても済むようになったので、より手軽に導入が出来るようになりました。




とりあえずインストールしてみましたが、若干工夫が必要な部分もあるので注意が必要です。
(すみません、スクリーンショットとるのをミスって若干解像度荒いです)



まぁ当然このあたりは変わりようもなく、、、



と、ここまで来たら次は構成ウィザードですが、ここで構成ウィザードを始めるとダメなので、一旦インストーラを終了します。



ここで一旦ログオフ~再ログオンをします。
何をやっているかというと、FIM をインストールしたことのある人ならお分かりだと思いますが、この後の Synchronization Service への構成(管理エージェントのインポートなど)を行うには、インストール実行ユーザが FIMSYNCADMINS グループの権限を持っている必要があるため、インストーラがメンバ追加をした権限を有効化するために一旦ログオフして再ログオンする必要があるのです。

再ログイン後、デスクトップに出来ている「ディレクトリ同期の構成」を実行すると構成ウィザードが再開されるので、通常通りオンライン側の管理ユーザと企業ディレクトリの管理ユーザを入れて構成を進めると問題なくセットアップが完了します。




ベースの FIM のビルド番号はつい先日リリースされた 4.1.3479.0 でした。
もう完全に同じものになってしまっています。。。




2013年10月26日土曜日

[WAAD]OAuth で WebAPI を保護する ③

前回、前々回に引き続き Windows Azure Active Directory(WAAD)の OAuth 2.0 で WebAPI を保護する方法を紹介します。

これまでのポスト
 [WAAD]OAuth で WebAPI を保護する ①
 [WAAD]OAuth で WebAPI を保護する ②


前回までで環境は整ったので、今回は実際にアクセスしてみます。(今回でようやく終わりです)

◆作業の流れ
これまでの作業の流れは以下の通りです。今回は最後の 7 を実行します。


  1. WebAPI の作成:今回は ASP.NET MVC4 の WebAPI を使います
  2. WebAPI の保護:WAAD を使って認可する様に設定を行います
  3. WebAPI を WAAD に登録:WAAD の保護対象リソースとして WAAD へ登録します
  4. OAuth クライアントの作成:本来は真面目にクライアントも作るのですが、今回は生の動きを見るために Chrome Extension の Advanced REST Client とダミー URL を使います
  5. OAuth クライアントを WAAD に登録:WAAD を使うアプリケーションとして OAuth クライアントを登録します
  6. WebAPI へのアクセス許可設定:OAuth クライアントが WebAPI へアクセスできるように設定します
  7. 動作確認:実際に OAuth2.0 のフローに従ってアクセスできるかどうかテストします。(今回は認可コードフローを試してみます)



◆実際の作業

7.動作確認

実際に OAuth クライアントから WebAPI へアクセスします。OAuth の認可コードフローを使って WAAD からアクセスに必要なアクセストークンを取得するので、まずは OAuth クライアントの client_id と client_secret を確認します。
クライアント ID(client_id)は 管理ポータルのアプリケーションの構成の中にありますが、client_secret は初期状態では生成されていません。そこでキーの項目で有効期限を選んで生成をします。尚、作成後ページ遷移をするとそのキーは二度と参照できなくなるので、必ずコピーしておきます。万一忘れてしまった場合は再度キーを生成します。




これで実際にアクセスするための準備はすべて終了です。
もう一度おさらいですが、今回やりたいことをシーケンスに表わすと以下のような流れになります。



上記のシーケンスの通り、まずは WAAD の 認可エンドポイントへリクエストを投げます。

※今回はクライアントを作らずにブラウザと Chrome Extension の Advanced REST Client を使うので、リソースオーナー(ユーザ)がクライアントへアクセスしてきたと仮定して、クライアントがリソースオーナーへリダイレクトしてくる認可要求を手動で実行します。

投げるリクエストは以下の通りです。

・メソッド:GET
・エンドポイント:
 https://login.windows.net/common/oauth2/authorize?api-version=1.0
・パラメータ
 response_type : code
 client_id : 先に取得した Client の client_id

実際はブラウザから以下の URL へアクセスします。
 https://login.windows.net/common/oauth2/authorize?api-version=1.0&response_type=code&client_id=[client_id]


すると、WAAD の認可エンドポイントから認証要求が返ってきますので、WAAD 上に登録した組織のアカウントでログインします。(フローの①)


認証が通ると、再度認可エンドポイントへ遷移し、設定がうまくいっていれば認可コードが払い出されます。
クライアントがダミー(WAAD に登録したアドレスが適当に http://localhost)なので、ブラウザにはエラーが表示されますが、アドレスバーを見るとちゃんとコードがパラメータに入っていることがわかります。
これが上記フローの②に該当します。


これで認可コードが取得できたので、次は WAAD のトークンエンドポイントで認可コードとアクセストークンを交換してもらいます。
今度は POST メソッドを使うので Advanced REST Client を使います。

投げるリクエストは以下の通りです。

・メソッド:POST
・エンドポイント:
 https://login.windows.net/common/oauth2/token
・フォームデータ
 grant_type : authorization_code
 client_id : 先に取得した Client の client_id
 client_secret : 先に取得した Client の client_secret
 code : 取得したcode
 resource : WebAPI の URI(WAAD に登録した URI)


こんな感じで投げます。


うまくいけば HTTP 200 が返ってきて、結果にアクセストークンが返ってきます。フローの④の部分です。(ついでに id_token も返ってきてます)


ようやくこれで WebAPI へアクセスする準備が整いました。
あとは単純に、このアクセストークンを Authorization ヘッダにセットして WebAPI の URI へリクエストを投げるだけです。

投げるリクエストは以下の通りです。実際のアクセストークンの前に「Bearer 」を入れるのを忘れないでください。

・メソッド:GET
・エンドポイント:
 http://localhost:52941/Api/Values
・ヘッダ
 Authorization : Bearer [取得したアクセストークン]

こんな感じです。上記フローの⑤に当たります。


前々回で保護設定をした状態では HTTP 401 が返ってきていましたが、今回はちゃんと HTTP 200 が返ってきます。
データも取得できているようです。



いかがでしょうか?
概念的に OAuth2.0 を使ってリソースを保護する、と言うだけなら簡単なのですが、実際にステップ毎に実行してみると理解ができると思います。
また、Visual Studio 2013 からは OWIN という新しい認証の実装が入ってきていますが、内部的にはこんな感じで動いています(たぶん)。

参考)Understanding OWIN Forms authentication in MVC 5




2013年10月25日金曜日

[WAAD]OAuth で WebAPI を保護する ②

前回に引き続き Windows Azure Active Directory(WAAD)の OAuth2.0 で WebAPI を保護する方法を紹介します。

◆作業の流れ
作業の流れは以下の通りで、前回は 1 ~ 2 を紹介しましたので、今回は 3 からです。

  1. WebAPI の作成:今回は ASP.NET MVC4 の WebAPI を使います
  2. WebAPI の保護:WAAD を使って認可する様に設定を行います
  3. WebAPI を WAAD に登録:WAAD の保護対象リソースとして WAAD へ登録します
  4. OAuth クライアントの作成:本来は真面目にクライアントも作るのですが、今回は生の動きを見るために Chrome Extension の Advanced REST Client とダミー URL を使います
  5. OAuth クライアントを WAAD に登録:WAAD を使うアプリケーションとして OAuth クライアントを登録します
  6. WebAPI へのアクセス許可設定:OAuth クライアントが WebAPI へアクセスできるように設定します
  7. 動作確認:実際に OAuth2.0 のフローに従ってアクセスできるかどうかテストします。(今回は認可コードフローを試してみます)


◆実際の作業

3.WAAD への保護リソースの登録

ここまでで WebAPI へアクセスした時に渡されるアクセストークンを WAAD で確認するための WebAPI 側の設定を行いました。次は WAAD 側に保護対象リソースとして WebAPI を登録する作業です。
以下の手順で WAAD にアプリケーションを追加します。

まずは Windows Azure の管理ポータルにログインし、Active Directory から対象にしたいディレクトリの設定を開きます。すると、アプリケーションというメニューがあるので、そこから追加をしていきます。

追加するのは「組織で開発中のアプリケーション」です。

次にアプリケーションに任意の名前を付けて、種類に「WEB アプリケーションや WEB API」にします。

次にアプリケーションの URI を指定します。ここには先ほどの WebAPI の URI を指定します。(今回は Visual Studio からデバッグするときの URL(http://localhost:xxxx)を使います。


WAAD のディレクトリへのアクセス権限を設定します。
今回は特にディレクトリ上のデータへのアクセスは不要で、単に認証さえ出来ればよいので、「シングルサインオン」にチェックを入れておきます。


これで作成した WebAPI の WAAD への登録は完了です。


4.OAuth クライアントの登録~5.WebAPI を WAAD に登録

次は WebAPI にアクセスする OAuth クライアントです。

保護リソースへアクセスするための OAuth クライアント(実際はこれも Web アプリケーションやネイティブアプリケーション)についても WAAD が認識している必要があるので、WAAD 上へ登録します。
今回はアクセスの流れと WebAPI の保護を見るだけなので、実際にクライアント・アプリケーションを作らずに Chrome Extension の Advanced REST Client を使いますので、WAAD 上にはダミーの URL でアプリケーションを登録します。


先ほどの WebAPI の時と同じように登録を開始します。

URI には適当な値を入れておきます。今回は「http://localhost」としています。OAuth2.0 の認可エンドポイントから認可コードを取得するときにこの URI にフラグメントとしてコードがくっついてくるので、実際にアクセスできる URI である必要はなく、404 Not Found であろうとブラウザのアドレスバーからコードが取得できる、という寸法です。



ディレクトリアクセスについては特には必要ないので、「シングルサインオン」としておきます。


6.WebAPI へのアクセス許可設定

次は、先ほど作った WebAPI に対して OAuth クライアントがアクセスできるようにするアクセス許可設定を行います。
現状では Preview 機能ですが、アプリケーション構成の中に「クライアントアクセス」があるので、ここでクライアントとなれるアプリケーションを設定します。プルダウンに先ほど作った OAuth クライアントが出てくるので、そちらを選択して保存します。






さて、実はこれで設定は基本的には終わりです。

次回は実際にアクセスをして、動きを確認したいと思います。

2013年10月22日火曜日

[WAAD]OAuth で WebAPI を保護する ①

Windows Azure Active Directory(WAAD)が OAuth2.0 / OpenID Connect に対応しつつある、という話は以前紹介しましたが、具体的にどうやって使うのか?についてはあまり解説記事もないので、今回簡単に紹介していこうと思います。
(一部、Preview の機能を使うので今後手順などが変更になる可能性があります)

参考)[WAAD] OAuth2.0 への対応状況まとめ&ちょこっと OpenID Connect も
    http://idmlab.eidentity.jp/2013/07/waad-oauth20-openid-connect.html

◆やりたいこと
WebAPI へのアクセスを WAAD の OAuth2.0 の機能を使って保護する
⇒つまり、WAAD が発行したアクセストークンを使って WebAPI の実行の認可をしてみようと思います。

簡単に絵にすると以下のようになります。


ポイントは、ユーザが直接 WebAPI を実行するのではなく、OAuth クライアントが実行する、かつその際に OAuth クライアントに ID/PWD を設定しておかなくてもユーザの代理として機能する、というところです。


◆作業の流れ
作業の流れは以下の通りです。
  1. WebAPI の作成:今回は ASP.NET MVC4 の WebAPI を使います
  2. WebAPI の保護:WAAD を使って認可する様に設定を行います
  3. WebAPI を WAAD に登録:WAAD の保護対象リソースとして WAAD へ登録します
  4. OAuth クライアントの作成:本来は真面目にクライアントも作るのですが、今回は生の動きを見るために Chrome Extension の Advanced REST Client とダミー URL を使います
  5. OAuth クライアントを WAAD に登録:WAAD を使うアプリケーションとして OAuth クライアントを登録します
  6. WebAPI へのアクセス許可設定:OAuth クライアントが WebAPI へアクセスできるように設定します
  7. 動作確認:実際に OAuth2.0 のフローに従ってアクセスできるかどうかテストします。(今回は認可コードフローを試してみます)
ちょっと長めなので、2,3回に分割して紹介していきます。


◆実際の作業
では、始めます。

1.WebAPI の作成

Visual Studio 2012 で以下の通り WebAPI を作成します。ここで作成した WebAPI へのアクセスを Windows Azure Active Directory(WAAD)の OAuth を使って保護します。

まずは、ASP.NET MVC4 Web アプリケーションを作成します。



プロジェクトテンプレートでは WebAPI を選択します。



作成が終わったら F5 を押してデバッグモードで起動します。
ブラウザで http://localhost:[アサインされたポート番号]/Api/Values へアクセスすると Visual Studio のテンプレートに登録されている値が表示されます。


2.WebAPI の保護

作成した WebAPI を WAAD で保護するための設定を行います。具体的には Authorization ヘッダに設定されてくるアクセストークンの Validation をするために、global.asax にValidationHandler を作成、登録します。これでトークンの Validation に失敗すると HTTP 401 Unauthorized が返るようになります。

まず必要なライブラリへの参照を設定します。必要なのは、以下の3つです。
  • System.IdentityModel
  • JSON Web Token Handler for the Microsoft .NET Framework(NuGet パッケージ)
  • Microsoft Token Validation Extension for Microsoft .NET Framework 4.5(NuGet パッケージ)
System.IdentityModel

JSON Web Token Handler for the Microsoft .NET Framework

Microsoft Token Validation Extension for Microsoft .NET Framework 4.5



参照設定が終わったら、global.asax へ TokenValidationHandler を追加します。
この部分は MSDN の以下のサイトに紹介されているので、global.asax のソースコードはそのまま使います。
 Securing a Windows Store Application and REST Web Service Using Windows Azure AD (Preview)

環境によって変えるのは、以下の2点だけです。
 const string domainName = “xxx.onmicrosoft.com";
  ※契約した WAAD のテナントドメイン名
 const string audience = “http://localhost:[ポート番号]";
  ※作成した WebAPI の URI


一応ソースを張り付けておきます。

using System;
using System.Collections.Generic;
using System.IdentityModel.Metadata;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel.Security;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using System.Xml;
using System.Xml.Linq;

namespace ProtectedAPI
{
    // メモ: IIS6 または IIS7 のクラシック モードの詳細については、
    // http://go.microsoft.com/?LinkId=9394801 を参照してください

    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            // Add Token Validation Handler -- 20131020
            GlobalConfiguration.Configuration.MessageHandlers.Add(new TokenValidationHandler());
        }
    }

    // Token Validation Handler Class -- 20131020
    internal class TokenValidationHandler : DelegatingHandler
    {
        // Domain name or Tenant name
        const string domainName = "xxxx.onmicrosoft.com";
        const string audience = "http://localhost:52941";

        static DateTime _stsMetadataRetrievalTime = DateTime.MinValue;
        static List<X509SecurityToken> _signingTokens = null;
        static string _issuer = string.Empty;


        // SendAsync is used to validate incoming requests contain a valid access token, and sets the current user identity 
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            string jwtToken;
            string issuer;
            List<X509SecurityToken> signingTokens;

            if (!TryRetrieveToken(request, out jwtToken))
            {
                return Task.FromResult<HttpResponseMessage>(new HttpResponseMessage(HttpStatusCode.Unauthorized));
            }

            try
            {
                // Get tenant information that's used to validate incoming jwt tokens
                GetTenantInformation(string.Format("https://login.windows.net/{0}/federationmetadata/2007-06/federationmetadata.xml", domainName), out issuer, out signingTokens);
            }
            catch (Exception)
            {
                return Task.FromResult<HttpResponseMessage>(new HttpResponseMessage(HttpStatusCode.InternalServerError));
            }

            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler()
            {
                CertificateValidator = X509CertificateValidator.None
            };

            TokenValidationParameters validationParameters = new TokenValidationParameters
            {
                AllowedAudience = audience,
                ValidIssuer = issuer,
                SigningTokens = signingTokens
            };

            try
            {

                // Validate token
                ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwtToken,
validationParameters);

                //set the ClaimsPrincipal on the current thread.
                Thread.CurrentPrincipal = claimsPrincipal;

                // set the ClaimsPrincipal on HttpContext.Current if the app is running in web hosted environment.
                if (HttpContext.Current != null)
                {
                    HttpContext.Current.User = claimsPrincipal;
                }

                // Verify that required permission is set in the scope claim
                if (ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope").Value != "user_impersonation")
                {
                    return Task.FromResult<HttpResponseMessage>(new HttpResponseMessage(HttpStatusCode.Unauthorized));
                }

                return base.SendAsync(request, cancellationToken);
            }
            catch (SecurityTokenValidationException)
            {
                return Task.FromResult<HttpResponseMessage>(new HttpResponseMessage(HttpStatusCode.Unauthorized));
            }
            catch (Exception)
            {
                return Task.FromResult<HttpResponseMessage>(new HttpResponseMessage(HttpStatusCode.InternalServerError));
            }
        }

        // Reads the token from the authorization header on the incoming request
        private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
        {
            token = null;
            string authzHeader;

            if (!request.Headers.Contains("Authorization"))
            {
                return false;
            }

            authzHeader = request.Headers.GetValues("Authorization").First<string>();

            // Verify Authorization header contains 'Bearer' scheme
            token = authzHeader.StartsWith("Bearer ") ? authzHeader.Split(' ')[1] : null;

            if (null == token)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Parses the federation metadata document and gets issuer Name and Signing Certificates
        /// </summary>
        /// <param name="metadataAddress">URL of the Federation Metadata document</param>
        /// <param name="issuer">Issuer Name</param>
        /// <param name="signingTokens">Signing Certificates in the form of X509SecurityToken</param>
        static void GetTenantInformation(string metadataAddress, out string issuer, out List<X509SecurityToken> signingTokens)
        {
            signingTokens = new List<X509SecurityToken>();

            // The issuer and signingTokens are cached for 24 hours. They are updated if any of the conditions in the if condition is true.            
            if (DateTime.UtcNow.Subtract(_stsMetadataRetrievalTime).TotalHours > 24
                || string.IsNullOrEmpty(_issuer)
                || _signingTokens == null)
            {
                MetadataSerializer serializer = new MetadataSerializer()
                {
                    CertificateValidationMode = X509CertificateValidationMode.None
                };
                MetadataBase metadata = serializer.ReadMetadata(XmlReader.Create(metadataAddress));

                EntityDescriptor entityDescriptor = (EntityDescriptor)metadata;

                // get the issuer name
                if (!string.IsNullOrWhiteSpace(entityDescriptor.EntityId.Id))
                {
                    _issuer = entityDescriptor.EntityId.Id;
                }

                // get the signing certs
                _signingTokens = ReadSigningCertsFromMetadata(entityDescriptor);

                _stsMetadataRetrievalTime = DateTime.UtcNow;
            }

            issuer = _issuer;
            signingTokens = _signingTokens;
        }

        static List<X509SecurityToken> ReadSigningCertsFromMetadata(EntityDescriptor entityDescriptor)
        {
            List<X509SecurityToken> stsSigningTokens = new List<X509SecurityToken>();

            SecurityTokenServiceDescriptor stsd = entityDescriptor.RoleDescriptors.OfType<SecurityTokenServiceDescriptor>().First();

            if (stsd != null && stsd.Keys != null)
            {
                IEnumerable<X509RawDataKeyIdentifierClause> x509DataClauses = stsd.Keys.Where(key => key.KeyInfo != null && (key.Use == KeyType.Signing || key.Use == KeyType.Unspecified)).
Select(key => key.KeyInfo.OfType<X509RawDataKeyIdentifierClause>().First());

                stsSigningTokens.AddRange(x509DataClauses.Select(clause => new X509SecurityToken(new X509Certificate2(clause.GetX509RawData()))));
            }
            else
            {
                throw new InvalidOperationException("There is no RoleDescriptor of type SecurityTokenServiceType in the metadata");
            }

            return stsSigningTokens;
        }
    }
}



この状態で再度デバッグ実行し、ブラウザからアクセスするとAuthorizationヘッダがないため、401 Unauthorizedが返ってきます。



とりあえず、今回は WebAPI を作るところまでを紹介しましたので、次回は WAAD で実際に保護するための設定を入れていきます。