de:code フォローアップ② Office アプリでの SharePoint Online アクセス (Common Consent Framework 利用)

先日 開催された de:code で担当させていただいた Office アプリ開発 SP1 新機能セッションフォローアップ記事の第2弾です。

セッションの一番最後に、デモで動作を見ていただき、処理内容を解説させていただいた、Office 用アプリで、Office 365 API で提供される Common Consent Framework を利用し、SharePoint Online へアクセスする内容のサンプルコード紹介です。コードを作成する際の、詳細手順はフォローアップ記事① でふれていますので、あわせて参考にしていただければと思います。

また、Office 365 の新しい API や Common Consent Framework については、MS 松崎さんのステキ濃ゆい Blog で解説されていますので、そちらをぜひ~。

   ・ 松崎さん Blog 「Office 365 API 入門
                          「Azure Active Directory の Common Consent Framework (Service 側)

■  Azure Active Directory にアプリを登録

   ●  Office 365 テナントのアカウントで、Azure 管理ポータルにサインアップ
        (初回はユーザー情報の登録が必要)

   ●  アプリを登録

       1. [アプリケーション] を開き、[追加] をクリック

           MailApp1

       2. [組織で開発中のアプリケーションを追加]

          MailApp2

       3. 名前を指定し、[Web アプリケーションや Web API] を選択 

       4. サインオン URL、アプリケーション ID にアプリ内のリダイレクト HTML ページの URL を指定

       5. 登録されたら、構成画面で、クライアント ID とキーをひかえておく

         MailApp3

       6. 同じく構成画面で、[他のアプリケーションに対するアクセス許可] でアクセスしたい内容を指定

         MailApp4

