Azure AD を使った API 連携の Client 開発 (OAuth 2.0 紹介)

※ Azure AD v1 endpoint に関する内容です (v2 endpoint の場合は、こちら を参照してください)

開発者にとっての Microsoft Azure Active Directory

こんにちは。

例えば、Facebook と連携して動作するスマホ アプリでは、Facebook で認証をおこなって、認証後は Facebook の友達リストや投稿内容の取得など API (Service) とつながった形で動作します。(このように、一般に、Mobile アプリと API は密接な関係にあります。) iOS、Android、Windows などの Native Application から Azure AD を使用するシナリオでも、ここで紹介する OAuth 2.0 を使うことで、認証をおこない、認証結果として Azure AD にぶらさがる他の API (Service) と連携して動作できます。
本投稿では、この Azure AD における OAuth 2.0 のフローを紹介します。

この方法では、Application (Native アプリ、Web アプリ) は、Azure AD 上の Service (Azure AD によるログインが必要な Service) に接続可能なトークンを受け取り、以降、このトークンを使って Service の処理を呼び出します。
Mobile が爆発的に普及した今、Cookie をはじめとするブラウザー技術のみに頼った連携方法より、むしろここで述べる API 連携をベースとした方法のほうが重要になりつつあります。プログラミングの前に、まずは、その Flow を理解しておきましょう。(Flow がわかれば、あとはそれをプログラム言語に落とすだけです。)

 

概要 (シナリオ) と HTTP Flow

今回のサンプルは、UI を持つ Windows, iOS, Android などの ネイティブ・アプリケーション (TestApp1) が、Azure Active Directory (AAD) にログインをおこなって、Azure Active Directory (AAD) で保護されたサービス (TestApp2) と連携して動作します。
例えば、Native Application から Office 365 のサービス (Exchange Online, SharePoint Online など) にアクセスする場合は、ネイティブ・アプリケーション (TestApp1) が皆さんが構築するアプリケーションに相当し、サービス (TestApp2) は Exchange Online や SharePoint Online などです。

大まかな設定と開発の流れは、以下の通りです。

  1. TestApp2 を Azure Active Directory の Application (Relying Party) として登録します
  2. TestApp1 を Client Application として Azure Active Directory に登録し、TestApp2 の Resource にアクセスするための Permission を設定します
  3. TestApp1 を構築 (プログラミング) します

まず、上記 3 の HTTP Flow を紹介します。(上記 1, 2 については後述します。)

