<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Richard diZerega's Blog</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/richard_dizeregas_blog/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/richard_dizeregas_blog/feed.xml</link>
    <description />
    <lastBuildDate>Wed, 28 Jun 2017 12:45:07 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Microsoft's Custom Vision Cognitive Service</title>
      <link>https://docs.microsoft.com/archive/blogs/richard_dizeregas_blog/microsofts-custom-vision-cognitive-service</link>
      <pubDate>Wed, 28 Jun 2017 12:45:06 GMT</pubDate>
      <dc:creator><![CDATA[Richard diZerega]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/richard_dizeregas_blog/?p=615</guid>
      <description><![CDATA[I'm in Sydney, Australia this week to speak at the Build Tour. Besides the keynote, I'm presenting a...]]></description>
      <content:encoded><![CDATA[I'm in Sydney, Australia this week to speak at the <a href="https://buildtour.microsoft.com/">Build Tour</a>. Besides the keynote, I'm presenting a session on Artificial Intelligence and Microsoft's new <a href="https://customvision.ai/">Custom Vision Service</a>. Custom Vision has received significant developer interest since Cornelia Carapcea introduced it in the <a href="https://channel9.msdn.com/Events/Build/2017/KEY01">Build keynote this year</a>. As I prepared for my Australia trip, I thought I'd build something neat with the Custom Vision Service and document my findings/experience in this blog post.  Let me introduce the <a href="https://snakebite.azurewebsites.net/">Snakebite Bot</a>…a bot that can help visually identify venomous snakes in North America.

<strong><em>Figure 1: Snakebite Bot (<a href="https://snakebite.azurewebsites.net">https://snakebite.azurewebsites.net</a>)</em></strong>
<a href="https://msdnshared.blob.core.windows.net/media/2017/06/Snakebite.gif"><img src="https://msdnshared.blob.core.windows.net/media/2017/06/Snakebite.gif" alt="" width="576" height="360" class="alignnone size-full wp-image-645" /></a>
<h1>Background</h1>
Cornelia demonstrated the use of the Custom Vision Service to identify plants by their leaves. I immediately saw the value in identifying poisonous plants like <a href="https://en.wikipedia.org/wiki/Toxicodendron_radicans">Poison Ivy</a>. When I heard I would be presenting Custom Vision in Australia, I immediately thought…everything in Australia can kill you…I'll start with snakes. Why snakes? When I was a little kid, I was bitten by a poisonous <a href="https://en.wikipedia.org/wiki/Agkistrodon_contortrix">Copperhead</a> snake at Possum Kingdom Lake (any <a href="https://en.wikipedia.org/wiki/Toadies">Toadies</a> fans out there?). You would think this would make me scared of snakes, but it did the opposite…dreams of becoming a herpetologist quickly emerged. Fast-forward and my snake career never took off, but becoming a developer did. That said, I never lost my fascination with reptiles. Enter the Snakebite bot…Custom Vision, bots, and snakes…what could be better?
<h1>Building the model</h1>
Microsoft's AI stack includes a number of Cognitive Services that are more commodity than "custom"…you just call the services with your data (ex: facial recognition). Custom Vision is a true "custom" service…you build and train a model specific to your needs. "Custom" doesn't mean complex as building and consuming a Custom Vision model is as easy as 1-2-3**.
<ol>
 	<li><a href="https://docs.microsoft.com/en-us/azure/cognitive-services/custom-vision-service/getting-started-build-a-classifier">Upload/tag photos</a></li>
 	<li><a href="https://docs.microsoft.com/en-us/azure/cognitive-services/custom-vision-service/test-your-model">Train and test the model</a></li>
 	<li><a href="https://docs.microsoft.com/en-us/azure/cognitive-services/custom-vision-service/use-prediction-api">Call into the model to get the most probably tag matches for a photo</a></li>
</ol>
<blockquote><em>**An important fourth step would be to continually enhance the model by evaluating photos with low probability matches. The Custom Vision Service will improve in this area as the service matures.</em></blockquote>
<h2>Upload/tab photos</h2>
While in preview, a model in the Custom Vision Service supports 1000 photos and 50 tags. You can <a href="mailto:customvisionteam@microsoft.com">work with Microsoft</a> to expand these quotas, but this is at Microsoft's discretion during preview. I would caution that multiple small models might be better than one monolithic model. For example, if I wanted to identify any venomous species, I might create specialized models for spiders, snakes, frogs, etc and use the Computer Vision cognitive services to determine which vision model to use.

I worked with Microsoft to expand my model to 10000 photos and 100 tags…and it still wasn't enough as North America has <a href="https://www.pitt.edu/~mcs2/herp/SoNA.html">129 species of snakes</a>. What did I do? I consolidated very similar species (ex: there are 15 different Garter Snake species that all look very similar so I consolidated them into one "Garter Snake" tag). Although my model will identify the most probable snake species, the more important identification is if a snake is dangerous or not and that can be accomplished with two tags (venomous vs non-venomous). After consolidating common species I ended up with 84 tags (81 species and 3 venomous classifications).

Photos should also be at least 256px on shortest edge but no larger than 6MB in size. Additionally, <a href="https://docs.microsoft.com/en-us/azure/cognitive-services/custom-vision-service/getting-started-build-a-classifier">Microsoft recommends at least 30 photos for every tag</a>. The ideal photos for a Custom Vision model capture the subject(s) from multiple perspectives against a solid background (ex: white background). Backgrounds proved particularly challenging with snakes photos. Not only are solid backgrounds impossible, many snakes are camouflaged against their background. Any unique background attributes could cause a false positive between species. Consider training a model with photos of snakes being held…Custom Vision algorithms might false match on hands instead of snakes. Rather than trying to crop out backgrounds, I concentrated on image volume with minimal background patterns/accents. My initial model was loaded with about 3000 snake photos from Bing/Goolge image searches.

<em><strong>Figure 2: Example of human/holding causing false positive match</strong></em>

<a href="https://msdnshared.blob.core.windows.net/media/2017/06/snakehold.png"><img src="https://msdnshared.blob.core.windows.net/media/2017/06/snakehold.png" alt="" width="500" height="300" class="alignnone size-full wp-image-635" /></a>
<h2>Train and test the model</h2>
Model training is a bit of a black box in the Custom Vision Service. You don't have an opportunity to manipulate the algorithm only train on the existing algorithm. The final trained model has a 61.8% precision across all species. However, I've found it to be incredibly accurate considering only the venomous vs non-venomous tags (which was the ultimate goal).

<em><strong>Figure 3: Trained model precision and recall</strong></em>

<a href="https://msdnshared.blob.core.windows.net/media/2017/06/Trained.png"><img src="https://msdnshared.blob.core.windows.net/media/2017/06/Trained.png" alt="" width="670" height="399" class="alignnone size-full wp-image-625" /></a>
<h2>Call into the model</h2>
Like the other Microsoft Cognitive Services, the <a href="https://docs.microsoft.com/en-us/azure/cognitive-services/custom-vision-service/use-prediction-api">Custom Vision Service provides a REST endpoint</a> secured by a subscription key. Specifically, you POST an image in the body of the REST call to get tag probabilities back. The Custom Vision Service will use the default training iteration unless an iteration ID is specified in the API call. The <a href="https://docs.microsoft.com/en-us/azure/cognitive-services/custom-vision-service/use-prediction-api">Custom Vision documentation shows how to do this in .NET</a>, but here is the same call in Node/Typescript. I'm offering this code because I spent the good part of a day getting the correct format for sending the image into the service correctly as a multipart stream.

<em><strong>Figure 4: Can sample for calling Custom Vision Service from Node/TypeScript</strong></em>
<pre>function (session, results) {
  var attachment = session.message.attachments[0];
  request({url: attachment.contentUrl, encoding: null}, function (error, response, body) {
    // Take the image and post to the custom vision service
    rest.post(process.env.IMG_PREDICTION_ENDPOINT, {
      multipart: true,
      headers: {
        'Prediction-Key': process.env.IMG_PREDICTION_KEY,
        'Content-Type': 'multipart/form-data'
      },
      data: {
        'filename': rest.data('TEST.png', 'image/png', body)
      }
    }).on('complete', function(data) {
      let nven = 0.0; //non-venomous
      let sven = 0.0; //semi-venomous
      let fven = 0.0; //full-venomous
      let topHit = { Probability: 0.0, Tag: '' }; 
      for (var i = 0; i &lt; data.Predictions.length; i++) {
        if (data.Predictions[i].Tag === 'Venomous')
          fven = data.Predictions[i].Probability;
        else if (data.Predictions[i].Tag === 'Non-Venomous')
          nven = data.Predictions[i].Probability;
        else if (data.Predictions[i].Tag === 'Semi-Venomous')
          sven = data.Predictions[i].Probability;
        else {
          if (data.Predictions[i].Probability &gt; topHit.Probability)
            topHit = data.Predictions[i];
        }
      }
      let venText = 'Venomous';
      if (nven &gt; fven)
        venText = 'Non-Venomous';
      if (sven &gt; fven &amp;&amp; sven &gt; nven)
        venText = 'Semi-Venomous';

      session.endDialog(`The snake you sent appears to be **${venText}** with the closest match being **${topHit.Tag}** at **${topHit.Probability * 100}%** probability`);
    }).on('error', function(err, response) {
      session.send('Error calling custom vision endpoint');
    }).on('fail', function(data, response) {
      session.send('Failure calling custom vision endpoint');
    });
  });
}</pre>
<h1 style="text-align: left">Conclusions</h1>
In all, I was incredibly impressed by the ease and accuracy of Microsoft's Custom Vision Service (even with poor backgrounds). I envision hundreds of valuable scenarios the service can help deliver and I hope you will give it a test drive.]]></content:encoded>
    </item>
    <item>
      <title>Bot Authentication in the Bot Framework</title>
      <link>https://docs.microsoft.com/archive/blogs/richard_dizeregas_blog/bot-authentication-in-the-bot-framework</link>
      <pubDate>Mon, 15 May 2017 13:48:23 GMT</pubDate>
      <dc:creator><![CDATA[Richard diZerega]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/richard_dizeregas_blog/?p=605</guid>
      <description><![CDATA[About a year ago, my colleague and resident bot wizard, Mat Velloso built a .net package for bots...]]></description>
      <content:encoded><![CDATA[About a year ago, my colleague and resident bot wizard, <a href="https://twitter.com/matvelloso">Mat Velloso</a> built a .net package for bots authenticating and calling services secured by Azure Active Directory (such as the Microsoft Graph). As Mat's <a href="https://github.com/microsoftdx/authbot">AuthBot</a> gained momentum, Mat and I discussed how we could improve and move it forward. We decided on the following:
<ul>
 	<li>Support more authentication providers beyond just Azure AD</li>
 	<li>Remove platform-specific dependencies on ADAL and MSAL</li>
 	<li>Make it easier to implement in a bot framework project</li>
 	<li>Align package naming with Bot Framework packages and <a href="https://github.com/MicrosoftDX/botauth">similar efforts in Node.js</a></li>
</ul>
As a response to these goals, I am pleased to introduce the <a href="https://github.com/richdizz/BotAuth/tree/master/CSharp">BotAuth</a> package. BotAuth uses a provider model that is dependency injected into the core authentication logic. It support virtually any OAuth2 provider with providers already available for AAD <a href="https://www.nuget.org/packages/BotAuth.AADv1/0.1.0-alpha">v1</a>, <a href="https://www.nuget.org/packages/BotAuth.AADv2/0.1.0-alpha">v2</a>, <a href="https://www.nuget.org/packages/BotAuth.AADb2c/0.1.0-alpha">B2C</a>, and anything supported by the <a href="https://github.com/titarenko/OAuth2">OAuth2</a> .net package (which includes Facebook, Google, LinkedIn, Yahoo, etc). Unlike the original AuthBot package, BotAuth does not have any provider-specific dependencies, all dependencies are injected into BotAuth when invoked through a bot dialog.

BotAuth is more aligned with the dialog model of the BotBuilder SDK. You simply forward the bot dialog to the BotAuth (with the appropriate provider and settings) and BotAuth will return an access token to that provider. It is very similar to a javascript promises approach and detailed later in this post. All of the bot framework packages are prefixed with "bot"…BotFramework-Emulator, BotBuilder, BotFramework-WebChat. Additionally, there is a popular bot authentication package in node.js effort called <a href="https://github.com/MicrosoftDX/botauth">BotAuth</a>. As such, the new package was named to align with these.
<h2>Using BotAuth</h2>
To use BotAuth, you should install the NuGet package of the provider(s) you want to use (NuGet packages reference below). Currently, BotAuth has providers for Azure AD v1, v2, and B2C applications as well as a generic <a href="https://github.com/titarenko/OAuth2">OAuth2</a> provider that supports a number of other identities (Facebook, Google, LinkedIn, etc). BotAuth is invoked by initializing an instance of <strong>AuthenticationOptions</strong> with your app details and passing that into the <strong>AuthDialog</strong> with a provider instance (implementing <strong>IAuthProvider</strong>). The AuthDialog returns an access token when the dialog resumes. Although you could cache the returned access token in your own app, the benefit of always going through the AuthDialog is that it manages token cache automatically and will prompt for authentication when necessary. In the sample below, the <strong>MSALAuthProvider</strong> is used, which is a provider for Azure AD v2 applications.

<em><strong>Invoking BotAuth in BotBuilder Dialog</strong></em>
<pre><span class="pl-k">public</span> <span class="pl-k">virtual</span> <span class="pl-k">async</span> <span class="pl-en">Task</span> <span class="pl-en">MessageReceivedAsync</span>(<span class="pl-en">IDialogContext</span> <span class="pl-smi">context</span>, <span class="pl-en">IAwaitable</span>&lt;<span class="pl-en">IMessageActivity</span>&gt; <span class="pl-smi">item</span>)
{
<span class="pl-k">    var</span> <span class="pl-en">message </span>= <span class="pl-en">await</span> <span class="pl-en">item</span>;

<span class="pl-c">    // Initialize AuthenticationOptions and forward to AuthDialog for token</span>
<span class="pl-en">    AuthenticationOptions</span> <span class="pl-en">options</span> = <span class="pl-k">new</span> <span class="pl-en">AuthenticationOptions</span>()
    {
        Authority = ConfigurationManager.AppSettings[<span class="pl-s"><span class="pl-pds">"</span>aad:Authority<span class="pl-pds">"</span></span>],
        ClientId = ConfigurationManager.AppSettings[<span class="pl-s"><span class="pl-pds">"</span>aad:ClientId<span class="pl-pds">"</span></span>],
        ClientSecret = ConfigurationManager.AppSettings[<span class="pl-s"><span class="pl-pds">"</span>aad:ClientSecret<span class="pl-pds">"</span></span>],
        Scopes = <span class="pl-k">new</span> <span class="pl-k">string</span>[] { <span class="pl-s"><span class="pl-pds">"</span>User.Read<span class="pl-pds">"</span></span> },
        RedirectUrl = ConfigurationManager.AppSettings[<span class="pl-s"><span class="pl-pds">"</span>aad:Callback<span class="pl-pds">"</span></span>]
    };
            
<span class="pl-k">    await</span> context.Forward(<span class="pl-k">new</span> <span class="pl-en">AuthDialog</span>(<span class="pl-k">new</span> <span class="pl-en">MSALAuthProvider</span>(), options), async (IDialogContext authContext, IAwaitable&lt;AuthResult&gt; authResult) =&gt;
    {
<span class="pl-k">        var</span> <span class="pl-en">result </span>= <span class="pl-en">await</span> <span class="pl-en">authResult</span>;

<span class="pl-c">        // Use token to call into service</span>
<span class="pl-k">        var</span> <span class="pl-en">json </span>= <span class="pl-k">await</span> <span class="pl-k">new</span> <span class="pl-en">HttpClient</span>().GetWithAuthAsync(result.AccessToken, <span class="pl-s"><span class="pl-pds">"</span>https://graph.microsoft.com/v1.0/me<span class="pl-pds">"</span></span>);
<span class="pl-k">        await</span> authContext.PostAsync(<span class="pl-pds">$"</span><span class="pl-s">I'm a simple bot that doesn't do much, but I know your name is </span>{json.Value&lt;<span class="pl-k">string</span>&gt;(<span class="pl-s"><span class="pl-pds">"</span>displayName<span class="pl-pds">"</span></span>)}<span class="pl-s"> and your UPN is </span>{json.Value&lt;<span class="pl-k">string</span>&gt;(<span class="pl-s"><span class="pl-pds">"</span>userPrincipalName<span class="pl-pds">"</span></span>)}<span class="pl-pds">"</span>);
    }, message, CancellationToken.None);
}</pre>
<h2>Samples</h2>
The BotAuth repo contains samples for all of the providers plus a multi-provider sample. Additionally, there are two Azure AD v2 samples for building Microsoft Graph enabled bots that can be cloned into your own project. One is a very basic bot and the other is a bot leveraging LUIS.

<a href="https://github.com/richdizz/BotAuth/tree/master/CSharp/SampleAADv1Bot">Azure AD v1 Sample</a>

<a href="https://github.com/richdizz/BotAuth/tree/master/CSharp/SampleAADv2Bot">Azure AD v2 Sample</a>

<a href="https://github.com/richdizz/BotAuth/tree/master/CSharp/SampleAADb2cBot">Azure AD B2C Sample</a>

<a href="https://github.com/richdizz/BotAuth/tree/master/CSharp/SampleGenericOAuth2Bot">Generic OAuth2 Sample</a>

<a href="https://github.com/richdizz/BotAuth/tree/master/CSharp/SampleMultiProviderBot">Multi-provider Sample</a>

<a href="https://github.com/richdizz/microsoft-graph-bot-quickstart">Basic Microsoft Graph Sample</a>

<a href="https://github.com/richdizz/microsoft-graph-bot-quickstart-luis">Microsoft Graph Sample with LUIS</a>
<h2>Building a Provider</h2>
To build a custom provider, you most install the <a href="https://www.nuget.org/packages/BotAuth/0.1.0-alpha">BotAuth</a> core package into your project and create a class that implements the <strong>IAuthProvider</strong> interface. This interface has 4 functions and a property you need to implement so the provider can be dependency injected into the BotAuth core library at runtime. Refer to the existing providers if you want more information on how to implement this interface.
<h2>Conclusion</h2>
This is the first release of BotAuth, so we are eager to get feedback and new contributors to the project. The best way to provide feedback it by submitted an issue (or pull-request) to the repo.

<a href="https://github.com/richdizz/BotAuth/tree/master/CSharp">BotAuth Repo on GitHub</a>

<a href="https://www.nuget.org/packages/BotAuth.AADv1/0.1.0-alpha">Azure AD v1 NuGet Package</a>

<a href="https://www.nuget.org/packages/BotAuth.AADv2/0.1.0-alpha">Azure AD v2 NuGet Package</a>

<a href="https://www.nuget.org/packages/BotAuth.AADb2c/0.1.0-alpha">Azure AD B2C NuGet Package</a>

<a href="https://www.nuget.org/packages/BotAuth.GenericOAuth2/0.1.0-alpha">Generic OAuth2 Package</a>]]></content:encoded>
    </item>
    <item>
      <title>Office Add-ins with Contextual Bots via Back Channel</title>
      <link>https://docs.microsoft.com/archive/blogs/richard_dizeregas_blog/office-add-ins-with-contextual-bots-via-back-channel</link>
      <pubDate>Fri, 03 Mar 2017 19:16:44 GMT</pubDate>
      <dc:creator><![CDATA[Richard diZerega]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/richard_dizeregas_blog/?p=595</guid>
      <description><![CDATA[Last week I published a post on using the SharePoint Framework to embed contextual bots in...]]></description>
      <content:encoded><![CDATA[Last week I published a post on using the <a href="https://blogs.msdn.microsoft.com/richard_dizeregas_blog/2017/02/15/sharepoint-framework-and-contextual-bots-via-back-channel/">SharePoint Framework to embed contextual bots in SharePoint</a>. In it, I described how the same approach could be used to embed a contextual bot in an Office Add-in. This post will illustrate how to do exactly that. I will walk through the development of a modern day "Clippy" powered by the <a href="https://docs.botframework.com/en-us/">Bot Framework</a> and <a href="https://dev.office.com/reference/add-ins/javascript-api-for-office">Office.js</a>. The sample that accompanies this post works in Word, Excel, and Outlook, but could also be updated for OneNote and PowerPoint.

[embed]https://www.youtube.com/watch?v=LpO37oSmWcY[/embed]
<h2>Determine the host</h2>
Clippy was a help tool that was pervasive across the Office suite. As such, I wanted to build my Clippy add-in to run in most of the Office clients. This required my add-in and bot to be "client aware". If you are writing an Office Add-in for a specific client, then you can likely omit this section and code directly against that Office product. For Word/Excel/PowerPoint/OneNote you can use <strong>Office.context.host</strong> to determine what Office product the add-in is being hosted in. For Outlook/OWA, you can check if <strong>Office.context.mailbox</strong> is defined. In the Clippy Bot, I check for the host as soon as the add-in is launched and pass that information to my bot via an "initialize" event to the back channel. You can use any event name you want with the back channel and can use the name for event listening logic.

<em><strong>Initializing the add-in upon launch</strong></em>
<pre>&lt;div id="botContainer"&gt;&lt;/div&gt;
&lt;script src="https://unpkg.com/botframework-webchat/botchat.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt;
Office.initialize = function (reason) {
   var host = "", user = { id: "", name: "" };

   // Determine the host and user information (Outlook)
   if (Office.context.host) {
      host = Office.context.host;
      user.id = host + " User";
      user.name = host + " User";
   }
   else if (Office.context.mailbox) {
      host = "Outlook";
      user.id = Office.context.mailbox.userProfile.emailAddress;
      user.name = Office.context.mailbox.userProfile.displayName;
   }

   // Initialize the bot connection and webchat component
   var botConnection = new BotChat.DirectLine({
      secret: "ezjFbUYS_TRUNCATED_0w6f0"
   });
   BotChat.App({
      botConnection: botConnection,
      user: user
   }, document.getElementById("botContainer"));

   // Post the initialize event to the bot backend
   botConnection
      .postActivity({ type: "event", name: "initialize", value: host, from: user })
      .subscribe(console.log("success"));</pre>
<h2>Mail add-ins provide better context</h2>
Office.js provides richer contextual information for Mail Add-ins such as user context and the abilities to get to additional data in Exchange/Exchange Online (all in the context of the mailbox user). In the code above, you may have noticed I use Office.js to retrieve user information via <strong>Office.context.mailbox.userProfile</strong> and passed the details to my bot (via back channel). However, I could have gone much further with Office.js by retrieving tokens that can be used to call into Exchange/Exchange Online (<strong>Office.context.mailbox.getCallbackTokenAsync </strong>or <strong>Office.context.mailbox.makeEwsRequestAsync</strong>).
<h2>Bot Logic</h2>
Earlier in the post I demonstrated sending an "initialize" event through the back channel from my add-in to the bot. The bot looks for events as activity is sent to the messages endpoint. When an activity of type "event" and name "initialize" occurs, the bot pulls out the data sent with the event (host client and user information) and stores it in the bot state.

<em><strong>Listening for events in the bot's messaging endpoint</strong></em>
<pre>[BotAuthentication]
public class MessagesController : ApiController
{
   // POST: api/Messages
   public async Task&lt;HttpResponseMessage&gt; Post([FromBody]Activity activity)
   {
      if (activity.Type == ActivityTypes.Event)
      {
         if (activity.Name == "initialize")
         {
            // Get the Office host from activity Properties then save it into BotState
            var host = activity.Value.ToString();
            var state = activity.GetStateClient();
            var userdata = state.BotState.GetUserData(activity.ChannelId, activity.From.Id);
            userdata.SetProperty&lt;string&gt;("host", host);
            userdata.SetProperty&lt;string&gt;("user", activity.From.Name);
            state.BotState.SetUserData(activity.ChannelId, activity.From.Id, userdata);

            // Route the activity to the correct dialog
            await routeActivity(activity);
         }
         ...TRUNCATED</pre>
Messages sent into the bot are dispatched to client-specific dialogs corresponding to the different Office clients that can host the add-in.

<em><strong>Dispatching messages to client-specific dialogs</strong></em>
<pre>private async Task routeActivity(Activity activity)
{
   // Make sure we know the host
   var state = activity.GetStateClient();
   var userdata = state.BotState.GetUserData(activity.ChannelId, activity.From.Id);
   var host = userdata.GetProperty&lt;string&gt;("host");

   switch (host)
   {
      case "Word":
         await Conversation.SendAsync(activity, () =&gt; new WordDialog());
         break;
      case "Excel":
         await Conversation.SendAsync(activity, () =&gt; new ExcelDialog());
         break;
      case "Outlook":
         await Conversation.SendAsync(activity, () =&gt; new OutlookDialog());
         break;
      default:
         ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
         Activity reply = activity.CreateReply($"Sorry, I can't figure out where you are running me from. You may not have given me enough time to initialize.");
         await connector.Conversations.ReplyToActivityAsync(reply);
         break;
   }
}</pre>
Each of the client-specific dialogs offer the user a choice of operations. For example, the <strong>ExcelDialog</strong> might allow the user to insert a <strong>Range</strong> or a <strong>Chart</strong>. When they select an operation, the bot sends an event with the name "officeOperation" to the add-in through the back channel. In the case of the Clippy Bot, I am only sending the operation name, but it could be any complex data.

<em><strong>Client-specific dialog logic</strong></em>
<pre>[Serializable]
public class ExcelDialog : IDialog&lt;IMessageActivity&gt;
{
   public async Task StartAsync(IDialogContext context)
   {
      context.Wait(MessageReceivedAsync);
   }
   
   public async Task MessageReceivedAsync(IDialogContext context, IAwaitable&lt;IMessageActivity&gt; result)
   {
      var msg = await result;

      string[] options = new string[] { "Chart", "Range" };
      string prompt = $"I see you are running the Clippy Bot in Excel. Select something and I'll insert it into a *NEW* worksheet:";
      PromptDialog.Choice(context, async (IDialogContext choiceContext, IAwaitable&lt;string&gt; choiceResult) =&gt;
      {
         var selection = await choiceResult;
         OfficeOperation op = (OfficeOperation)Enum.Parse(typeof(OfficeOperation), selection);

         // Send the operation through the backchannel using Event activity
         var reply = choiceContext.MakeMessage() as IEventActivity;
         reply.Type = "event";
         reply.Name = "officeOperation";
         reply.Value = op.ToString();
         await choiceContext.PostAsync((IMessageActivity)reply);
      }, options, prompt);
   }
}</pre>
<h2>Add-in listening on back channel</h2>
The add-in can easily listen for events sent through the back channel by leveraging the filter and subscribe functions of the activity on the bot connection. In the Clippy Bot, the add-in listens for activity of <strong>type=event</strong> and <strong>name=officeOperation</strong>. When one of these activities comes through, the <strong>activity.value</strong> contains the data sent from the bot. In the case of Clippy Bot, this is the operation the add-in should perform with office.js.

<em><strong>Listening and responding to events in the add-in</strong></em>
<pre>// Listen for events from the bot back channel
botConnection.activity$
   .filter(function (a) {
      return (a.type === "event" &amp;&amp; a.name === "officeOperation");
   })
   .subscribe(function (a) {
      switch (a.value)
      {
         case "Reply":
            Office.context.mailbox.item.displayReplyForm(
            {
               "htmlBody": "&lt;h1&gt;Hello from the Clippy Bot!!!&lt;/h1&gt;&lt;img href='https://https://klippybot.azurewebsites.net/images/clippy.png' alt='Clippy Bot Image' /&gt;"
            });
            botConnection.postActivity({ type: "event", name: "confirmation", value: true, from: user }).subscribe(console.log("bot operation success"));
            break;
         case "ReplyAll":
            Office.context.mailbox.item.displayReplyAllForm(
            {
               "htmlBody": "&lt;h1&gt;Hello from the Clippy Bot!!!&lt;/h1&gt;&lt;img href='https://https://klippybot.azurewebsites.net/images/clippy.png' alt='Clippy Bot Image' /&gt;"
            });
            botConnection.postActivity({ type: "event", name: "confirmation", value: true, from: user }).subscribe(console.log("bot operation success"));
            break;
         case "Chart":
            if (Office.context.requirements.isSetSupported("ExcelApi", "1.2")) {
               Excel.run(function (context) {
                  var sheet = context.workbook.worksheets.add("ClippySheet" + cnt++);
                  var rangeData = [["Character", "Coolness Score"],
                     ["Clippy", 10],
                     ["Cortana", 8],
                     ["Siri", 4],
                     ["Alexa", 6]];
                  sheet.getRange("A1:B5").values = rangeData;
                  sheet.getRange("A1:B1").format.font.bold = true;
                  sheet.tables.add("A1:B5", true);
                  sheet.charts.add("ColumnClustered", sheet.getRange("A1:B5"), "auto");
                  return context.sync().then(function () { botConnection.postActivity({ type: "event", name: "confirmation", value: true, from: user }).subscribe(console.log("bot operation success")); });
                  // Ignore any errors on context.sync
               }); // Ignore any errors on Word.run
            } // Ignore old versions of Office
            break;
         case "Range":
            if (Office.context.requirements.isSetSupported("ExcelApi", "1.2")) {
               Excel.run(function (context) {
                  var sheet = context.workbook.worksheets.add("ClippySheet" + cnt++);
                  var rangeData = [["Character", "Coolness Score"],
                     ["Clippy", 10],
                     ["Cortana", 8],
                     ["Siri", 4],
                     ["Alexa", 6]];
                  sheet.getRange("A1:B5").values = rangeData;
                  sheet.getRange("A1:B1").format.font.bold = true;
                  sheet.tables.add("A1:B5", true);
                  return context.sync().then(function () { botConnection.postActivity({ type: "event", name: "confirmation", value: true, from: user }).subscribe(console.log("bot operation success")); });
                  // Ignore any errors on context.sync
               }); // Ignore any errors on Word.run
            } // Ignore old versions of Office
            break;
         case "Image":
            if (Office.context.requirements.isSetSupported("WordApi", "1.2")) {
               Word.run(function (context) {
                  context.document.body.insertInlinePictureFromBase64("iVB_TRUNCATED_5CYII=", "End");
                  return context.sync().then(function () { botConnection.postActivity({ type: "event", name: "confirmation", value: true, from: user }).subscribe(console.log("bot operation success")); });
                  // Ignore any errors on context.sync
               }); // Ignore any errors on Word.run
            } // Ignore old versions of Office
            break;
         case "Paragraph":
            if (Office.context.requirements.isSetSupported("WordApi", "1.2")) {
               Word.run(function (context) {
                  context.document.body.insertText("Hello from the Clippy Bot!!!", "End");
                  return context.sync().then(function () { botConnection.postActivity({ type: "event", name: "confirmation", value: true, from: user }).subscribe(console.log("bot operation success")); });
                  // Ignore any errors on context.sync
               }); // Ignore any errors on Word.run
            } // Ignore old versions of Office
            break;
      }
   });</pre>
<h2>Final Thoughts</h2>
Although "Clippy" was just a silly way to illustrate the back channel techniques with bots and add-ins, I hope you can see how the techniques could be used to deliver powerful scenarios. The entire Clippy Bot solution is available at the GitHub repo below. Like the <a href="https://blogs.msdn.microsoft.com/richard_dizeregas_blog/2017/02/15/sharepoint-framework-and-contextual-bots-via-back-channel/">SharePoint Framework samples</a>, I have checked-in the solution with a working DirectLine secret of a published bot so you can try it immediately. Enjoy!

<a href="https://github.com/richdizz/Office-Embedded-Bot-Back-Channel">https://github.com/richdizz/Office-Embedded-Bot-Back-Channel</a>]]></content:encoded>
    </item>
    <item>
      <title>SharePoint Framework and Contextual Bots via Back Channel</title>
      <link>https://docs.microsoft.com/archive/blogs/richard_dizeregas_blog/sharepoint-framework-and-contextual-bots-via-back-channel</link>
      <pubDate>Wed, 15 Feb 2017 22:09:15 GMT</pubDate>
      <dc:creator><![CDATA[Richard diZerega]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/richard_dizeregas_blog/?p=555</guid>
      <description><![CDATA[This year Microsoft has made significant developer investments in SharePoint and bots, with new...]]></description>
      <content:encoded><![CDATA[This year Microsoft has made significant developer investments in SharePoint and bots, with new developer surfaces in the all-new <a href="https://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview">SharePoint Framework</a> and <a href="https://dev.botframework.com/">Bot Framework</a> (respectively). Combining these technologies can deliver some very powerful scenarios. In fact, <a href="https://dev.office.com/patterns-and-practices-detail/11341">SharePoint PnP has a sample on embedding a bot into SharePoint</a>. The sample does a good job of explaining the basics of the Bot Framework DirectLine channel and <a href="https://github.com/Microsoft/BotFramework-WebChat">WebChat</a> component (built with React). However, it really just shows how to embed a bot in SharePoint with no deeper integration. I imagine scenarios where the embedded bot automatically knows who the SharePoint user is and make REST calls on behalf of the user. In this post, I will demonstrate how a bot can interact and get contextual information from SharePoint through the Bot Framework "back channel".

[embed]https://www.youtube.com/watch?v=CLAQbDQwlrc[/embed]
<h2>Bot Architecture</h2>
To build a more contextual SharePoint/bot experience, it helps to understand the architecture of a bot built with the Bot Framework. Bot Framework bots use a REST endpoint that clients POST activity to. The activity type that is most obvious is a "Message". However, clients can pass additional activity types such as pings, typing, etc. The "back channel" involves posting activity to the same REST endpoint with the "Event" activity type. The "back channel" is bi-directional, so a bot endpoint can send "invisible" messages to a bot client by using the same "Event" activity type. Bot endpoints and bot clients just need to have additional logic to listen and respond to "Event" activity. This post will cover both.
<h2>Setup</h2>
Similar to the <a href="https://dev.office.com/patterns-and-practices-detail/11341">PnP sample</a>, our back channel samples will leverage the <a href="https://github.com/Microsoft/BotFramework-WebChat">Bot Framework WebChat</a> control and the DirectLine channel of the Bot Framework. If you haven't used the Bot Framework before, you build a bot and then configure channels for that bot (ex: Skype, Microsoft Teams, Facebook, Slack, etc). DirectLine is a channel that allows more customized bot applications. You can learn more about it in the <a href="https://bots.botframework.com/">Bot Framework documentation</a> or the <a href="https://dev.office.com/patterns-and-practices-detail/11341">PnP sample</a>. I have checked in my SPFx samples with a DirectLine secret for a published bot...you are welcome to use this for testing. As a baseline, here is the code to leverage this control without any use of the back channel.

<em><strong>Bot Framework WebChat before back channel code</strong></em>
<pre>import { App } from 'botframework-webchat';
import { DirectLine } from 'botframework-directlinejs';
require('../../../node_modules/BotFramework-WebChat/botchat.css');
import styles from './EchoBot.module.scss';
...
public render(): void {
   this.domElement.innerHTML = `&lt;div id="${this.context.instanceId}" class="${styles.echobot}"&gt;&lt;/div&gt;`;

   // Initialize DirectLine connection
   var botConnection = new DirectLine({
      secret: "AAos-s9yFEI.cwA.atA.qMoxsYRlWzZPgKBuo5ZfsRpASbo6XsER9i6gBOORIZ8"
   });

   // Initialize the BotChat.App with basic config data and the wrapper element
   App({
      user: { id: "Unknown", name: "Unknown" },
      botConnection: botConnection
   }, document.getElementById(this.context.instanceId));
}</pre>
<h2>Client to Bot Back Channel</h2>
I don't think all embedded bots need bi-directional use of the back channel. However, I do think all embedded bots can benefit from the client-to-bot direction, if only for contextual user/profile information. To use the back channel in this direction, the client needs call the <strong>postActivity</strong> method on the DirectLine <strong>botConnection</strong> with event data. Event data includes <strong>type</strong> ("event"), <strong>name</strong> (a unique name for your event), <strong>value</strong> (any data you want to send on the back channel), and <strong>from </strong>(the user object containing id and name). In the sample below, we are calling the SharePoint REST endpoint for profiles to retrieve the user's profile and sending their name through the back channel (using the event name "sendUserInfo").

<em><strong>Sending data from client to bot via back channel</strong></em>
<pre>// Get userprofile from SharePoint REST endpoint
var req = new XMLHttpRequest();
req.open("GET", "/_api/SP.UserProfiles.PeopleManager/GetMyProperties", false);
req.setRequestHeader("Accept", "application/json");
req.send();
var user = { id: "userid", name: "unknown" };
if (req.status == 200) {
   var result = JSON.parse(req.responseText);
   user.id = result.Email;
   user.name = result.DisplayName;
}

// Initialize the BotChat.App with basic config data and the wrapper element
App({
   user: user,
   botConnection: botConnection
}, document.getElementById(this.context.instanceId));

// Call the bot backchannel to give it user information
botConnection
   .postActivity({ type: "event", name: "sendUserInfo", value: user.name, from: user })
   .subscribe(id =&gt; console.log("success")); 
}</pre>
On the bot endpoint, you need to listen for activity of type event. This will be slightly different depending on C# or Node bot implementation, but my sample uses C#. For C#, the activity type check can easily be implemented in the messages Web API (see <a href="https://github.com/ryanvolum/backChannelBot">here</a> for Node example of back channel). Notice in the sample below we are extracting the user information sent through the back channel (on activity.Value) and saving it in UserState so it can be used throughout the conversation.

<em><strong>Using data sent through the back channel from client to bot</strong></em>
<pre>public async Task&lt;HttpResponseMessage&gt; Post([FromBody]Activity activity)
{
   if (activity.Type == ActivityTypes.Event &amp;&amp; activity.Name == "sendUserInfo")
   {
      // Get the username from activity value then save it into BotState
      var username = activity.Value.ToString();
      var state = activity.GetStateClient();
      var userdata = state.BotState.GetUserData(activity.ChannelId, activity.From.Id);
      userdata.SetProperty&lt;string&gt;("username", username);
      state.BotState.SetUserData(activity.ChannelId, activity.From.Id, userdata);

      ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
      Activity reply = activity.CreateReply($"The back channel has told me you are {username}. How cool is that!");
      await connector.Conversations.ReplyToActivityAsync(reply);
   }
   else if (activity.Type == ActivityTypes.Message)
   {
      // Handle actual messages coming from client
      // Removed for readability
   }
   var response = Request.CreateResponse(HttpStatusCode.OK);
   return response;
}</pre>
<h2>Bot to Client Back Channel</h2>
Sending data through the back channel from bot to client is as simple as sending a message. The only difference is you need to format the activity as an event with name and value. This is a little tricky in C# as you need to cast an <strong>IMessageActivity</strong> to <strong>IEventActivity</strong> and back (as seen below). The <strong>IEventActivity</strong> is new to BotBuilder, so you should update the <strong>Microsoft.Bot.Builder</strong> package to the latest (mine uses <strong>3.5.2</strong>).

<em><strong>Sending data from bot to client via back channel</strong></em>
<pre>[Serializable]
public class RootDialog : IDialog&lt;IMessageActivity&gt;
{
   public async Task StartAsync(IDialogContext context)
   {
      context.Wait(MessageReceivedAsync);
   }

   public async Task MessageReceivedAsync(IDialogContext context, IAwaitable&lt;IMessageActivity&gt; result)
   {
      var msg = await result;

      string[] options = new string[] { "Lists", "Webs", "ContentTypes"};
      var user = context.UserData.Get&lt;string&gt;("username");
      string prompt = $"Hey {user}, I'm a bot that can read your mind...well maybe not but I can count things in your SharePoint site. What do you want to count?";
      PromptDialog.Choice(context, async (IDialogContext choiceContext, IAwaitable&lt;string&gt; choiceResult) =&gt;
      {
         var selection = await choiceResult;

         // Send the query through the backchannel using Event activity
         var reply = choiceContext.MakeMessage() as IEventActivity;
         reply.Type = "event";
         reply.Name = "runShptQuery";
         reply.Value = $"/_api/web/{selection}";
         await choiceContext.PostAsync((IMessageActivity)reply);
      }, options, prompt);
   }
}</pre>
Listening for the back channel events on the client again involves the DirectLine <strong>botConnection</strong> object where you <strong>filter</strong> and <strong>subscribe</strong> to specific activity. In the sample below we listen for activity type of event and name <strong>runShptQuery</strong>. When this type of activity is received, we perform a SharePoint REST query and return the aggregated results to the bot (again via back channel).

<em><strong>Using data sent through the back channel from bot to client</strong></em>
<pre>// Listen for events on the backchannel
var act:any = botConnection.activity$;
act.filter(activity =&gt; activity.type == "event" &amp;&amp; activity.name == "runShptQuery")
   .subscribe(a =&gt; {
      var activity:any = a;
      // Parse the entityType out of the value query string
      var entityType = activity.value.substr(activity.value.lastIndexOf("/") + 1);

      // Perform the REST call against SharePoint
      var shptReq = new XMLHttpRequest();
      shptReq.open("GET", activity.value, false);
      shptReq.setRequestHeader("Accept", "application/json");
      shptReq.send();
      var shptResult = JSON.parse(shptReq.responseText);

      // Call the bot backchannel to give the aggregated results
      botConnection
        .postActivity({ type: "event", name: "queryResults", value: { entityType: entityType, count: shptResult.value.length }, from: user })
        .subscribe(id =&gt; console.log("success sending results"));
   });</pre>
<h2>Conclusion</h2>
Hopefully you can see how much more powerful a SharePoint or Office embedded bot can become with additional context provided through the back channel. I'm really excited to see what creative solutions developers can come up with this, so keep me posted. Big props to <a href="https://twitter.com/billba">Bill Barnes</a> and <a href="https://twitter.com/NotABotRyan">Ryan Volum</a> on my team for their awesome work on the WebChat and the back channel. Below, I have listed four repositories used in this post. <span style="text-decoration: underline">I have purposefully checked in the SharePoint Framework projects with a DirectLine secret to my bot so you can immediately run them without deploying your own bot</span>.

<a href="https://github.com/richdizz/SPFx-1-Way-Bot-Back-Channel">SPFx-1-Way-Bot-Back-Channel</a>
Simple SharePoint Framework Project that embeds a bot and uses the Bot Framework back channel to silently send the bot contextual information about the user.

<a href="https://github.com/richdizz/SPFx-2-Way-Bot-Back-Channel">SPFx-2-Way-Bot-Back-Channel</a>
Simple SharePoint Framework Project that embeds a bot and uses the Bot Framework back channel to silently integrate the bot and client to share contextual information and API calls.

<a href="https://github.com/richdizz/CSharp-BotFramework-OneWay-BackChannel">CSharp-BotFramework-OneWay-BackChannel</a>
Simple C# Bot Framework project that listens on the back channel for contextual information sent from the client (WebChat client in SharePoint page)

<a href="https://github.com/richdizz/CSharp-BotFramework-TwoWay-BackChannel">CSharp-BotFramework-TwoWay-BackChannel</a>
Simple C# Bot Framework project that uses the back channel for contextual information and API calls against the client (WebChat client in SharePoint page)]]></content:encoded>
    </item>
    <item>
      <title>Microsoft Teams and OAuth in Custom Tab</title>
      <link>https://docs.microsoft.com/archive/blogs/richard_dizeregas_blog/microsoft-teams-and-oauth-in-custom-tab</link>
      <pubDate>Wed, 08 Feb 2017 21:04:45 GMT</pubDate>
      <dc:creator><![CDATA[Richard diZerega]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/richard_dizeregas_blog/?p=515</guid>
      <description><![CDATA[Custom Tabs provide developers with canvas to integrate their own user interface into Microsoft...]]></description>
      <content:encoded><![CDATA[Custom Tabs provide developers with canvas to integrate their own user interface into Microsoft Teams. Developers have a lot of flexibility in what they load in the tab, but source domain(s) must be registered in the tab manifest. This is very similar to Office add-ins. In fact, you can think of tabs as Microsoft Teams version of an Office add-in. Like Office add-ins, there are some scenarios where source domains can be hard to predict. This is especially true in federated authentication scenarios. But like Office add-ins, Microsoft Teams offers a dialog API can be used to achieve complex authentication flows. In this post, I will illustrate how to use the Microsoft Teams authenticate dialog to perform an OAuth flow. I'll user Azure AD and the Microsoft Graph, but you could replace those with any identity provider/service.

<a href="https://msdnshared.blob.core.windows.net/media/2017/02/TabAuth.gif"><img src="https://msdnshared.blob.core.windows.net/media/2017/02/TabAuth.gif" alt="tabauth" width="1380" height="809" class="alignnone size-full wp-image-525" /></a>
<h2>Microsoft Teams Authentication Dialog</h2>
The <a href="https://msdn.microsoft.com/en-us/microsoft-teams/jslibrary">Microsoft Teams Tab Library</a> provides an authentication dialog for performing authentication flows. For a comprehensive explanation on why dialogs are necessary, you should review my older post on <a href="https://blogs.msdn.microsoft.com/richard_dizeregas_blog/2015/08/10/connecting-to-office-365-from-an-office-add-in/">Connecting to Office 365 from an Office Add-in</a>. You need a page or view to manage the authentication flow and pass the appropriate authentication results (ex: tokens) back to the tab via the Microsoft Teams Tab Library. The dialog can be launched by calling microsoftTeams.authentication.authenticate:

<em><strong>Launching authentication dialog</strong></em>
<pre>microsoftTeams.authentication.authenticate({
    url: "/app/auth.html",
    width: 400,
    height: 400,
    successCallback: function(token) {
        // Use access token to get some data from a service
        getData(token);
    },
    failureCallback: function(err) {
        document.getElementById("auth").innerText("Token failure.");
    }
});</pre>
Passing information back to the tab is done by calling microsoftTeams.authentication.notifySuccess and microsoftTeams.authentication.notifyFailure. Here is an example of passing an access token back from the authentication dialog.

<em><strong>Passing information from dialog to tab</strong></em>
<pre>authContext.acquireToken("https://graph.microsoft.com", function(error, token) {
    if (error || !token)
        microsoftTeams.authentication.notifyFailure(null);
    else
        microsoftTeams.authentication.notifySuccess(token);
});</pre>
As mentioned in the opening, my sample performs OAuth against Azure AD to call the Microsoft Graph. To do this, I leverage the <a href="https://github.com/AzureAD/azure-activedirectory-library-for-js">Azure AD Authentication Library for JavaScript</a> (adal.js). Here is my completed auth.html that is used in the authentication dialog.

<em><strong>Auth.html using adal.js</strong></em>
<pre>&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;Document&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.13/js/adal.min.js"&gt;&lt;/script&gt;
    &lt;script src="https://statics.teams.microsoft.com/sdk/v0.2/js/MicrosoftTeams.min.js"&gt;&lt;/script&gt;
    &lt;script type="text/javascript"&gt;
    // Initialize microsoft teams tab library
    microsoftTeams.initialize();
 
    // Setup auth parameters for ADAL
    window.config = {
        instance: "https://login.microsoftonline.com/",
        tenant: "common",
        clientId: "c6951c6d-dcaa-4e45-b4b8-2763c7916569",
        postLogoutRedirectUri: window.location.origin,
        cacheLocation: "localStorage",
        endpoints: {
            "https://graph.microsoft.com": "https://graph.microsoft.com"
        }
    };

    // Setup authcontext
    var authContext = new AuthenticationContext(window.config);
    if (authContext.isCallback(window.location.hash))
        authContext.handleWindowCallback(window.location.hash);
    else {
        // Check if user is cached
        var user = authContext.getCachedUser();
        if (!user)
            authContext.login(); // No cached user...force login
        else {
            authContext.acquireToken("https://graph.microsoft.com", function(error, token) {
                if (error || !token) {
                    // TODO: this could cause infinite loop
                    // Should use microsoftTeams.authentication.notifyFailure after one try
                    authContext.login();
                }
                else
                    microsoftTeams.authentication.notifySuccess(token);
            });
        }
    }
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
I should note that Microsoft Teams offers a "silent" authentication option if Azure AD is the identity provider. This is only valid for scenarios where the existing Azure AD session cookies (from the user having already signed in to Microsoft Teams) will be sufficient for the custom tab. Any user consent, federation, or t<span>wo-factor authentication constraints would require the dialog approach.</span>
<h2>Conclusion</h2>
Integrating 3rd party identity and services into Microsoft Teams can deliver compelling scenarios to users. Hopefully this post illustrated how to achieve that with the Microsoft Teams Tab Library. You can find the official documentation on the authentication dialog <a href="https://msdn.microsoft.com/en-us/microsoft-teams/auth">here</a> and the sample used in this post on GitHub: <a href="https://github.com/richdizz/Microsoft-Teams-Tab-Auth">https://github.com/richdizz/Microsoft-Teams-Tab-Auth</a>]]></content:encoded>
    </item>
    <item>
      <title>Microsoft Teams and Custom Tab Theme</title>
      <link>https://docs.microsoft.com/archive/blogs/richard_dizeregas_blog/microsoft-teams-and-custom-tab-theme</link>
      <pubDate>Tue, 07 Feb 2017 19:23:55 GMT</pubDate>
      <dc:creator><![CDATA[Richard diZerega]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/richard_dizeregas_blog/?p=485</guid>
      <description><![CDATA[Custom tabs are an extensibility component in Microsoft Teams that allows developers to embed web...]]></description>
      <content:encoded><![CDATA[Custom tabs are an extensibility component in Microsoft Teams that allows developers to embed web content within a team channel. Tab content is effectively loaded in an IFRAME to ensure the appropriate isolation. Isolation should not compromise the native feel of a tab, so Microsoft offers the <a href="https://msdn.microsoft.com/en-us/microsoft-teams/jslibrary">Microsoft Teams Tab Library</a> to enable a tab page to establish context and interact with Microsoft Teams. One way this library can be used is in matching the active theme in Microsoft Teams (at preview options include light, dark, and high contrast). It is important to draw the distinction between theme and style...tab developers are encouraged to use their own brand styles, but should try to match theme so their tab feels integrated. In this post, I will detail how to use the <a href="https://msdn.microsoft.com/en-us/microsoft-teams/jslibrary">Microsoft Teams Tab Library</a> to get the active theme in Microsoft Teams and how to register an event handler to listen for theme changes (neither of these are in the preview documentation).

<em><strong>Example of handling theme changes in custom tab</strong></em>

<a href="https://msdnshared.blob.core.windows.net/media/2017/02/Untitled.gif"><img src="https://msdnshared.blob.core.windows.net/media/2017/02/Untitled.gif" alt="Tab Theme Changes" width="1380" height="809" class="alignnone size-full wp-image-505" /></a>
<h2>Getting Active Theme</h2>
The context provided by the Microsoft Teams Tab Library includes the active theme the user is leveraging in the Microsoft Teams client. The context can be retrieved by calling <strong>microsoftTeams.getContext</strong> as seen below (notice the theme attribute on context):

<em><strong>Using microsoftTeams.getContext for active theme</strong></em>
<pre>microsoftTeams.getContext(function (context) {
    setTheme(context.theme);
});</pre>
<h2>Listening for Theme Changes</h2>
The Microsoft Teams Tab Library also includes an event handler that can be used to "listen" for theme changes in the Microsoft Teams client. The event handler can be registered by passing a callback function into <strong>microsoftTeams.registerOnThemeChangeHandler</strong> as seen below:

<em><strong>Register handler to listen for theme changes</strong></em>
<pre>// Setup themes refs from teams css
var themedStyleSheets = [];
themedStyleSheets.push("https://statics.teams.microsoft.com/hashedcss/stylesheets.min-e05e0092.css");
themedStyleSheets.push("https://statics.teams.microsoft.com/hashedcss/stylesheets.theme-contrast.min-669e1eed.css");
themedStyleSheets.push("https://statics.teams.microsoft.com/hashedcss/stylesheets.theme-dark.min-fe14eeb8.css");

// setTheme function for initialize and theme changes
var setTheme = function (theme) {
    if (theme === "default") 
        document.getElementById("themeCSS").setAttribute("href", themedStyleSheets[0]);
    else if (theme === "contrast") 
        document.getElementById("themeCSS").setAttribute("href", themedStyleSheets[1]);
    else if (theme === "dark") 
        document.getElementById("themeCSS").setAttribute("href", themedStyleSheets[2]);
};
microsoftTeams.registerOnThemeChangeHandler(setTheme);</pre>
Notice for matching theme, my approach was to leverage the same themed css that Microsoft Teams uses. At the very least, you should probably leverage the background colors used for the default, dark, and contrast themes (in preview these are <span>#eef1f5, #2b2b30, and #000000 </span>respectfully).
<h2>What about Config/Remove pages?</h2>
The tab config and remove pages are loaded into a Microsoft Teams dialog. This dialog is meant to feel native, which makes it even more important to match the theme and styles of Microsoft Teams. Notice I say theme and style here...you shouldn't try to use your brand styles in this dialog...it is for settings. In general, you can use the exact same approach with these pages as we used with the main tab content (get active theme via <strong>getContext</strong> and listen for theme changes via <strong>registerOnThemeChangeHandler</strong>). However, the one difference is that the dialog displays with a white background in the default theme instead of #<span>eef1f5</span> (all other themes can inherit background color). Here is my slightly modified script to handle this.

<em><strong>Theme management from Config/Remove pages</strong></em>
<pre>// setTheme function for initialize and theme changes
var themeChanged = function (theme) {
    if (theme === "default") {
        document.getElementById("themeCSS").setAttribute("href", themedStyleSheets[0]);
        document.getElementById("body").style.background = "#fff"; //special case for default
    }
    else if (theme === "contrast") {
        document.getElementById("themeCSS").setAttribute("href", themedStyleSheets[1]);
        document.getElementById("body").style.background = "inherit";
    }
    else if (theme === "dark") {
        document.getElementById("themeCSS").setAttribute("href", themedStyleSheets[2]);
        document.getElementById("body").style.background = "inherit";
    }
};</pre>
<h2>Conclusion</h2>
Developers should build their tabs to feel integrated with Microsoft Teams. Matching theme is a great step in achieving an integrated feel optimal user experience. Config and Remove pages should take that a step further to march the theme AND style of Teams. Hopefully this post illustrated how to use the Microsoft Teams Tab Library to work with themes. You can find the sample used in this post on GitHub: <a href="https://github.com/richdizz/Microsoft-Teams-Tab-Themes">https://github.com/richdizz/Microsoft-Teams-Tab-Themes</a>.]]></content:encoded>
    </item>
    <item>
      <title>Delivering Better Applications through Microsoft Teams</title>
      <link>https://docs.microsoft.com/archive/blogs/richard_dizeregas_blog/delivering-better-applications-through-microsoft-teams</link>
      <pubDate>Mon, 06 Feb 2017 19:24:27 GMT</pubDate>
      <dc:creator><![CDATA[Richard diZerega]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/richard_dizeregas_blog/?p=475</guid>
      <description><![CDATA[When Microsoft announced the public preview of Microsoft Teams, I think they unveiled the future of...]]></description>
      <content:encoded><![CDATA[When Microsoft announced the public preview of Microsoft Teams, I think they unveiled the future of Microsoft productivity. It provides a single canvas that aggregates relevant information and tools across Office 365 and many 3rd party applications. 3rd party integration is made possible by a compelling extensibility story that developers can take advantage of today.

While in preview, developers can integrate custom experiences into Microsoft Teams through bots, custom tabs, and connectors. Each of these capabilities offers unique capabilities, but developers can also combine them to build even more compelling and comprehensive applications. Although I'll cover each of the capabilities in more detail, here is my perspective on where each fits into the :
<ul>
 	<li><strong>Bots</strong> - great for real-time interactions with individuals and teams</li>
 	<li><strong>Tabs</strong> - great for apps that need a more dynamic or complex user interface</li>
 	<li><strong>Connectors</strong> - great for actionable notifications/messages with light weight user experience</li>
</ul>
<h2>It all started with bots…</h2>
My journey with Microsoft Teams actually started with a bot project. <a href="https://twitter.com/matvelloso">Mat Velloso</a> gives a great talk on "what is a bot" (<a href="https://channel9.msdn.com/Series/Explain/Meet-the-presenter-bot-a-bot-that-presents-about-bots">he even built a bot that presents about bots</a>). As Mat describes it, a bot is just another app. With that in mind, I decided to take an existing mobile app I built last year and deliver it as a bot. Even with cross-platform tools like Xamarin and Cordova, developers are still challenged to get users to install their mobile app(s). A bot on the other hand can run within existing applications that have enormous user bases (ex: Facebook Messenger, Skype, WeChat, etc).

As I started to build my bot, I quickly realized that more advanced user interfaces were hard to deliver. In my case, photo tagging was a challenge. Yes, cognitive services can face detect most pictures, but some photos need manually drawn photo tags. I also found long/paged lists could be more easily delivered in a traditional user interface. I'm sure there are many other scenarios, but these were my roadblock.

<em>Note: many bot channels support the ability to send messages with rich formatting and controls. These are great, but don't yet come close to the empty canvas of a traditional user interface.</em>

Enter Microsoft Teams. Not only did Microsoft Teams provide a channel for my bot, but it also offered custom tabs for complex user interfaces. I also found connectors as a valuable tool in Microsoft Teams for sending proactive actionable notifications to users. Below is a video of my original mobile application and the application re-imagined for Microsoft Teams:

[embed]https://www.youtube.com/watch?v=FV_Mvf1x0Nk[/embed]
<h2>Bots</h2>
2016 was a huge year for bots, with a surge of interest coming from exciting platforms like Alexa/Cortana and new bot channels like Facebook Messenger and Skype. Microsoft went "all-in" on bots with high-profile bot launches (who can forget Tay), significant investments in the <a href="https://dev.botframework.com/">Bot Framework</a> and bot support in popular platforms like Skype. Microsoft Teams was launched with bot support (and even has a bot of its own). For developers, Microsoft Teams is just another channel in the bot framework. That means that bot developers can enable their bot(s) for Microsoft Teams with little to no code modification. Users can interact with bots in Microsoft Teams through one:one chats and team conversations (not enabled during preview).

<a href="https://msdn.microsoft.com/en-us/microsoft-teams/bots">Bots in Microsoft Teams</a> can take advantage of "cards", which is a light weight UI framework for delivering formatted and actionable messages/controls to users. It is the exact card framework used by Office 365 Connectors (discussed later in this post). It uses json instead of markup to deliver messages that can reader across a number of platforms and clients.

Lots of information exists around building bots, especially with the bot framework. Microsoft is busy creating a Bot Design Center, a set of online documentation which will detail best practices and patterns for building bots (I will provide link once published). The only unique constraint in my bot was authenticating users to establish identity and call into the Microsoft Graph. In the next few weeks, I'll author a post on the effort of performing a secure OAuth flow from within a bot.
<h2>Tabs</h2>
Tabs are Microsoft Teams version of an Office Add-in. They give developers the ability to load web content into a specific Channel of a Team. There are three potential web pages tab developers need to consider:
<ul>
 	<li><strong>Configuration Page</strong>: this is the initial page that is launched when a tab is added to Microsoft Teams. It gives the end-user the ability to configure specifics about the tab and allows the developer to capture configuration information and set the page that will be displayed in the tab. This page is also used if the developer allows the tab configuration to be updated.</li>
 	<li><strong>Tab Content Page</strong>: this is the actual page that displays in a tab within Microsoft Teams.</li>
 	<li><strong>Tab Remove Page</strong>: this is an optional page a developer can use to "clean up" when a user deletes a tab. For example, you might want to allow the user to delete any data associated with the tab.</li>
</ul>
The <a href="https://msdn.microsoft.com/en-us/microsoft-teams/jslibrary">Microsoft Teams Tab Library</a> is a JavaScript library that helps developers integrate their web content with Microsoft Teams. One of the most important components of this is the settings object. Developers can use this to retrieve and save settings for the tab. Retrieving settings is useful in determining what team/channel the tab is running in or the Theme the user has applied to Microsoft Teams. Saving settings is useful in capturing both the content and remove page URLs.

<em><strong>Retrieving Settings</strong></em>
<pre>microsoftTeams.getContext(function (context) {
    // Do something with context...ex: context.theme, context.groupId, context.channelId;
});</pre>
<em><strong>Saving Settings</strong></em>
<pre>microsoftTeams.settings.setSettings({
    contentUrl: "https://somedomain/tab.html",
    suggestedDisplayName: "Some default tab name",
    websiteUrl: "https://somedomain/index.html",
    removeUrl: "https://somedomain/tabremove.html"
});</pre>
Beyond settings, the Microsoft Teams Tab Library has a number of additional objects, functions, and events that can be used to build great solutions. In the next few weeks, I'll author posts that demonstrate how to use this library for setting styles/themes for a tab (including listening for theme changes in Microsoft Teams) and performing authentication flows and establishing identity. In the meantime, Microsoft Teams engineering has aggregate some <a href="https://msdn.microsoft.com/en-us/microsoft-teams/tabs">great documentation</a> about tab development.
<h2>Connectors</h2>
Office 365 Connectors are the <a href="https://dev.outlook.com/Connectors">same connectors that run in Outlook</a>. The only difference is that Microsoft Teams expands the group/team construct with an additional layer of detail…the channel, which provide additional organization for Microsoft Teams. Connectors are added through a trust flow that gives a 3rd party app a "web hook" for sending cards into Microsoft Teams. During preview, Connectors do not support side loading into Microsoft Teams like they can in Outlook. Instead, you would use the add connector menu option on a channel and select the one from the Connector Gallery. It should be noted that bots can deliver the same cards as connectors. However, there are compelling scenarios for using each. In my case, I only wanted my bot used in one:one conversations and the connectors to message an entire Team (even when the bot wasn't added to the Team).
<h2>Conclusion</h2>
I hope this post expanded your knowledge of Microsoft Teams extensibility and started to illustrate how you can use it to deliver powerful applications. I hate the concept of blog "series", so instead I will post some additional patterns a learnings from working with Microsoft Teams in the next weeks. In the meantime, checkout the documentation at <a href="https://msdn.microsoft.com/en-us/microsoft-teams/index">https://msdn.microsoft.com/en-us/microsoft-teams/index</a>]]></content:encoded>
    </item>
    <item>
      <title>Adding Office 365 Connectors from a Mobile App</title>
      <link>https://docs.microsoft.com/archive/blogs/richard_dizeregas_blog/adding-office-365-connectors-from-a-mobile-app</link>
      <pubDate>Thu, 16 Jun 2016 17:32:07 GMT</pubDate>
      <dc:creator><![CDATA[Richard diZerega]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/richard_dizeregas_blog/?p=455</guid>
      <description><![CDATA[Office 365 Connectors are a powerful way to aggregate group information and content into one place...]]></description>
      <content:encoded><![CDATA[Office 365 Connectors are a powerful way to aggregate group information and content into one place (regardless of the source). Third parties can use connectors to send useful information and content into Office 365 Groups where users can have conversations, collaborate, and take action on that third party data. Developers can leverage the <a href="https://outlook.office.com/connectors/publish">Connectors Developer Dashboard</a> to register custom Connectors and even generate the HTML markup for integrating the Connector with their websites. However, there is little guidance on adding the same functionality to a mobile application. In this post, I will detail the steps to integrate a third-party Office 365 Connector with a custom mobile application.

[embed]https://www.youtube.com/watch?v=m8zLuQDDiBQ[/embed]
<h1>Adding Connectors from Mobile Apps</h1>
The two documented ways for adding a Office 365 Connector to a group are through the Connector Catalog in Office 365 or via "Connect to Office 365" button on a third party website. All published Connectors are displayed in the Connector Catalog, but third parties can choose to update their website(s) to support the "Connect to" flow. I call this a "flow" because it involves navigating the user to Office 365 to select a group and consent the Connector before returning to the third party website with connection details. I found this very similar to a OAuth2 code authorization flow. So much so, I thought I could use OAuth2 mobile patterns to add Connectors from a mobile application.

The "Connect to" flow for Connectors involves a redirect to the Connector consent screen (where the user also selects a group). This redirect includes a callback_url that Office 365 will use to send back connection details (the group name and webhook URL). This can be accomplished in a native mobile application by leveraging a browser control or in-app browser plug-in and an event listener for when the callback_url is called. Every mobile platform supports some form of a browser control with events for address changes in that control. Below is a code block in TypeScript that uses the Cordova InApp Browser Plugin to "listen" for the callback and then grab the group and webhook details off that response.

<strong><em>Use of In-App Browser to perform Connector consent flow (TypeScript/Cordova):</em></strong>
<pre>  connectToO365() {
    let ctrl = this;
    let ref = cordova.InAppBrowser.open("https://outlook.office.com/connectors/Connect?state=myAppsState&amp;app_id=4b543361-dbc2-4726-a351-c4b43711d6c5&amp;callback_url=https://localhost:44300/callback", "_blank", "location=no,clearcache=yes,toolbar=yes");
    ref.addEventListener("loadstart", function(evt) {
      ctrl.zone.run(() =&gt; {
        //listen for the callback URI
        if (evt.url.indexOf('https://localhost:44300/callback') == 0) {
          //decode the uri into the response parameters
          let parts = evt.url.split('&amp;');
          ctrl.sub.webhook = decodeURIComponent(parts[1].split('=')[1]);
          ctrl.sub.group = decodeURIComponent(parts[2].split('=')[1]);
          ref.close();
        }
      });
    });
  }
</pre>
<h1>Final Thoughts</h1>
I believe that Office 365 Groups are incredibly powerful and the future of Microsoft collaboration. In fact, most of the mobile applications I have been building are group-centric (checkout <a href="https://github.com/richdizz/GoodWithFaces">Good with Faces</a>), so incorporating Connectors just adds an additional layer of integration value. You can download the full Angular2/Ionic2/TypeScript sample used in this post at the repo below:

<a href="https://github.com/richdizz/Adding-O365-Connector-From-Mobile-App">https://github.com/richdizz/Adding-O365-Connector-From-Mobile-App</a>]]></content:encoded>
    </item>
    <item>
      <title>Using OneDrive and Excel APIs in the Microsoft Graph for App Storage</title>
      <link>https://docs.microsoft.com/archive/blogs/richard_dizeregas_blog/using-onedrive-and-excel-apis-in-the-microsoft-graph-for-app-storage</link>
      <pubDate>Tue, 07 Jun 2016 15:53:28 GMT</pubDate>
      <dc:creator><![CDATA[Richard diZerega]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/richard_dizeregas_blog/?p=435</guid>
      <description><![CDATA[The Microsoft Graph is constantly evolving with new and powerful endpoints. If you want a glimpse...]]></description>
      <content:encoded><![CDATA[The Microsoft Graph is constantly evolving with new and powerful endpoints. If you want a glimpse into current engineering investments and future of the Graph, take a look at the latest developments on beta branch. One of the beta endpoints I'm particularly excited for is the new <a href="https://graph.microsoft.io/en-us/docs/api-reference/beta/resources/excel">Excel APIs</a>. The Excel APIs allow you to perform advanced manipulations of Excel remotely via REST calls into the Microsoft Graph. I recently recorded an <a href="https://channel9.msdn.com/Shows/Office-Dev-Show/Office-Dev-Show-Episode-28-Microsoft-Graph-and-Excel-APIs">Office Dev Show on Channel 9</a> discussing these APIs. I found it incredibly easy to manipulate worksheet data using the Excel APIs. So much so, that I thought I would try to use Excel and OneDrive as the data layer for a mobile application. In this post, I'll illustrate how to perform CRUD operations on worksheet data using the Excel APIs in the Microsoft Graph. I'll also discuss a few patterns for working with files in OneDrive for Business and provisioning application assets at run-time.

<strong>NOTE</strong>: The sample used in this post is built with <strong>Ionic2</strong>/<strong>Angular2</strong>/<strong>TypeScript</strong>, but the patterns and API end-points apply to any language platform.

<em><strong>Video showcase of solution:</strong></em>

[embed]https://www.youtube.com/watch?v=InKtB447zFM[/embed]
<h1>Ensuring App Resources</h1>
If OneDrive for Business and Excel will serve as the data layer for my application, I need to ensure the appropriate files and folders are configured each time a user launches the application. OneDrive already provides a special folder for each custom application to store app-specific resources. These special app folders get provisioned on-demand with the app's name to the "Apps" folder of the drive (ex: Apps/MyExpenses). You aren't limited to working in this folder, but it keeps things clean for app-specific files and consistent with competitors like Dropbox. You can reference your application's special folder by referencing the endpoint <strong>/drive/special/approot</strong>. My expense application uses this special folder to store receipt images captured by the application and an Expenses.xlsx file that it used for store all application data (more on this later). Below is the TypeScript I use to check for the existence of Expenses.xlsx.

<strong><em>ensureConfig ensures the Expenses.xlsx is provisioned:</em></strong>
<pre>    //ensures the "Expenses.xslx" file exists in the approot
    ensureConfig() {
        let helper = this;
        return new Promise((resolve, reject) =&gt; {
            helper.authHelper.getTokenForResource(helper.authHelper._graphResource).then(function(token: Microsoft.ADAL.AuthenticationResult) {
               helper.http.get('https://graph.microsoft.com/v1.0/me/drive/special/approot:/Expenses.xlsx', {
                   headers: new Headers({ 'Authorization': 'Bearer ' + token.accessToken })
               })
               .subscribe(res =&gt; {
                   // Check the response status
                   if (res.status === 200) {
                        helper.workbookItemId = res.json().id;
                        window.localStorage.setItem('CACHE_KEY_WORKBOOK', helper.workbookItemId);  
                        resolve(true);    
                   }
                   else {
                       //create the files
                       helper.createWorkbook().then(function(datasourceId: string) {
                           helper.workbookItemId = datasourceId;
                           window.localStorage.setItem('CACHE_KEY_WORKBOOK', helper.workbookItemId);  
                           resolve(true);   
                       }, function(err) {
                           reject(err);
                       });
                  }
               });
            }, function(err) {
                reject(err); //error getting token for MS Graph
            });
        });
    }
</pre>
<h2>Provisioning the Workbook</h2>
If the Expenses.xlsx does not exist, the mobile application will provision it on-demand (creating it's own datasource...pretty cool). For this, I decided to store the Excel workbook template IN the Cordova mobile application. If is it determined that Expenses.xlsx file doesn't exist, the application will read this template and provision it in OneDrive for Business. The function below illustrates this provisioning. One important note below...the <strong>Angular2</strong> documentation for <strong>http.put</strong> indicates that the body can be any object. However, my testing determined it only supports a string body right now which is not appropriate for binary content of an upload. For this reason, I'm using an <strong>XmlHttpRequest</strong> instead. Angular2 is still in beta, so hopefully this will be fixed before final release.

<em><strong>createWorkbook provisions the Expenses.xslx file when it does not exist in OneDrive:</strong></em>
<pre>    
    //creates the "Expenses.xslx" workbook in the approot folder specified
    createWorkbook(folderId: string) {
        //adds a the workbook to OneDrive
        let helper = this;
        return new Promise((resolve, reject) =&gt; {
            //get token for resource
            helper.authHelper.getTokenForResource(helper.authHelper._graphResource).then(function(token: Microsoft.ADAL.AuthenticationResult) {
                //reference the Excel document template at the root application www directory
                window.resolveLocalFileSystemURL(cordova.file.applicationDirectory + 'www/Expenses.xlsx', function (fileEntry) {
                    fileEntry.file(function (file) {
                        //open the file with a FileReader
                        var reader = new FileReader();
                        reader.onloadend = function(evt: ProgressEvent) {
                            //read base64 file and convert to binary
                            let base64 = evt.target.result;
                            base64 = base64.substring(base64.indexOf(',') + 1);
                            
                            //perform the PUT
                            helper.uploadFile(base64, 'Expenses.xlsx').then(function(id: string) {
                                resolve(id);
                            }, function(err) {
                                reject(err);
                            });
                        };
  
                        //catch read errors
                        reader.onerror = function(err) {
                            reject('Error loading file'); 
                        };
                        
                        //read the file as an ArrayBuffer
                        reader.readAsDataURL(file);
                    }, 
                    function(err) {
                        reject('Error opening file');
                    });
                }, function(err) {
                    reject('Error resolving file on file system');
                });
            }, function(err) {
                reject(err); //error getting token for MS Graph
            });
        });
    }
</pre>
&nbsp;
<h1>CRUD Operations with Excel</h1>
CRUD operations with the Excel APIs are relatively easy if you understand the data format Excel expects (multi-dimensional array of values). Retrieving data is accomplished by performing a <strong>GET</strong> on the rows of a specific table (ex: /drive/items/workbook_id/workbook/worksheets('worksheet_id')/tables('table_id')/rows)

<em><strong>getRows function retrieves rows from the Excel workbook:</strong></em>
<pre>    //gets rows from the Expenses.xslx workbook
    getRows() {
        let helper = this;
        return new Promise((resolve, reject) =&gt; {
            helper.authHelper.getTokenForResource(helper.authHelper._graphResource).then(function(token: Microsoft.ADAL.AuthenticationResult) {
               helper.http.get('https://graph.microsoft.com/beta/me/drive/items/' + helper.workbookItemId + '/workbook/worksheets(\'Sheet1\')/tables(\'Table1\')/rows', {
                   headers: new Headers({ 'Authorization': 'Bearer ' + token.accessToken })
               })
               .subscribe(res =&gt; {
                  // Check the response status before trying to resolve
                  if (res.status === 200)
                     resolve(res.json().value);
                  else
                     reject('Get rows failed');
               });
            }, function(err) {
                reject(err); //error getting token for MS Graph
            });
        });
    }
</pre>
Adding data to the worksheet uses the exact same endpoint as above, but with a <strong>POST</strong>. Also, the row data to add must be included in the body of the POST and formatted as a multi-dimensional array (ex: { "values": [["col1Value", "col2Value", "col3Value"]]}).

<em><strong>addRow function adds a row to the Excel workbook:</strong></em>
<pre>    //adds a row to the Excel datasource
    addRow(rowData: any) {
        let helper = this;
        return new Promise((resolve, reject) =&gt; {
            helper.authHelper.getTokenForResource(helper.authHelper._graphResource).then(function(token: Microsoft.ADAL.AuthenticationResult) {
               helper.http.post('https://graph.microsoft.com/beta/me/drive/items/' + helper.workbookItemId + '/workbook/worksheets(\'Sheet1\')/tables(\'Table1\')/rows', JSON.stringify(rowData), {
                   headers: new Headers({ 'Authorization': 'Bearer ' + token.accessToken })
               })
               .subscribe(res =&gt; {
                  // Check the response status before trying to resolve
                  if (res.status === 201)
                     resolve();
                  else
                     reject('Get rows failed');
               });
            }, function(err) {
                reject(err); //error getting token for MS Graph
            });
        });
    }
</pre>
Updating a row in a worksheet is a little different than you might expect. Instead of referencing the table in Excel, you <strong>PATCH</strong> a specify range with new values (in the same multi-dimensional array format as add). Because updates (and deletes as you will see later) are performed against ranges, it is important to keep track of the row index of the data you work with.

<em><strong>updateRow function update a row in the Excel workbook (via Range):</strong></em>
<pre>    //updates a row in the Excel datasource
    updateRow(index:number, rowData:any) {
        let helper = this;
        return new Promise((resolve, reject) =&gt; {
            helper.authHelper.getTokenForResource(helper.authHelper._graphResource).then(function(token: Microsoft.ADAL.AuthenticationResult) {
               let address = 'Sheet1!A' + (index + 2) + ':D' + (index + 2);
               helper.http.patch('https://graph.microsoft.com/beta/me/drive/items/' + helper.workbookItemId + '/workbook/worksheets(\'Sheet1\')/range(address=\'' + address + '\')', JSON.stringify(rowData), {
                   headers: new Headers({ 'Authorization': 'Bearer ' + token.accessToken })
               })
               .subscribe(res =&gt; {
                  // Check the response status before trying to resolve 
                  if (res.status === 200)
                     resolve();
                  else
                     reject('Get rows failed');
               });
            }, function(err) {
                reject(err); //error getting token for MS Graph
            });
        });
    }
</pre>
Deleting a row is accomplished by performing a <strong>POST</strong> to a specific range with /delete tacked on to the end. You can also include instruction in the body on how the Excel should treat the delete. For CRUD operations, we want deleting to shift rows up, so {"shift", "Up"} is in the body of the POST.

<em><strong>deleteRow function deletes a row in the Excel workbook (via Range):</strong></em>
<pre>    //deletes a row in the Excel datasource
    deleteRow(index:number) {
        let helper = this;
        return new Promise((resolve, reject) =&gt; {
            helper.authHelper.getTokenForResource(helper.authHelper._graphResource).then(function(token: Microsoft.ADAL.AuthenticationResult) {
                let address = 'Sheet1!A' + (index + 2) + ':D' + (index + 2);
               helper.http.post('https://graph.microsoft.com/beta/me/drive/items/' + helper.workbookItemId + '/workbook/worksheets(\'Sheet1\')/range(address=\'' + address + '\')/delete', JSON.stringify({ 'shift': 'Up' }), {
                   headers: new Headers({ 'Authorization': 'Bearer ' + token.accessToken })
               })
               .subscribe(res =&gt; {
                  // Check the response status before trying to resolve
                  if (res.status === 204)
                     resolve();
                  else
                     reject('Delete row failed');
               });
            }, function(err) {
                reject(err); //error getting token for MS Graph
            });
       });
    }
</pre>
<h1>CRUD Operations with OneDrive</h1>
Nothing too special about working with files and OneDrive. However, I found the OneDrive documentation to be a little unrealistic and unhelpful as it shows operations on text files and not binary data. Binary data (ex: images and Office documents) introduces additional complexity (especially for a client-side application) so I thought I would document some of the utilities I wrote to work with files. Thanks to <a href="https://twitter.com/waldekm">Waldek Mastykarz</a>, <a href="https://twitter.com/StfBauer">Stefan Bauer</a>, and <a href="https://twitter.com/sahilmalik">Sahil Malik</a> for advisement on upload...it wasn't working at first (turned out to be Angular2 bug), and they were very helpful. The <strong>getBinaryFileContents</strong> function is directly from Waldek's blog <a href="https://blog.mastykarz.nl/2-practical-tips-office-365-group-files-api/">HERE</a>.

<em><strong>uploadFile uploads a binary file to a specific location in OneDrive using PUT</strong></em>
<pre>    //uploads a file to the MyExpenses folder
    uploadFile(base64: string, name: string) {
        let helper = this;
        return new Promise((resolve, reject) =&gt; {
            helper.authHelper.getTokenForResource(helper.authHelper._graphResource).then(function(token: Microsoft.ADAL.AuthenticationResult) {
                //convert base64 string to binary
                let binary = helper.getBinaryFileContents(base64);
                
                //prepare the request
                let req = new XMLHttpRequest();
                req.open('PUT', 'https://graph.microsoft.com/v1.0/me/drive/special/approot:/' + name + '/content', false);
                req.setRequestHeader('Content-type', 'application/octet-stream');
                req.setRequestHeader('Content-length', binary.length.toString());
                req.setRequestHeader('Authorization', 'Bearer ' + token.accessToken);
                req.setRequestHeader('Accept', 'application/json;odata.metadata=full');
                req.send(binary);
                            
                //check response
                if (req.status === 201)
                    resolve(JSON.parse(req.responseText).id); //resolve id of new file
                else
                    reject('Failed to upload file');
            }, function(err) {
                reject(err); //error getting token for MS Graph
            });
        });
    }
</pre>
<em><strong>deleteFile deletes a specific file using DELETE</strong></em>
<pre>    //deletes a file from OneDrive for business
    deleteFile(id:string) {
        let helper = this;
        return new Promise((resolve, reject) =&gt; {
            helper.authHelper.getTokenForResource(helper.authHelper._graphResource).then(function(token: Microsoft.ADAL.AuthenticationResult) {
               helper.http.delete('https://graph.microsoft.com/beta/me/drive/items/' + id, {
                   headers: new Headers({ 'Authorization': 'Bearer ' + token.accessToken })
               })
               .subscribe(res =&gt; {
                  // Check the response status before trying to resolve
                  if (res.status === 204)
                     resolve();
                  else
                     reject('Delete row failed');
               });
            }, function(err) {
                reject(err); //error getting token for MS Graph
            });
       });
    }
</pre>
For images, I decided to take advantage of another beta API in the Microsoft Graph...thumbnails. The <a href="https://graph.microsoft.io/en-us/docs/api-reference/beta/api/item_list_thumbnails">thumbnails API</a> allow you to download small formats of an image in OneDrive. It will generate Small, Medium, and Large thumbnails for all images. I decided a medium thumbnail would look fine in my mobile application and be MUCH more performant.

<em><strong>loadPhoto loads the medium thumbnail for a specified image in OneDrive</strong></em>
<pre>    //loads a photo from OneDrive for Business
    loadPhoto(id:string) {
        //loads a photo for display
        let helper = this;
        return new Promise((resolve, reject) =&gt; {
            helper.authHelper.getTokenForResource(helper.authHelper._graphResource).then(function(token: Microsoft.ADAL.AuthenticationResult) {
                //first get the thumbnails
                helper.http.get('https://graph.microsoft.com/beta/me/drive/items/' + id + '/thumbnails', {
                   headers: new Headers({ 'Authorization': 'Bearer ' + token.accessToken })
               })
               .subscribe(res =&gt; {
                    // Check the response status before trying to resolve
                    if (res.status === 200) {
                        var data = res.json().value;
                        var resource = data[0].medium.url.substring(8);
                        resource = "https://" + resource.substring(0, resource.indexOf('/'));
                        helper.authHelper.getTokenForResource(resource).then(function(thumbtoken: Microsoft.ADAL.AuthenticationResult) {
                            //prepare the content request
                            let req = new XMLHttpRequest();
                            req.open('GET', data[0].medium.url, true);
                            req.responseType = 'blob';
                            req.setRequestHeader('Authorization', 'Bearer ' + thumbtoken.accessToken);
                            req.setRequestHeader('Accept', 'application/json;odata=verbose');
                            req.onload = function(e) {
                                //check response
                                if (this.status === 200) {
                                    //get the blob and convert to base64 using FileReader
                                    var blob = req.response;
                                    var reader = new FileReader();
                                    reader.onload = function(evt){
                                        var base64 = evt.target.result;
                                        base64 = base64.substring(base64.indexOf(',') + 1);
                                        resolve(base64);
                                    };
                                    reader.readAsDataURL(blob);
                                }
                                else
                                    reject('Failed to read image');
                            };
                            req.onerror = function(e) {
                               reject('Failed to download image');
                            };
                            req.send();
                        }, function(err) {
                            reject('Error getting token for thumbnail');
                        });
                    }
                    else
                        reject('Thumbnail load failed');
               });
            }, function(err) {
                reject(err); //error getting token for MS Graph
            });
       });
    }
</pre>
<h1>Final Thoughts</h1>
The Excel APIs in the Microsoft Graph have capabilities well beyond CRUD operations for an app, but I thought this was an interesting pattern (especially for a guy that is always going over my monthly Azure quota). You can grab the MyExpenses solution written with Ionic2/Angular2/TypeScript at GitHub repo listed below:

<a href="https://github.com/richdizz/Ionic2-Angular2-ExcelAPI-Expense-App">https://github.com/richdizz/Ionic2-Angular2-ExcelAPI-Expense-App</a>

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Angular 2.0 and the Microsoft Graph</title>
      <link>https://docs.microsoft.com/archive/blogs/richard_dizeregas_blog/angular-2-0-and-the-microsoft-graph</link>
      <pubDate>Wed, 16 Dec 2015 14:47:00 GMT</pubDate>
      <dc:creator><![CDATA[Richard diZerega]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/richard_dizeregas_blog/2015/12/16/angular-2-0-and-the-microsoft-graph/</guid>
      <description><![CDATA[Yesterday, the AngularJS Team announced the official beta release of Angular 2. The beta milestone...]]></description>
      <content:encoded><![CDATA[<p>Yesterday, the AngularJS Team <a title="announced the official beta release of Angular 2" href="https://angularjs.blogspot.com/2015/12/angular-2-beta.html">announced the official beta release of Angular 2</a>. The beta milestone is a significant achievement that should motivate developers to give Angular 2 a serious consideration for new web/mobile projects. Besides getting ready for primetime, Angular 2 offers some significant improvements over Angular 1 (<a title="just read here" href="https://angularjs.blogspot.com/2015/11/highlights-from-angularconnect-2015.html">just read here</a>). In this post, I&rsquo;ll describe the steps of building an Angular 2 application that authenticates to Azure Active Directory (using an implicit OAuth2 flow) and calls into the Microsoft Graph API. In addition to the <a title="completed solution on GitHub" href="https://github.com/OfficeDev/O365-Angular2-Microsoft-Graph-MyFiles">completed solution on GitHub</a>, I&rsquo;ve also provided a step-by-step video of building the Angular 2/Office 365 app from scratch:</p>
<p>[View:https://www.youtube.com/watch?v=QoTKK2_-dC0]</p>
<h1>Angular 2 and TypeScript</h1>
<p>The AngularJS Team worked closely with Microsoft to take advantage of TypeScript in Angular 2. If you are new to TypeScript, it is a strongly-typed JavaScript superset that compiles down to JavaScript. Because it is class-based and type-strict, it is favored by many developers for client-side development (especially those with object oriented backgrounds). TypeScript introduces a few interesting challenges to a web project.</p>
<p>TypeScript must be compiled to JavaScript before run in a browser (some browsers can compile TypeScript, but this is slow). Luckily, the TypeScript compiler can be run in a watch mode that will automatically re-compile when a TypeScript file has been saved. Notice the start script we have configured below in the package.json (used by the Node Package Manager). This script will concurrently start the TypeScript complier in watch mode (<strong>tsc &ndash;w</strong>) and start the live-server web hosts (which also listens for files changes to provide automatic refreshes).</p>
<div style="padding-top: 5px;"><strong>package.json with start script</strong><br />
<table style="border: 1px solid #36295e; border-image: none; width: 550px; background-color: #eeeeee;">
<tbody>
<tr>
<td style="font-family: Consolas;">
<div style="padding: 5px; width: 540px; white-space: nowrap; -ms-overflow-x: scroll;">
<pre>{
	"name": "Angular2Files",
	"version": "1.0.0",
	"scripts": {
		<span style="background-color: #ffff00;">"start": "concurrent \"tsc -w\" \"live-server\""</span>
	},
	"dependencies": {
		"jquery": "*",
		"bootstrap": "*",
		"angular2": "2.0.0-beta.0",
		"systemjs": "0.19.6",
		"es6-promise": "^3.0.2",
		"es6-shim": "0.33.0",
		"reflect-metadata": "0.1.2",
		"rxjs": "5.0.0-beta.0",
		"zone.js": "0.5.10"
	},
	"devDependencies": {
		"concurrently": "^1.0.0",
		"live-server": "^0.9.0",
		"typescript": "1.7.3"
	}
}</pre>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<p>&nbsp;</p>
<p>The class-based implementation of TypeScript promotes the separation of different classes into different script files. This can make script references messy in HTML. To overcome this, the project (and samples on angular.io) use SystemJS to reference all the generated .js files as a package. Below shows the System.config that tells SystemJS to reference all the .js files found under the &ldquo;src&rdquo; folder.</p>
<div style="padding-top: 5px;"><strong>System.config to dynamically load .js scripts</strong><br />
<table style="border: 1px solid #36295e; border-image: none; width: 550px; background-color: #eeeeee;">
<tbody>
<tr>
<td style="font-family: Consolas;">
<div style="padding: 5px; width: 540px; white-space: nowrap; -ms-overflow-x: scroll;">
<pre>&lt;script type="text/javascript"&gt;
	System.config({
		packages: {
			"src": {
				format: "register",
				defaultExtension: "js"
			}
		}
	});
	System.import("src/app/app");
&lt;/script&gt;</pre>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<p>&nbsp;</p>
<h1>OAuth2 with Azure AD</h1>
<p>The Azure AD Team created an Angular module for integrating the Azure AD Authentication Library (ADAL) into Angular 1 projects. For this project, I decided to leverage a manual/raw OAuth2 flow instead of using a library (which I think is valuable for all developers to understand). However, I&rsquo;m starting to investigate the ngUpgrade and ngForward goodness the AngularJS Teams are offering for mixing Angular 1 and 2 in one project&hellip;perhaps my next post</p>
<p>For an implicit OAuth flow with Azure AD, we will first redirect the user through a sign-in and consent flow to get an id_token. Once we have an id_token, we know the user is signed in and we should be able to get an access_token using a different redirect. These two redirects are depicted in the AuthHelper.ts file as the login and getAccessToken functions. Note that both the id_token and access_token will be passed back from Azure AD as URL parameters. The constructor handles this parameter check.</p>
<div style="padding-top: 5px;"><strong>authHelper for manage Azure AD authentication</strong><br />
<table style="border: 1px solid #36295e; border-image: none; width: 550px; background-color: #eeeeee;">
<tbody>
<tr>
<td style="font-family: Consolas;">
<div style="padding: 5px; width: 540px; white-space: nowrap; -ms-overflow-x: scroll;">
<pre>import { Injectable } from "angular2/core";
import { SvcConsts } from "../svcConsts/svcConsts";

@Injectable()
export class AuthHelper {
	//function to parse the url query string
	private parseQueryString = function(url) {
		var params = {}, queryString = url.substring(1),
		regex = /([^&amp;=]+)=([^&amp;]*)/g, m;
		while (m = regex.exec(queryString)) {
			params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
		}
		return params;
	}
	private params = this.parseQueryString(location.hash);
	public access_token:string = null;
	
	constructor() {
		//check for id_token or access_token in url
		if (this.params["id_token"] != null)
			this.getAccessToken();
		else if (this.params["access_token"] != null)
			this.access_token = this.params["access_token"];
	}
	
	login() {
		//redirect to get id_token
		window.location.href = "https://login.microsoftonline.com/" + SvcConsts.TENTANT_ID + 
			"/oauth2/authorize?response_type=id_token&amp;client_id=" + SvcConsts.CLIENT_ID + 
			"&amp;redirect_uri=" + encodeURIComponent(window.location.href) + 
			"&amp;state=SomeState&amp;nonce=SomeNonce";
	}
	
	private getAccessToken() {
		//redirect to get access_token
		window.location.href = "https://login.microsoftonline.com/" + SvcConsts.TENTANT_ID + 
			"/oauth2/authorize?response_type=token&amp;client_id=" + SvcConsts.CLIENT_ID + 
			"&amp;resource=" + SvcConsts.GRAPH_RESOURCE + 
			"&amp;redirect_uri=" + encodeURIComponent(window.location.href) + 
			"&amp;prompt=none&amp;state=SomeState&amp;nonce=SomeNonce";
	}
}</pre>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<p>&nbsp;</p>
<h1>Angular 2 Routes</h1>
<p>Many single page applications in Angular 1 make use of Angular routing (or Angular UI Router) to dynamically load partial views without reloading the entire page. Implementing Angular 1 routing required an additional script reference and dependency to the routing module (ex: ngRoute). Angular 2 routing has many similarities to its predecessor&hellip;references an additional script reference, added as dependency on the root module, contains route config, offers object/functions to navigate between views, etc. However, these look much different when implemented in TypeScript. Below is the routing implementation for my project where I&rsquo;ve highlighted routing specific code.</p>
<p>Note: the ADAL module for Angular 1 provided a route extension for determining if a view required authentication or not (via requireADLogin flag). Given my simple project contains only two views (login and files), I simply perform a check in the constructor of the App to navigate between the two based on the existence of an access token.</p>
<div style="padding-top: 5px;"><strong>Route configuration in Angular 2</strong><br />
<table style="border: 1px solid #36295e; border-image: none; width: 550px; background-color: #eeeeee;">
<tbody>
<tr>
<td style="font-family: Consolas;">
<div style="padding: 5px; width: 540px; white-space: nowrap; -ms-overflow-x: scroll;">
<pre>import { Component, provide } from "angular2/core";
import { bootstrap } from "angular2/platform/browser";
<span style="background-color: #ffff00;">import { Router, RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, LocationStrategy, HashLocationStrategy } from "angular2/router";</span>
import { HTTP_PROVIDERS } from "angular2/http";

import { Login } from "../login/login";
import { Files } from "../files/files";
import { AuthHelper } from "../authHelper/authHelper";

@Component({
	selector: "files-app",
	template: "<span style="background-color: #ffff00;">&lt;router-outlet&gt;&lt;/router-outlet&gt;</span>",
	<span style="background-color: #ffff00;">directives: [ROUTER_DIRECTIVES],</span>
	providers: [HTTP_PROVIDERS]
})

// Configure the routes for the app
<span style="background-color: #ffff00;">@RouteConfig([ { name: "Login", component: Login, path: "/login" }, { name: "Files", component: Files, path: "/files" } ])</span>

export class App {
	constructor(<span style="background-color: #ffff00;">router:Router</span>, auth:AuthHelper) {
		// Route the user to a view based on presence of access token
		if (auth.access_token !== null) {
			// access token exists...display the users files
			<span style="background-color: #ffff00;">router.navigate(["/Files"]);</span>
		}
		else {
			// access token doesn't exist, so the user needs to login
			<span style="background-color: #ffff00;">router.navigate(["/Login"]);</span>
		}
	}
}

bootstrap(App, [AuthHelper, <span style="background-color: #ffff00;">ROUTER_PROVIDERS</span>, <span style="background-color: #ffff00;">provide(LocationStrategy, { useClass: HashLocationStrategy })</span>]);</pre>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<p>&nbsp;</p>
<h1>Calling the Microsoft Graph</h1>
<p>In Angular 1, the $http object was commonly used for performing REST calls into the Microsoft Graph. Angular 2 offers an Http object that performs the same operations. This requires an additional script reference and import as seen below. Also notice the addition of the Authorization Bearer token included in the header of the Microsoft Graph request.</p>
<div style="padding-top: 5px;"><strong>Calling Microsoft Graph</strong><br />
<table style="border: 1px solid #36295e; border-image: none; width: 550px; background-color: #eeeeee;">
<tbody>
<tr>
<td style="font-family: Consolas;">
<div style="padding: 5px; width: 540px; white-space: nowrap; -ms-overflow-x: scroll;">
<pre>import { Component, View } from "angular2/core";
<span style="background-color: #ffff00;">import { Http, Headers } from "angular2/http";</span>

import { AuthHelper } from "../authHelper/authHelper"

@Component({
	selector: "files"
})

@View({
	templateUrl: "src/files/view-files.html"
})

export class Files {
	private files = [];
	constructor(http:Http, authHelper:AuthHelper) {
		// Perform REST call into Microsoft Graph for files on OneDrive for Business
		<span style="background-color: #ffff00;">http.get</span>("https://graph.microsoft.com/v1.0/me/drive/root/children", {
			<span style="background-color: #ffff00;">headers: new Headers</span>({ "Authorization": "Bearer " + authHelper.access_token })
		})
		.subscribe(res =&gt; {
			// Check the response status before trying to display files
			if (res.status === 200)
				this.files = res.json().value;
			else
				alert("An error occurred calling the Microsoft Graph: " + res.status);
		});
	}
}</pre>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<p>&nbsp;</p>
<h1>Conclusions</h1>
<p>I&rsquo;m really excited the see Angular 2 reach Beta and anxious to see the creative ways the Microsoft community leverages is in their solutions. You can download the Angular 2 Files project from GitHub:&nbsp;<a title="https://github.com/OfficeDev/O365-Angular2-Microsoft-Graph-MyFiles" href="https://github.com/OfficeDev/O365-Angular2-Microsoft-Graph-MyFiles">https://github.com/OfficeDev/O365-Angular2-Microsoft-Graph-MyFiles</a></p>]]></content:encoded>
    </item>
  </channel>
</rss>