コード例 (メールアプリで取得した添付ファイルを、OneDriveに保存するメール表示フォームの例)
    ※ 各ファイルやクラス等の名前は、任意に変更ください。

   ● Manifest でのアクティブ化ルール

        MailApp5

   ● 添付ファイル処理 コントローラークラス (GetAttachmentController クラス)

    // POST 処理
    [HttpPost()]
    public string SaveAttachment(AttachmentRequest request)
    {
      try
      {
        Attachment attachment = GetAttachmentFile(request.AttachmentId,
            request.AuthToken, request.EwsUrl);
        return SaveTOSP(attachment);
      }
      catch (Exception e)
      {
        return "エラー発生: " + e.Message + "\n\n" + e.StackTrace;
      }
    }

    // EWS よびだしを行い、添付ファイルを取得
    private Attachment GetAttachmentFile(string attachmentId,
        string authToken, string ewsUrl)
    {
      // GetAttachment SOAP リクエスト用に文字列作成
      string getAttachmentRequest =
          @"<?xml version=""1.0"" encoding=""utf-8""?>
           <soap:Envelope xmlns:xsi=""
http://www.w3.org/2001/XMLSchema-instance"&quot;
            xmlns:xsd=""http://www.w3.org/2001/XMLSchema"&quot;
            xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"&quot;
            xmlns:t=""http://schemas.microsoft.com/exchange/services/2006/types"&quot;>
                <soap:Header>
                <t:RequestServerVersion Version=""Exchange2013"" />
                </soap:Header>
                    <soap:Body>
           <GetAttachment   
           xmlns="
http://schemas.microsoft.com/exchange/services/2006/messages&quot;
           xmlns:t=""http://schemas.microsoft.com/exchange/services/2006/types"&quot;>
                        <AttachmentShape/>
                        <AttachmentIds>
                        <t:AttachmentId Id=""{0}""/>
                        </AttachmentIds>
                    </GetAttachment>
                    </soap:Body>
                </soap:Envelope>";
      getAttachmentRequest = String.Format(getAttachmentRequest, attachmentId);

      // Web リクエストオブジェクトの作成
      HttpWebRequest webRequest = WebRequest.CreateHttp(ewsUrl);
      webRequest.Headers.Add("Authorization",
          string.Format("Bearer {0}", authToken));
      webRequest.PreAuthenticate = true;
      webRequest.AllowAutoRedirect = false;
      webRequest.Method = "POST";
      webRequest.ContentType = "text/xml; charset=utf-8";
      byte[] bodyBytes
        = System.Text.Encoding.UTF8.GetBytes(getAttachmentRequest);
      webRequest.ContentLength = bodyBytes.Length;

      Stream requestStream = webRequest.GetRequestStream();
      requestStream.Write(bodyBytes, 0, bodyBytes.Length);
      requestStream.Close();

      // EWS リクエスト
      HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();

      // XML ドキュメントをレスポンスから取得
      if (webResponse.StatusCode == HttpStatusCode.OK)
      {
        Stream responseStream = webResponse.GetResponseStream();
        XmlDocument xmlDocument = new XmlDocument();
        xmlDocument.Load(responseStream);

        string fileName = xmlDocument.
            GetElementsByTagName("t:Name")[0].InnerText;
        byte[] bytes = Convert.FromBase64String(xmlDocument.
            GetElementsByTagName("t:Content")[0].InnerText);

        // レスポンス クローズ
        responseStream.Close();
        webResponse.Close();

        return new Attachment()
        {
          AttachmentBytes = bytes,
          AttachmentName = fileName
        };
      }
      return null;
    }

    // SharePoint ライブラリにファイル保存
    private string SaveTOSP(Attachment attachment)
    {
      string ResourceId =
https://****-my.sharepoint.com/;
      string ApiEndpoint = https://*****-my.sharepoint.com/personal/***/_api;
      string accessToken = OAuthController.GetAccessToken(ResourceId);

      // HTTP リクエスト (新しい File API 利用)
      HttpWebRequest webRequest = WebRequest.CreateHttp(ApiEndpoint
          + "/files/Add(name=’" + attachment.AttachmentName
          + "’, overwrite=true)");
      webRequest.Accept = "application/json;odata=verbose";
      webRequest.Headers.Add("Authorization",
          string.Format("Bearer {0}", accessToken));
      webRequest.Method = "POST";
      webRequest.ContentLength = attachment.AttachmentBytes.Length;
      webRequest.ContentType = "application/octet-stream";

      Stream requestStream = webRequest.GetRequestStream();
      requestStream.Write(attachment.AttachmentBytes, 0,
          attachment.AttachmentBytes.Length);
      requestStream.Close();

      // SharePoint へリクエスト送信
      HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
      if (webResponse.StatusCode == HttpStatusCode.OK)
      {
        Stream responseStream = webResponse.GetResponseStream();
        StreamReader reader = new StreamReader(responseStream);
        return reader.ReadToEnd();
      }
      return "エラー";
    }

    public class Attachment
    {
      public byte[] AttachmentBytes { get; set; }
      public string AttachmentName { get; set; }
    }

    public class AttachmentRequest
    {
      public string AuthToken { get; set; }
      public string AttachmentId { get; set; }
      public string EwsUrl { get; set; }
    }

   ● OAuthController クラス内容

     // AAD のクライアントID 指定
    private static readonly string ClientId = "クライアントID";
    // AAD のクライアントシークレット指定
    private static readonly string ClientSecret = "キー";
    private const string OAuthUrl = "