今回は、Azure Active Directoryの「o365demo01.onmicrosoft.com」のテナントに登録されている「TestApp1」(URLは「http://localhost/testapp1」)から、同じテナントの「TestApp2」(URL/リソース名は「http://localhost/testapp2」)の API(REST API など)を呼び出します。
この場合、まず、Native Application (Windows, iOS, Android, など) の Web ブラウザー・コンポーネントなどを使い、下記のようなURLにアクセスします。(CLIENT ID は、Azure AD から取得します。)

GET https://login.microsoftonline.com/common/oauth2/authorize?response_type=code&client_id=5bbe4005-5e99-4a51-9846-f0dd9a02549a&resource=http%3a%2f%2flocalhost%2ftestapp2&redirect_uri=http%3a%2f%2flocalhostf%2ftestapp1

補足 : 上記で「http%3a%2f%2flocalhost%2ftestapp2」は「http://localhost/testapp2」、「http%3a%2f%2flocalhost%2ftestapp1」は「http://localhost/testapp1」の URL Encode 文字列です。(以降も同様)

補足 : 下記の通り、「common」の代わりにテナント ドメインを使ってアクセスしても構いません。(以降も同様)
GET https://login.microsoftonline.com/o365demo01.onmicrosoft.com/oauth2/authorize?response_type=code&client_id=5bbe4005-5e99-4a51-9846-f0dd9a02549a&resource=http%3a%2f%2flocalhost%2ftestapp2&redirect_uri=http%3a%2f%2flocalhost%2ftestapp1

上記の URI にアクセスすると、Azure Active Directory のいつものログイン画面が表示されます。そして、ログイン画面で認証が完了すると、Azure Active Directory は下記の URL にリダイレクトします。

http://localhost/testapp1?code=AgAAAAAAjYxaWEmhi_qYPxQYHmw...

補足 : この Flow では、code が query string で返ってきている点に注意してください。このため、HTTP を使用すると、SSL (https) の設定に関わらずネットワーク上を code が流れる結果となります。(JavaScript、AngularJS などで使用する場合は、下記の Implicit Grant Flow を使用してください。)

補足 (2014/10/29 追記) : OAuth の Implicit Grant Flow もサポートされました。Implicit Grant Flow を使用するには、あらかじめ、manifest をダウンロードして、oauth2AllowImplicitFlow プロパティを true に変更し、アップロードしなおしてください。(manifest の編集方法については「Azure Active Directory の Common Consent Framework (Client 側)」を参照してください。)

Native Application (Windows, iOS, Android, など) は上記の code を取得し、引き続き、以下の HTTP POST メソッドを使って OAuth の Access Token を取得します。

POST https://login.microsoftonline.com/common/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=AgAAAAAAjYxaWEmhi_qYPxQYHmw...&client_id=5bbe4005-5e99-4a51-9846-f0dd9a02549a&redirect_uri=http%3A%2F%2Flocalhost%2Ftestapp1

補足 : 下記の通り POST することで、SignIn (ログイン) 画面を表示せずに ID / Password を渡して access token を取得できます。
ただし、この場合、二要素認証 (多要素認証)、AD フェデレーションなど、Azure AD を使ったさまざまなオプションに対応できなくなるのでお勧めはしません。(使用は限定的にしてください。)
grant_type=password&resource=http%3a%2f%2flocalhost%2ftestapp2&client_id=5bbe4005-5e99-4a51-9846-f0dd9a02549a&client_secret=GZOT2mNo%2F9%2B…&username=<user id>&password=<password>

以下の Response が返ってきます。

{
  "access_token":"eyJ0eXAiOiJKV1. . .",
  "token_type":"Bearer",
  "expires_in":"28800",
  "expires_on":"1373631999",
  "multi_resource":true,
  "refresh_token":"AgAAAAAAZQxwv. . .",
  "scope":"user_impersonation"
}

補足 : Access Token には User Context を含むトークンと、含まないトークンがあります。(この方法で取得される Access Tokenは、User Context を含んだトークンです。)
User Context を含まない Token (App-Only Token) を使用した Flow については「Azure AD : Backend Server-Side アプリの開発 (Deamon, Service など)」に記載しましたので参照してください。

あとは、この Access Token をヘッダーに設定して、Azure AD 上で保護されたサービス (http://localhost/testapp2) を呼び出します。

GET http://localhost/testapp2/testapi

Authorization: Bearer eyJ0eXAiOiJKV1...

受け取ったサービス側 (この例の場合、http://localhost/testapp2) では Access Token を検証するための処理 (フロー) を作成します。
これについては、後述します。

なお、アカウントから Sign Out (logout) をおこなって、ブラウザーに残った Cookie などをクリアする場合には、下記のとおり https://login.microsoftonline.com/common/oauth2/logout?post_logout_redirect_uri=<redirect 先> にアクセスします。(logout が完了すると、post_logout_redirect_uri に戻ってきます。)

https://login.microsoftonline.com/common/oauth2/logout?post_logout_redirect_uri=http%3A%2F%2Flocalhost%2Ftestapp1

 

こんなときには、Access Token の再取得

Access Token には、接続先のサービス (Resource) の情報が入っています。つまり、同じネイティブ アプリケーション (同一ユーザー) で別のサービス (Resource) に接続する際には、別の Access Token を取得しなおす必要があります

Access Token を再度取得する際は、わざわざ上記のログイン フローを実装する必要はありません。上記で Response として返された Refresh Token を使用して下記の通り Request をおこなうと、別の Access Token の取り直しが可能です。(SignIn (ログイン) 画面は表示されません。)

POST https://login.microsoftonline.com/common/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token={received refresh token}&client_id={client id}&resource={another resource id}

また、Access Token の有効期限は、既定で 1 時間です。Access Token が期限切れになった場合にも、上記の通り Refresh Token を使って Access Token を取得しなおします。

補足 : この token lifetime の初期設定が変更できるようになりました。詳細は「Active Directory team blog : Configurable token lifetimes in Azure AD are now Public Preview !」を参照してください。(2016/10 追記)

補足 : refresh token は既定で最大 14 日使用可能で、再取得することで、さらに 14 日間使用できます。(最大で 90 日まで延長できます。)
-> 2017/08 以降、新規に作成されたテナントについては、既定で 90 日間使用可能 (再取得することで、さらに 90 日間延長) で、Revoke されるまで無制限に使用できる設定になっています。(既存テナントでこの設定にするには、PowerShell を使って設定変更してください。) 詳細は「Team Blog : Changes to the Token Lifetime Defaults in Azure AD」を参照してください。

補足 : また、password を変更すると、refresh token は無効になります。(refresh token の再取得が必要です。)

 

Application の Azure AD への登録と Permission 設定

前述の通り、Client Application (上記の TestApp1) と Service Application (上記の TestApp2) の双方は、あらかじめ、Azure AD に登録しておく必要があります。
なお、Service Application として Office 365 など Microsoft が提供するサービスにアクセスする場合には、既に Azure AD に Exchange Online、SharePoint Online などのサービスが登録されているので、これらの Service Application の登録は不要です。(Client Application の登録は必要です。)

登録方法は簡単です。
Azure Portal にログインして、ナビゲーションから [Azure Active Directory] を選択して、[App Registrations] メニューを選択し、表示される画面で[New application registration] を押せば登録できます。

Service Application の場合、登録後に「App ID URI」のプロパティを確認してください。これが、上述の resource の名前になります。(好みの名前に変更できます。)
また、Client Application (Native Application) を登録する際は、[Redirect URI] として上記フローで使う redirect_uri を指定します。(この指定した URL 以外にリダイレクトさせることはできません。) なお、[Redirect URI] は複数登録できますが、https://localhost/* のように * (アスタリスク) も使用できます。

登録が完了した後、アプリを表示すると、上記で使用する Client ID が [Application ID] として表示されるので、この値をコピーしておきます。

さいごに Permission 設定をおこないます。
作成した Client Application の [Required permissions] メニューを選択して表示される画面の [Add] ボタンを押します。表示される画面で Service Application (今回の場合、「TestApp2」) を選択して追加し、[DELEGATED PERMISSIONS] (デリゲートされたアクセス許可) 欄のチェックを付けて保存します。(Service Application が Office 365 の場合は、Office 365 Exchange Online、Office 365 SharePoint Online などを選択して、[Read user’s mail]、[Edit or Delete user’s file] など、操作にあわせた Delegated Permission を選択します。)

ここでは Azure Portal を使用して Application を登録しましたが、Application を Multi-Tenant 対応で構築することで、End User は わざわざ管理画面から Application を登録する必要はありません。(つまり、上記は、アプリケーション提供者、もしくは開発者のみがおこなえば OK です。)
この手順については、「Azure Active Directory の Common Consent Framework (Client 側)」に解説しましたので参考にしてください。

 

Native Client (Public Client) と Web Client (Confidential Client)

上記で Azure AD に Client を登録する際、下図のように [Web app / API] と [Native] を選択しますが、この選択は慎重におこなってください。

上記のフローから分かるように、Client Id などが分かると Client のなりすましをされる可能性があるため、URL リンクなどで簡単になりすませる Web Application Client の場合は、Access Token を取得する際に Client Secret と呼ばれる秘匿情報を使用し、サーバー サイドで処理するなどして、この Client Secret がわからないようにします。
Client Secret は、Client Application の [Configure] (構成) タブを選択して表示される画面で、[Key] の入力欄の有効期限 (1 年、または 2 年) を設定して [保存] ボタンを押すと下図の通り生成されます。(この値は、最初の保存時以降は見えなくなるので、作成時に必ずコピーしておいてください。)

補足 : Client Secret (Client の Password) を使わず、証明書 (Certificate) を使って保護することも可能です。
証明書を使った方法は「Azure AD : Backend Server-Side アプリの開発」(ここでは Application 権限でログインしていますが、User 権限で証明書を使うことも可能です) に手順を記載しましたので参照してください。

一方、Native Client Application のようにユーザー端末に配布されるものは Client Secret は使用しません。Native Client Application で Web Client Application のコードを流用して Client Secret を埋め込んでしまうと、コードを解析などされて Client Secret を盗まれる危険があるので注意してください。(Client Secret が盗まれると、この Secret を使っているすべての Client が危険にさらされます。)
Azure Active Directory の Common Consent Framework (Client 側)」で述べるように、ユーザーがはじめて Client Application を使用する際には意志確認 (Consent UI の表示) がおこなわれますが、Native Client Application では上記フローで Access Token を取得するたびに必ず意思確認をおこなうようになっており、このため通常は、取得した Refresh Token を Application でずっと保持 (キャッシュ) しておき、以降は、この Refresh Token から Access Token を取得します。(このようにすると、2 回目以降の Access Token 取得の際に意識確認はおこなわれません。)

2016/03 追記 : Native Client Application も、一度「Azure Active Directory の Common Consent Framework (Client 側)」で述べる意思確認 (Consent UI の表示) がおこなわれると、以降は Azure AD Graph に Consent 情報が記録されるようになりました。この仕様変更については「Azure AD Graph team blog : Azure AD will now record consent for mobile client apps」を参照してください。

 

Client Application のプログラミング

以上で事前準備 (セットアップ) は完了です。
あとは、上述の HTTP Flow を各言語で実装すれば OK です。(TestApp1 のプログラミングをおこないます。)

複雑なフローではないため、ブラウザーやブラウザー コンポーネント (WebView, UIWebView など) と連携できれば、あとはさまざまなプログラミング言語を使ってフローが実装できます。(PHP や Node.js などを使用した実装例を こちら に掲載しました。)

実は、.NET など一部の言語では、上記の流れをすべて自力で構築しなくて良いよう、Azure Active Directory Authentication Library (ADAL) というライブラリーが提供されています。
ここでは C# を例に、この Active Directory Authentication Library (ADAL) を使用してみましょう。(以下では、WPF アプリケーションとして構築します。)

補足 : 現在、Windows Store App 用、Windows Phone App 用、iOS 用Android 用 の ADAL がリリースされています。

Visual Studio で WPF アプリケーションを新規作成したら、NuGet で Active Directory Authentication Library (ADAL) をインストールします。

ボタンを配置して、ボタン クリック時のイベントとして下記の通りプログラミングします。

. . .
using System.IdentityModel.Clients.ActiveDirectory;
using System.Net;
. . .

private void Button_Click(object sender, RoutedEventArgs e)
{
  // get token (including app and user context)
  AuthenticationContext authCtx =
    new AuthenticationContext("https://login.microsoftonline.com/common");
  AuthenticationResult authRes = authCtx.AcquireToken(
    "http://localhost/testapp2",
    "5bbe4005-5e99-4a51-9846-f0dd9a02549a",
    new Uri("http://localhost/testapp1"));

  // call service application using token (auth code)
  HttpWebRequest request =
    WebRequest.Create("http://localhost/testapp2/api/test") as HttpWebRequest;
  request.Method = "GET";
  request.Headers["Authorization"] = authRes.CreateAuthorizationHeader();
  request.ContentType = "application/json";
  HttpWebResponse response = request.GetResponse() as HttpWebResponse;

  // skip code from here ...
}

このアプリケーションを実行すると、上記の AcquireToken の箇所で、下図の通り Office 365 へのテナント (今回の場合、o365demo01.onmicrosoft.com) のログイン画面が表示されます。

また、最新の ADAL では、token が期限切れになった場合、AcquireTokenByRefreshToken を使用して新しい token を取り直すこともできます。

補足 : ADAL では、取得した access token や refresh token を cache しています。例えば、AcquireToken を使って、ある resource の access token を取得したあとで、再度、AcquireToken を使用して別の resource の access token を取得する場合、内部で前述の方法を使って、最初に取得した refresh token を使用して access token を取得します。(つまり、2 回目は SignIn 画面は表示されません。)

 

Access Token の検証と属性取得

OAuth や OpenID Connect で受け取った Access Token (または Id Token) が「正しいか ?」、「改竄されていないか ?」の検証をおこなう場合、「Azure AD : Service 開発 (access token の verify)」で記載している方法でデジタル署名を確認します。(ADAL などのライブラリーの内部では、こうした検証をおこなっています。)

また、Access Token (Id Token) が正しいと判断された場合、「Azure AD : Service 開発 (access token の verify)」で記載している方法で、Token から氏名、メールアドレス、ユーザーの識別子、テナントの識別子などの基本情報を (Claim として) 取得できます。

今回はサービスを利用する Client アプリですが、サービス側のアプリを開発する場合には、こうした確認は必須です。

補足 : サービス側では、受け取った Access Token を使って、(ログイン画面を表示せずに) さらに別のサービス (resource) にアクセスすることもできます。
この方法については、「OAuth on_behalf_of の利用 (Azure AD)」にわけて記載しましたので参照してください。

 

なぜ、この方式なのか ?

個別のアプリから認証・認可をおこなう方法はいくつか考えられますが、この OAuth 2 の方式は、従来技術と比べ、いくつかの利点があります。
1つは、前述したモバイルとの親和性の高さです。昨今、どの Native 環境でも何某かのブラウザー コンポーネント (WebView, UIWebView など) が提供されていますが、面倒な手続きはこのブラウザー コンポーネントに任せ、アプリ側は前述した比較的容易な OAuth 2 のフローのみを実装すれば良く、幅広いプラットフォームやプログラミング言語で使用できます。
もう 1 つの利点は、Windows Server Active Directory (AD) とのフェデレーションや MFA (2FA) など 周辺のさまざまな要素技術との親和性の高さです。例えば、従来の BASIC 認証や WS-Trust を使用した場合、こうした新しい技術が登場する度に、それに応じた対応が必要になります。しかし、この方式だと、MFA などの追加の処理はブラウザー コンポーネントが処理し、アプリ側は同じフローで対応が可能です (プログラムの変更は不要です)。

 

さまざまなシナリオへの対応

OAuth は、現実のアプリケーションを想定した多くのケースを想定して設計されており、以降、これらについて紹介します。

 

※ 変更履歴 :

2015/03/19  エンドポイントを https://login.windows.net から https://login.microsoftonline.com (新エンドポイント) に変更

2017/05/26  画面を新ポータル (https://portal.azure.com) に変更

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s