Azure AD : ログインをしない Backend Server-Side アプリの開発 (Daemon など)


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

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

こんにちは。

Daemon, Batch, Windows Service などのバックエンド アプリケーションでは、「Azure AD を使った API 連携の Client 開発 (OAuth)」のフローで Login 画面 (SignIn UI) を表示することはできません。

また、こうしたバッチなどのバックエンド アプリケーションでは、ある特定のユーザーとして処理をおこなうのではなく、組織内のあらゆるオブジェクトを対象に処理するケースが一般的です。(もし、ある特定のユーザーとしてオフラインで接続したいなら、「Azure AD を使った API 連携の Client 開発 (OAuth)」のフローで、いったんユーザーによる SignIn をおこない、以降は refresh token を使用してバックエンドで resource と連携できます。)

今回は、こうした Backend で Azure AD の resource と連携する際のフローと、この技術と関連する Application スコープの Role (Application Role) について紹介します。

後述するように、Azure AD Graph、Exchange Online、SharePoint Online, Skype for Business Online (Preview) が、この方式に対応しています。(2016/07 変更 : Skype for Business Online も追加しました。)

 

App-Only Token の取得

以前も紹介しましたが、「Azure AD を使った API 連携の Client 開発 (OAuth)」の HTTP フローで access token を取得した場合、その token には App Context と User Context が含まれます。平たく書くと、「どのアプリを使って、どのユーザーが使用しているか」といった情報が含まれています。

しかし、今回紹介する Login (SignIn) UI を表示しない HTTP フローで処理することで、App Context のみの access token を取得できます。(この方法で取得した access token は、後述の通り、テナント全体に対する強い権限を有しています。)

以降では、この HTTP フローを 3 パターンご紹介します。

 

Pattern 1. Symmetric Key を使う Flow (非推奨)

App Only の Access Token を取得する方法の 1 つ目として、Symmetric Key を使用する方法があります。

Azure Active Directory とは」で解説したように、Windows PowerShell を使って ServicePrincipal の Symmetric Key を取得できます。
この Symmetric Key から SHA256 アルゴリズムを使用した HMAC の署名を作成して client assertion を生成し、この client assertion を使って App-Only の access token を取得できます。

この処理は、ADAL (Active Directory Authentication Library) v1 を使って以下の通り記述できます。(v2 以降では、この方法は使えません。)

using Microsoft.IdentityModel.Clients.ActiveDirectory;
...

AuthenticationContext context = new AuthenticationContext(
  // https://accounts.accesscontrol.windows.net/{tenant realm}
  "https://accounts.accesscontrol.windows.net/sampledir.onmicrosoft.com");
SymmetricKeyCredential credential = new SymmetricKeyCredential(
  // {AppPrincipalId}@{tenant realm}
  "0cbc5463-60c5-4907-8025-f5ea0caf4f8b@sampledir.onmicrosoft.com",
  // {Base64 encoded symmetric Key}
  Convert.FromBase64String("yjIPua1dae..."));
AuthenticationResult assertion = context.AcquireToken(
  // {graph api AppPrincipalId}/directory.windows.net@{tenant realm}
  @"00000002-0000-0000-c000-000000000000/graph.windows.net@sampledir.onmicrosoft.com",
  credential);
string authHeader = assertion.CreateAuthorizationHeader();
. . .

補足 : 実際に Graph API にアクセスする際には、Azure AD の Windows PowerShell を使って、Service Support Administrator ロールを Service Principal (Application) に付与しておいてください。(下記では、Service Principal の Object Id を 25e7dbb2-74c8-46b1-9a7e-c40b80894770 と仮定します。)
Add-MsolRoleMember -RoleMemberType ServicePrincipal -RoleName “Service Support Administrator” -RoleMemberObjectId 25e7dbb2-74c8-46b1-9a7e-c40b80894770

なお、紹介しておきながらアレですが、現在、この方法は推奨されていないと思われますので、後述の client_secret による方法か、証明書 (Asymmetric Certificate) による方法を使用してください。(ADAL v2 では、この SymmetricKeyCredential を使った処理は提供されなくなりました。)

 

Pattern 2. Client Id, Client Secret を使う Flow

