2012年9月26日水曜日

[WAAD] REST Client で Graph API を直接実行する

# 一部ご指摘をいただきましたので若干修正(Base64 encode ⇒ Base64 url encode)

Windows Azure Active Directory をアプリケーションから利用する際、 Windows プラットフォームであれば、Windows Azure Authentication Library(AAL)を利用することになると思いますが、折角 RESTful な Graph API があるので、汎用 REST Client を使って API へアクセスしてみたいと思います。

■準備
 TenantContextID、AppPrincipalId、SymmetricKey が必要となります。前回同様、CreateServicePrincipal.ps1を使って必要な値を取得します。

 用意が出来たらいよいよアクセスしますが、今回やろうとしていることの全体像を簡単に図にしておきます。

 まず Graph API を利用するために必要な認可を ACS で受け、ACS から取得した Access Token を持って Graph API を利用する、という流れになります。

 実際にやってみると以下のような手順になります。

■Graph API を利用するための Access Token を取得する(REST Client ⇒ ACS)

 初めに、Graph API へのアクセスを行うために必要な Access Token を ACS から取得する必要があります。
 そのためには ACS へ JSON Web Token(JWT)形式でリクエストを投げる必要があります。

 リクエスト先および内容は以下の通りです。

Target Endpointhttps://accounts.accesscontrol.windows.net/tokens/OAuth/2
MethodPOST
Request HeaderContent typeapplication/x-www-form-urlencoded
Request Bodygrant_typehttp://oauth.net/grant_type/jwt/1.0/bearer
assertion
(実際は Symmetric Key でデジタル署名したもの)
{
"alg": "HS256",
"typ": "JWT"
}
{
"aud": "00000001-0000-0000-c000-000000000000/accounts.accesscontrol.windows.net@[TenantContextId]",
"iss": "[AppPrincipalId]@[TenantContextId]",
"nbf": "[UNIX 時間で現在の時刻]",
"exp": "[UNIX 時間でTokenの有効期限]"
}
resourceresource : 00000002-0000-0000-c000-000000000000/directory.windows.net@[テナントID]

 Assertion の部分を作り方は少々面倒ですが、下記の通りです。

 ・ヘッダ部分を Base64 でエンコード

  文字列「{"alg": "HS256","typ": "JWT"}」をこのあたりのツールで Base64 URL エンコードします。

  結果、「eyJhbGciOiAiSFMyNTYiLCJ0eXAiOiAiSldUIn0」という文字列が得られます。



 ・ボディ部分を作成

  JWT を構成する、nbf(Not Before / 有効開始時刻)、exp(Expiration Time)クレームは UNIX 時刻(1970年からの経過秒数)で表すため、このあたりのツールを使って現在時刻および現在時刻+1時間を変換した値を取得します。

  例えば、2012年9月25日23時00分00秒~2012年9月26日00時00分00秒まで有効なトークンを作成するには、
  "nbf" : "1348581600",
  "exp" : "1348585200"
  というクレームを設定します。

  結果、以下の様な JWT ボディを作成します。
  {
   "aud": "00000001-0000-0000-c000-000000000000/accounts.accesscontrol.windows.net@[TenantContextId]",
   "iss": "[AppPrincipalId]@[TenantContextId]",
            "nbf": "1348581600",
            "exp": "1348585200"
  }

 ・ボディ部分を Base64 でエンコード

  先ほどのヘッダ部分と同様に生成した JWT のボディも Base64 URL エンコードします。

  結果、「eyJleHAiOiIxMzQ4NTU5MTY4IiwiYXVkIjoiM(以下略)」といった文字列が得られます。

 ・ヘッダ+ボディを連結した raw token と SymmetricKey でデジタル署名を生成

  ヘッダ部分とボディ部分を"."(ピリオド)で連結した raw token を作成し、SymmetricKey を Base64 デコードした文字列を使ってデジタル署名を生成します。

  良いオンラインツールがなかったので、以下の Java コードを作成し、署名を生成しました。
  ※Java であることに深い意味はありません。手元に eclipse が起動していた&Java のサンプルがあった、というだけです。
  ※どなたか良いオンラインツールがあれば教えてください。


import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Mac;
import org.apache.commons.codec.binary.Base64;

public class GenerateSignature {

    private static byte[] signData(String signingKey, String rawToken) {
        SecretKeySpec secretKey = null;
        secretKey = new SecretKeySpec(Base64.decodeBase64(signingKey), "HmacSHA256");
        Mac mac;
        byte[] signedData = null;
        try {
            mac = Mac.getInstance("HmacSHA256");
            mac.init(secretKey);
            mac.update(rawToken.getBytes("UTF-8"));
            signedData = mac.doFinal();
        } catch (Exception e) {
            System.out.println("signData error");
        }        
        return signedData;
    }
    /**
     * @param args
     */
    public static void main(String[] args) {
        String signingkey = "Li7Awy53cq(以下略。SymmetricKey文字列)";
        String rawToken = "eyJhbGciOiJ(以下略。ヘッダ部分)).eyJleHAiOiIxMzQ4(以下略。ボディ部分)";
        String signature = Base64.encodeBase64String(signData(signingkey,rawToken));
        System.out.println(signature);
    }

}

 ・Assertion の生成

  生成した署名文字列を先ほどの raw token の後に同じく"."(ピリオド)で連結します。



 このようにして作成した Assertion を他のパラメータと一緒に ACS へ POST します。
 リクエストの POST は Chrome Extension の Advanced Rest Client を使いました。

 以下の様にパラメータをセットし、[Send request]ボタンをクリックします。


 うまくいくと、HTTP 200 が返ってきて、JSON 形式で Access Token が取得できます。




■取得した Access Token で Graph API を利用する(REST Client ⇒ Graph API)

 いよいよ Graph API を使います。

 リクエスト先および内容は以下の通りです。

Target Endpointhttps://directory.windows.net/[テナントドメイン名].onmicrosoft.com/Users
MethodGET
Request HeaderAuthorization取得した Access Token 文字列(bearer 以降)
x-ms-dirapi-data-contract-version0.8


 先ほどと同様に Advanced Rest Client にパラメータをセットして、[Send request]ボタンをクリックします。


 うまくいくと、HTTP 200 が返ってきて、ユーザ一覧が取得できます。



尚、書き込みを行う場合はロール設定が必要になるので、このあたりは次回以降で。。

2 件のコメント:

=nat さんのコメント...

Base64ではなく、Base64url が正式です。なので、 = パディングは行われません。

Naohiro Fujie さんのコメント...

> natさん

本当ですね。ご指摘ありがとうございます。
確認、修正しておきます。