https://login.windows.net/{0}";
    private static readonly string AuthorizeUrl
      = string.Format(CultureInfo.InvariantCulture, OAuthUrl,
        "common/oauth2/authorize?response_type=code&client_id={0}&resource={1} 
        &redirect_uri={2}");
    private static readonly Uri RedirectUrl = new Uri(
        System.Web.HttpContext.Current.Request.Url,"/リダイレクトページ.html");

    [HttpPost()]
    public string GetAuthorizationUrl(AuthorizationRequest request)
    {
      return String.Format(CultureInfo.InvariantCulture, AuthorizeUrl,
          Uri.EscapeDataString(ClientId), Uri.EscapeDataString(request.ResourceId),
          Uri.EscapeDataString(RedirectUrl.ToString())
      );
    }

    [HttpPost()]
    public string OAuthFlow(AuthorizationParameters parameters)
    {
      try
      {
        ClientCredential credential = new ClientCredential(ClientId, ClientSecret);
        string authority =
          string.Format(CultureInfo.InvariantCulture, OAuthUrl, "common");
        AuthenticationContext authContext = new AuthenticationContext(authority);
        AuthenticationResult result
          = authContext.AcquireTokenByAuthorizationCode(parameters.Code,
            new Uri(RedirectUrl.GetLeftPart(UriPartial.Path)),credential);
        // トークンのキャッシュ
        SSInfo.RefreshToken = result.RefreshToken;
        return "OAuth 認証終了";
      }
      catch (ActiveDirectoryAuthenticationException ex)
      {
        return "OAuth 失敗" + ex.ToString();
      }
    }

    public static string GetAccessToken(string resourceId)
    {
      try
      {
        string refreshToken = Storage.SSInfo.RefreshToken;
        ClientCredential credential = new ClientCredential(ClientId, ClientSecret);
        string authority =
          string.Format(CultureInfo.InvariantCulture, OAuthUrl, "common");
        AuthenticationContext authContext = new AuthenticationContext(authority);
        AuthenticationResult result = authContext.AcquireTokenByRefreshToken(
            refreshToken, ClientId, credential, resourceId);
        return result.AccessToken;
      }
      catch (ActiveDirectoryAuthenticationException)
      {
        return null;
      }
    }

    public class AuthorizationRequest
    {
      public string ResourceId { get; set; }
    }
    public class AuthorizationParameters
    {
      public string Code { get; set; }
    }
   public class SSInfo
    {
      public static string RefreshToken { get; set; }
      public static string ClientId { get { return ""; } }
      public static string ClientSecret { get { return ""; } }
    }

   ● リダイレクト HTML ページの内容

    
       app.initialize();
       var oauthToken = {
           Code : getUrlParameter(‘code’)
       };

       $.ajax({
           url: ‘../../api/OAuth/OAuthFlow’,
           type: ‘POST’,
           data: JSON.stringify(oauthToken),
           contentType: ‘application/json;charset=utf-8’
       }).done(function (data) {
           app.showNotification(JSON.stringify(data));
           window.close();
       }).fail(function (status) {
           app.showNotification(‘エラー’, JSON.stringify(status));
       });

       function getUrlParameter(parameterName) {
           var pattern = “[\\?&]” + parameterName + “=([^&#]*)”,
               regularExpression = new RegExp(pattern),
               results = regularExpression.exec(window.location.href);

           return results ? results[1] : null;
       }
  

   ● アプリの HTML

      <button id="providePermission">① アクセス許可取得</button>
      <button id="saveToSP">② 添付ファイルを OneDrive へ保存 </button>

   ● アプリの HTML から参照する js

      – Initialize

          $(‘#providePermission’).click(To365OAuthFlow); 
          $(‘#saveToSP’).click(saveToSP);

     
      – 関数追加

    // OAuth フローのはじまり
     function To365OAuthFlow() {
        var dataToSend = {
            ResourceId: "Microsoft.SharePoint"
        }

        $.ajax({
            url: ‘../../api/OAuth/GetAuthorizationUrl’,
            type: ‘POST’,
            data: JSON.stringify(dataToSend),
            contentType: ‘application/json;charset=utf-8’
        }).done(function (data) {
            window.open(data);
        }).fail(function (status) {
            app.showNotification(‘エラー’, JSON.stringify(status));
        }).always(function () {
            $(‘.disable-while-sending’).prop(‘disabled’, false);
        });
    }

    // OneDrive へ保存
    function saveToSP() {
        var attachmentId = Office.context.mailbox.item.attachments[0].id;
        var ewsUrl = Office.context.mailbox.ewsUrl;
        Office.context.mailbox.getCallbackTokenAsync(function (ar) {
            var attachmentData = {
                AuthToken: ar.value,
                AttachmentId: attachmentId,
                EwsUrl: ewsUrl
            };

            sendRequest("GetAttachment/SaveAttachment", attachmentData);
        });
    }
   
  
    // ヘルパー関数 コントローラークラス内の API 呼び出し
     function sendRequest(method, data) {
        $.ajax({
            url: ‘../../api/’ + method,
            type: ‘POST’,
            data: JSON.stringify(data),
            contentType: ‘application/json;charset=utf-8’
        }).done(function (data) {
            app.showNotification("成功", "");
        }).fail(function (status) {
            app.showNotification(‘エラー’, JSON.stringify(status));      
        });
     }  

 

以上、コード内で行っている処理はセッション中に解説させていただいたとおりです。

上記サンプルでは、OneDrive へファイルを Add する REST コールを行っていますが、同様に、サンプルコード内の OAuthController.GetAccessToken で取得できるトークンをリクエストヘッダーで利用し、365 コンテンツへの REST アクセスが行っていただけます。

以上、参考にしていただければと思います。

奥田 うさぎ

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中