Azure AD を使った API 連携の Client 開発 (OAuth)」で説明した client secret を使って、App Only の Access Token を取得できます。

まず、作成する Application は Web Client Application として Azure AD に登録してください。(Native Client Application は不可です。)

あとは、下記の HTTP Request を使って、client id, client secret のみから access token (app only の access token) を取得できます。(なお、https://login.microsoftonline.com/common は使えないので注意してください。)
Fiddler などを使って、是非試してみてください。

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

grant_type=client_credentials&resource=http%3a%2f%2flocalhost%2ftestapp2&client_id=5bbe4005-5e99-4a51-9846-f0dd9a02549a&client_secret=GZOT2mNo%2F9%2B...

 

Pattern 3. 証明書 (Cert) を使う Flow

Asymmetric の証明書 (Certificate) を使って client assertion を作成し、この client assertion を使って access token を取得できます。
少し手続きが面倒なので、以下に、この手順を詳しく解説します。

まず、前述の 2 と同様に、作成する Backend Application を Web Client Application として Azure AD に登録してください。(Native Client Application は不可です。)

つぎに、X.509 証明書 (private key 含む) を作成します。今回は、Visual Studio ツールに付属している makecert を使用して、下記の通り 自己署名 (self-signed) の証明書を作成してみます。
以降、public key の入った cer ファイル (server.cer) は Azure AD 側に登録し、private key を含む pfx ファイル (server.pfx) はプログラム (Backend Application) が使います。

rem -- なんちゃって CA の作成
makecert -r -pe -n "CN=My Root Authority" -ss CA -sr CurrentUser -a sha1 -sky signature -cy authority -sv CA.pvk CA.cer

rem -- なんちゃって server 証明書の作成
makecert -pe -n "CN=DemoApp" -a sha1 -sky Exchange -eku 1.3.6.1.5.5.7.3.1 -ic CA.cer -iv CA.pvk -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 -sv server.pvk server.cer
pvk2pfx -pvk server.pvk -spc server.cer -pfx server.pfx -pi {password}

public key の入った server.cer を、以降の手順で Azure AD 上の Application (上記で登録した Application) に設定します。
まず準備として、Windows PowerShell を使って、証明書の Raw と Thumbprint (拇印) の情報を下記の通り取得しておいてください。(下記では、それぞれ raw.txt, thumbprint.txt に出力しています。)

$cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cer.Import("C:\Demo\test\server.cer")

$raw = $cer.GetRawCertData()
$rawtxt = [System.Convert]::ToBase64String($raw)
echo $rawtxt > raw.txt

$hash = $cer.GetCertHash()
$thumbprint = [System.Convert]::ToBase64String($hash)
echo $thumbprint > thumbprint.txt

取得した証明書の情報を、Manifest (設定の記述されたテキスト ファイル) を使って Azure AD の Application に設定するため、上記で登録した Application の Manifest File をダウンロードします。

メモ帳などで Manifest File を開き、keyCredentials に下記の通り追記します。
customKeyIdentifier には上記で取得した thumbprint, value には raw (の Base64 エンコード文字列), keyId には GUID を新規作成して設定します。

{
  "allowActAsForAllClients": null,
  "appId": "aecbe375-503b-4acb-8211-97927fa60365",
  "appMetadata": {
    "version": 0,
    "data": []
  },
  . . .

  "keyCredentials": [
    {
      "customKeyIdentifier": "MgBsvZT0Gp...",
      "keyId": "9de40fd2-9559-4b52-b075-04ab17227411",
      "type": "AsymmetricX509Cert",
      "usage": "Verify",
      "value": "MIIDFjCCAg..."
    }
  ],
  . . .

}

編集が完了したら、Manifest File をアップロードして Application に反映します。

以上で Azure AD 側の設定は完了です。つぎに、Application (Backend Application) 側の設定をおこないます。

まず、上記で作成した private key (今回は、上記の server.pfx ファイルを使用) を使って「Azure AD : Service 開発 (access token の validation check)」で解説した dot (.) 区切りの署名 (digital signature) 付きの token を作成します。
なお、sign (署名作成) の際の元となる payload には、下記を使用してください。(iss, sub には Application の client id,  jti には reply attack 防止のための GUID を新規作成して設定します。x5t については「Azure AD : Service 開発 (access token の validation check)」を参照してください。)

なお、実際のプログラム サンプルは省略しますが、この RS256 による sign (署名作成) の処理は、JavaScript の場合は jsjws、PHP の場合は openssl_sign などが使えるでしょう。(.NET (C# など) については後述します。)

{
  "alg": "RS256",
  "x5t": "MgBsvZT0Gp6OGykFGY2b5JacOXc"
}

{
  "aud": "https:\/\/login.microsoftonline.com\/sampledir.onmicrosoft.com\/oauth2\/token",
  "iss": "aecbe375-503b-4acb-8211-97927fa60365",
  "sub": "aecbe375-503b-4acb-8211-97927fa60365"
  "jti": "75655218-1661-4a2d-a121-2e80b0004564",
  "nbf": 1427863434,
  "exp": 1427864034,
}

最後に、下記の通り、client_assertion にこの token  (dot 区切りの token) を設定して HTTP Request をおこないます。(ただし、下記で、client_assertion は URL Encode をおこなってください。また、上記の通り、https://login.microsoftonline.com/common は使用できないので注意してください。)
この Request の結果として、access token が取得できます。

POST https://login.microsoftonline.com/sampledir.onmicrosoft.com/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

resource=https%3A%2F%2Ftsmatsuz-sv01.azurewebsites.net%2F&client_id=aecbe375-503b-4acb-8211-97927fa60365&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJhbGciOiJSUzI...&grant_type=client_credentials

.NET (C# など) の場合は、sign (署名作成) に HashAlgorithm クラスを使って実装できますが、ご存じの通り、ADAL (Active Directory Authentication Library) が提供されているので、このライブラリーを使うと以下の通り数ステップでプログラミングできます。(ADAL v2 を使用しています。)

. . .
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Security.Cryptography.X509Certificates;
. . .

AuthenticationContext ctx = new AuthenticationContext(
  "https://login.microsoftonline.com/sampledir.onmicrosoft.com/oauth2/authorize",
  false);
X509Certificate2 cert = new X509Certificate2(
  @"C:\Demo\test\server.pfx",
  "P@ssw0rd", // password of private key
  X509KeyStorageFlags.MachineKeySet);
ClientAssertionCertificate ast = new ClientAssertionCertificate(
  "aecbe375-503b-4acb-8211-97927fa60365", // client id
  cert);
var res = await ctx.AcquireTokenAsync(
  "https://tsmatsuz-sv01.azurewebsites.net/", // resource id
  ast);
MessageBox.Show(res.AccessToken);
. . .

 

Application Role の使用

上記の 2 (client_secret による方法), 3 (certificate による方法) では、「Azure AD : Application Role の使用」で解説した Application Role を扱うことが可能です
例えば、管理 (Admin) Role、表示 (View) Role といった Application Role を resource 側に定義し、アクセスする Application (今回の場合、backend application) の性質 (種類) にあわせて Application スコープの Role を設定できます。

例えば、呼び出す先の resource application 側の Manifest に下記太字の通り追記して、Service01Manage という Role を定義しておきます。(上記の呼び出す側の Manifest ではないので注意してください !)
なお、allowedMemberTypes が Application になっている点に注目してください。上述の通り、このスタイルのフローでは User Context は含まれないので、Application スコープの Role を定義します。

{
  "allowActAsForAllClients": null,
  "appId": "97cff508-1b4a-40a0-8fc1-7882ffac57b1",
  "appMetadata": {
    "version": 0,
    "data": []
  },
  "appRoles": [
    {
      "allowedMemberTypes": [
        "Application"
      ],
      "description": "Manage Service01 App (Sample App Role)",
      "displayName": "Service01Manage",
      "id": "a4a6c767-53f6-432b-a314-137598e4d119",
      "isEnabled": true,
      "value": "service01manage",
      "origin": "Application"
    }
  ],
  . . .

}

この resource に接続する Client Application 側 (上述の Backend Application) では、[Application Permissions] (アプリケーションのアクセス許可) 設定として、下図の通り Service01Manage が選択できるようになります。
Client Application にこの Permission (Application Role) を付与して、上述の 2, 3 のいずれかの方法で access token を取得してみてください。

補足 : このアクセス許可は、Azure Portal 以外に、「Azure AD : Application Role の使用」で解説したように Graph API (api-version 1.5 以上) を使って (プログラムから) 付与することも可能です。

取得した access token をデコードすると (デコード方法は「Azure AD : Service 開発 (access token の validation check)」を参照)、下記 roles の通り Role が付与されているのが確認できます。resource 側ではこの内容を見て権限に応じた動きを実装できます。

{
  "aud": "https://tsmatsuz-sv01.azurewebsites.net/",
  "iss": "https://sts.windows.net/7698d4ed-6610-4807-977c-b19ab9e111b5/",
  "iat": 1427867255,
  "nbf": 1427867255,
  "exp": 1427871155,
  "ver": "1.0",
  "tid": "7698d4ed-6610-4807-977c-b19ab9e111b5",
  "roles": [
    "service01manage"
  ],
  "oid": "c4552233-bd36-4b04-8871-738c279bf683",
  "sub": "c4552233-bd36-4b04-8871-738c279bf683",
  "idp": "https://sts.windows.net/7698d4ed-6610-4807-977c-b19ab9e111b5/",
  "appid": "aecbe375-503b-4acb-8211-97927fa60365",
  "appidacr": "2"
}

なお、Microsoft が提供しているサービスでは、現在、Azure Active Directory (Graph API)、Exchange Online、SharePoint Online, Skype for Business Online (Preview) が この Application スコープの Role (Application Permissions) に対応しています。(2016/07 変更 : Skype for Business Online も追加しました。)
これらの権限を使用すると、テナント内の全ユーザーのメールの Inbox にアクセスしたり、全ユーザーの予定表にアクセスすることが可能で、特定のユーザーではなく、テナント全体に対する強い権限を有しています。例えば、組織内のすべてのユーザー情報を同期する定期稼働のバッチや (Azure AD Graph)、夜間に全ユーザーの予定表を更新するバッチ (Exchange Online) のような処理を構築できます。

 

Consent の使用 (Multi-Tenant への対応)

このタイプのアプリケーションにおいても、「Azure Active Directory の Common Consent Framework」で紹介した Consent 画面を使って、簡単にマルチテナント対応にできます。(個別の利用組織ごとに、client secret や証明書のセットアップをおこなう必要はありません。)

このタイプのアプリケーションのコンセントをおこなう際は、必ず 全体管理者 (Administrator) のロールを持つユーザーでログインしてください。全体管理者 (Administrator) のロールでログインすることで、必ず Admin consent が使用され、利用組織全体でこのアプリケーションが使用できます。(Administrator Consent については「Azure Active Directory の Common Consent Framework」を参照してください。)

補足 : 逆に、この Application 用の Permission が設定されている場合、User Consent を使用することはできません。(このため、User Consent の機能とこのタイプの機能を併用したい場合は、それぞれ Application をわける必要があります。)

補足 : 上述の通り、”common” のエンドポイントは使用できないため、マルチテナント対応にする際は注意してください。(ログイン後に Tenant id を取得して Uri を組み立てます。)

 

Comments (3)

  1. Daisuke Uno says:

    弊社で「Pattern 3. 証明書 (Cert) を使う Flow」にて検証を行った際に、判明した内容を共有いたします。

    >keyId には GUID を新規作成して設定します。

    keyIdには証明書から取得したGUIDを指定する必要がありました。

    >x5t については「Azure AD : Service 開発 (access token の validation check)」を参照してください。)

    上記リンク先には記載がありませんでしたが、x5tには証明書から取得した拇印を指定する必要がありました。

    取得方法は下記URLに書いてありました。

    参考URL:msdn.microsoft.com/…/building-service-apps-in-office-365.aspx

    ご参考になれば幸いです。

  2. フィードバックありがとうございます!

    こうした情報は、大変参考に(特に、これから構築を予定されている方にとって。。。)なります。

  3. Daisuke Uno says:

    お世話になっております。

    すみません、弊社での検証結果に誤りがありましたのでご連絡いたします。

    >keyIdには証明書から取得したGUIDを指定する必要がありました。

    こちら誤りで、「keyId には GUID を新規作成して設定します。」の記載で間違いありませんでした。

    確認不足で申し訳ありません。本文の再修正をよろしくお願いいたします。

Skip to main content