Jekyll2023-04-19T17:57:43+00:00https://massivescale.com/feed.xmlThe World According to MarcMarc LaFleur's personal blog of meaningless informationMarc LaFleurApplication vs Delegated Scopes2018-06-08T00:00:00+00:002018-06-08T00:00:00+00:00https://massivescale.com/application-vs-delegated-scopes<p><a href="https://graph.microsoft.io">Microsoft Graph</a> has two parallel sets of <a href="https://developer.microsoft.com/graph/docs/concepts/permissions_reference">permission scopes</a>; Delegated & Application. Understanding the difference between the two, and when to use them, is important when it comes to optimizing your integration with Microsoft Graph and mitigating against unintentionally requesting more access than you really need (or that an IT department would wish to grant).</p>
<h2 id="delegated-permissions">Delegated Permissions</h2>
<p>To understand Delegated Permissions, its helpful to start with the dictionary definition of the word “Delegate”:</p>
<blockquote>
<p><strong>del·e·gate</strong> (verb) - To entrust a task or responsibility to another</p>
</blockquote>
<p>This is precisely what Microsoft Graph means by “Delegated”, a user has delegated a given task to your application. When your application subsequently makes a call to the API, it is executing that function <em>on behalf</em> of the authenticated user.</p>
<p>The simplest illustration of Delegated Permissions in action is <code class="language-plaintext highlighter-rouge">/me</code>. Behind the scenes, the Graph is automatically mapping <code class="language-plaintext highlighter-rouge">/me</code> to the user that delegated permission to your application. Because all activities executed within a delegated scope are made on behalf of a single user, Graph is able to determine who “me” is referring to.</p>
<blockquote class="key">
<p><strong>Activities executed using Delegated scopes are <em>always</em> executed on behalf of an authenticated User.</strong></p>
</blockquote>
<h2 id="application-permissions">Application Permissions</h2>
<p>Unlike Delegated scopes, Application scopes do <em>not</em> have a “user” in context. When your application is operating under Application scopes, your permissions are not provided by a given User but by the Azure AD instance itself.</p>
<p>In general, Application scopes are global. So where requesting the Delegated <code class="language-plaintext highlighter-rouge">Mail.Read</code> scope provides access to any mailbox the current user has permission to access, it’s Application equivalent provides access to <em>every</em> user’s mailbox. For this reason, you should use extreme caution before deciding to use Application scopes.</p>
<blockquote class="key">
<p><strong>Activities executed using Application scopes are executed on behalf of your Application.</strong></p>
</blockquote>
<p>Because Application permission scopes are so broad, all Application scopes require Admin Consent before your application can access a given tenant. For this reason, they are generally not a great fit for commercial/packaged applications.</p>
<h2 id="determining-which-permissions-get-applied">Determining Which Permissions Get Applied</h2>
<p>Which scopes are applied to your application is determined by the OAuth Grant your use to obtain an access token. OAuth Grants that authenticate an individual receive Delegated permissions, while those that do not will receive Application permissions.</p>
<table>
<thead>
<tr>
<th>Oauth Grant Type</th>
<th>Permission Type</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://oauth.net/2/grant-types/authorization-code/">Authorization Code Grant</a></td>
<td>Delegated</td>
</tr>
<tr>
<td><a href="https://oauth.net/2/grant-types/implicit/">Implicit Grant</a></td>
<td>Delegated</td>
</tr>
<tr>
<td><a href="https://oauth.net/2/grant-types/client-credentials/">Client Credentials</a></td>
<td>Application</td>
</tr>
</tbody>
</table>
<blockquote class="tip">
<p>A single Access Token can only contain one type of scopes (Application or Delegated). So if your particular scenario requires both Application <em>and</em> Delegated scopes, your application will need to obtain two separate tokens. The first will represent the User and be obtained using an Authorization Cord or Implicit grant, the second will represent the application itself and be obtained using a Client Credentials grant.</p>
</blockquote>
<h2 id="best-practices">Best Practices</h2>
<ul>
<li><strong>Principal of Least Privileged Permissions</strong> - An application should only request permissions that are absolutely necessary to complete the required task(s). For example: If your needs to read a user’s mailbox, you should select <code class="language-plaintext highlighter-rouge">Mail.Read</code> rather than <code class="language-plaintext highlighter-rouge">Mail.ReadWrite</code>.</li>
<li><strong>Avoid Application scopes in interactive applications</strong> - Using application permissions for interactive scenarios represents a significant compliance and security risk. They can inadvertently elevate a user’s privileges and circumvent policies configured by the tenants’ administrators. A good rule of thumb is that Application scopes should only be used for headless/daemon scenarios.</li>
</ul>
<h2 id="further-reading">Further Reading</h2>
<ul>
<li><a href="/microsoft-v2-endpoint-primer">Microsoft v2 Endpoint Primer</a></li>
<li><a href="/microsoft-v2-endpoint-implicit-grant">v2 Endpoint & Implicit Grant</a></li>
<li><a href="/microsoft-v2-endpoint-admin-consent">v2 Endpoint & Consent</a></li>
<li><a href="/microsoft-v2-endpoint-admin-consent">v2 Endpoint & Admin Consent</a></li>
</ul>Marc LaFleurMicrosoft Graph has two parallel sets of permission scopes; Delegated & Application. Understanding the difference between the two, and when to use them, is important when it comes to optimizing your integration with Microsoft Graph and mitigating against unintentionally requesting more access than you really need (or that an IT department would wish to grant).v2 Endpoint & Consent2018-02-21T00:00:00+00:002018-02-21T00:00:00+00:00https://massivescale.com/microsoft-v2-endpoint-user-vs-admin<p><em>This is a continuation of my <a href="#microsoft-v2-endpoint-series">Microsoft v2 Endpoint Series</a>. If you haven’t read this article yet, I highly recommend starting there. I will be glossing over several bits of configuration we previously covered.</em></p>
<p>When it comes to selecting the scopes your application will be requesting from an API such as Microsoft Graph, it is worth considering how those scopes will impact your initial deployment and user experiences.</p>
<p>With Azure AD, the act of authorizing a set of scopes for an application is referred to as “providing consent”. There are two distinct types of consent involved; User and Admin.</p>
<h2 id="user-consent">User Consent</h2>
<p>User Consent is likely something you’ve run across multiple times; you log in to an application for the first time and get presented with a list of permissions to accept:</p>
<p><img src="/assets/images/user-consent.png" alt="user consent" /></p>
<p>In the screenshot above, Azure AD is asking the User to consent to a list of permissions that were requested by the Application. When the user clicks “Accept” they are providing their consent and the Application will be granted those permissions on the user’s behalf.</p>
<p>Of course, not all permission scopes are created equal. While some scopes such as the ability to read any user’s basic profile from the tenant are pretty innocuous, other such as the ability to <em>write</em> to those profiles represent a potential risk to data integrity. In order to mitigate this risk, certain permissions require an additional level of consent. This is where Admin Consent comes in.</p>
<h2 id="admin-consent">Admin Consent</h2>
<p>Admin Consent is quite different than User Consent. Namely, in that, it is authorizing the application itself rather than the end user. It doesn’t replace User Consent, it is simply an additional layer on top of it. One type of consent does not remove the need for the other.</p>
<p>One way to think about Admin Consent is in terms of “Parental Consent”. So, if my son wants to invite someone over to the house they first need <em>my</em> permission. Only after I give the thumbs up can they go ahead and invite their friend to come over.</p>
<p>In this example, my “son” is the “Application”. The permission I granted was “Admin Consent”. The invitation is my son’s invitation is a request for “User Consent”.</p>
<p>You can determine if you need to receive Admin Consent by checking the “Admin Consent Required” column in the <a href="https://developer.microsoft.com/graph/docs/concepts/permissions_reference#calendars-permissions">documentation for Scopes</a>.</p>
<blockquote class="tip">
<p>If you’re using the Client Credentials (<code class="language-plaintext highlighter-rouge">client_credentials</code>) Grant then you will need Admin Consent regardless of the scopes you’ve selected. This is due to Client Credentials not authenticating a user and therefore not having a User Consent component whatsoever. For this grant, Admin Consent is the only consent you will receive.</p>
</blockquote>
<h2 id="microsoft-v2-endpoint-series">Microsoft v2 Endpoint Series</h2>
<ul>
<li><a href="/microsoft-v2-endpoint-primer">Microsoft v2 Endpoint Primer</a></li>
<li><a href="/microsoft-v2-endpoint-implicit-grant">v2 Endpoint & Implicit Grant</a></li>
<li><a href="/microsoft-v2-endpoint-admin-consent">v2 Endpoint & Consent</a></li>
<li><a href="/microsoft-v2-endpoint-admin-consent">v2 Endpoint & Admin Consent</a></li>
</ul>
<h2 id="further-reading">Further Reading</h2>
<ul>
<li><a href="https://joonasw.net/view/azure-ad-v2-and-msal-from-dev-pov">Azure AD v2 and MSAL from a developer’s point of view</a> by Joonas Westlin</li>
</ul>Marc LaFleurThis is a continuation of my Microsoft v2 Endpoint Series. If you haven’t read this article yet, I highly recommend starting there. I will be glossing over several bits of configuration we previously covered.Ruby & Ubuntu on Windows 102017-07-12T00:00:00+00:002017-07-12T00:00:00+00:00https://massivescale.com/ruby-on-wsl<p><em>We recently published Ubuntu to the <a href="https://www.microsoft.com/store/apps/9NBLGGH4MSV6">Windows Store</a>. With the switch from beta to GA, I found myself with a beautifully pristine Ubuntu environment to rebuild. I decided to take advantage of this opportunity and document some of the configuration challenges I faced. I worked around these issues in the beta as well but it happened in dribs and drabs making it hard to coherently document.</em></p>
<p>This post covers getting <a href="https://www.ruby-lang.org">Ruby</a> 2.2.5 installed and running. I don’t do much with Ruby but my blog is built on top of a Ruby package called Jekyll. While most of the heavy lifting is done behind the scenes on GitHub using GitHub pages, I find it extremely helpful to have a local copy. And since <a href="http://jekyllrb.com/">Jekyll</a> is notoriously tricky to get up and running on Windows, the Windows Subsystem for Linux is a really elegant solution.</p>
<blockquote class="tip">
<p>If you’re not already using Ubuntu from the Store, I highly recommend reading Scott Hanselman’s <a href="https://www.hanselman.com/blog/UbuntuNow%C4%B0nTheWindowsStoreUpdatesToLinuxOnWindows10AndImportantTips.aspx">Ubuntu now in the Windows Store: Updates to Linux on Windows 10 and Important Tips</a>.</p>
</blockquote>
<h2 id="default-package-repo">Default Package Repo</h2>
<p>Ruby is included in Ubuntu’s default package list but it will install Ruby v1.9.3 which is an outdated version and not supported by GitHub Pages and Jekyll. This isn’t an uncommon issue with a distro package repositories so you often find yourself pulling packages direction from the vendor. Ruby, however, turned out to be a bit trickier to sort out for a lowly Windows Dev like myself. ;)</p>
<h2 id="getting-started">Getting Started</h2>
<p>We’re going to be installing several packages here using <code class="language-plaintext highlighter-rouge">apt</code>. Before doing that, it is always a good idea to refresh your package index:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt-get update
</code></pre></div></div>
<p>Before we can install Ruby 2.2.5, we need to first install some dependencies. Executing the following command line will get that ball rolling:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt-get <span class="nb">install </span>git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev
</code></pre></div></div>
<h2 id="installing-rvm">Installing rvm</h2>
<p>After some poking around, I decided on using the Ruby Version Manager (<code class="language-plaintext highlighter-rouge">rvm</code>) to install Ruby. There are <a href="https://github.com/rbenv/rbenv">several</a> <a href="https://www.ruby-lang.org/en/documentation/installation">others</a> out there but I found most of the <code class="language-plaintext highlighter-rouge">rvm</code> commands easier to comprehend.</p>
<p>We’ll install <code class="language-plaintext highlighter-rouge">rvm</code> using the following commands:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt-get <span class="nb">install </span>libgdbm-dev libncurses5-dev automake libtool bison libffi-dev
gpg <span class="nt">--keyserver</span> hkp://keys.gnupg.net <span class="nt">--recv-keys</span> 409B6B1796C275462A1703113804BB82D39DC0E3
curl <span class="nt">-sSL</span> https://get.rvm.io | bash <span class="nt">-s</span> stable
<span class="nb">source</span> ~/.rvm/scripts/rvm
</code></pre></div></div>
<h2 id="installing-ruby-225">Installing Ruby 2.2.5</h2>
<p>Now what we have all of the prerequisites installed, we can install the actual Ruby package. While some of the previous commands were a bit opaque to me, it is this last bit that I found attractive about <code class="language-plaintext highlighter-rouge">rvm</code>. This portion is extremely straightforward:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rvm <span class="nb">install </span>2.2.5
rvm use 2.2.5 <span class="nt">--default</span>
</code></pre></div></div>
<blockquote>
<p>Note that this <em>does</em> take a while to download, configure, and compile. It isn’t as simple as installing some small binaries.</p>
</blockquote>
<p>One aspect about this that I like is how easily I can switch versions. If I decide I would rather use Ruby 2.5.0 then I can simply install it and switch to that as my default:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rvm <span class="nb">install </span>2.5.0
rvm use 2.5.0 <span class="nt">--default</span>
</code></pre></div></div>
<p>One this was done, I was able to get Jekyll installed using <code class="language-plaintext highlighter-rouge">gem install jekyll bundler</code></p>
<p>Below is the complete command list as a Gist</p>
<script src="https://gist.github.com/e1d1e5a9cdb9f9eff26e03c614f1dd6f.js"> </script>Marc LaFleurWe recently published Ubuntu to the Windows Store. With the switch from beta to GA, I found myself with a beautifully pristine Ubuntu environment to rebuild. I decided to take advantage of this opportunity and document some of the configuration challenges I faced. I worked around these issues in the beta as well but it happened in dribs and drabs making it hard to coherently document.v2 Endpoint & Admin Consent2017-06-14T00:00:00+00:002017-06-14T00:00:00+00:00https://massivescale.com/microsoft-v2-endpoint-admin-consent<p><em>This is a continuation of my <a href="#microsoft-v2-endpoint-series">Microsoft v2 Endpoint Series</a>. If you haven’t read this article yet, I highly recommend starting there. I will be glossing over several bits of configuration we previously covered.</em></p>
<p>As developers dig into <a href="https://graph.microsoft.io">Microsoft Graph</a>, they inevitably find themselves needing permission scopes that require “<a href="https://docs.microsoft.com/azure/active-directory/active-directory-assign-admin-roles">Admin Consent</a>”. These are scopes that have deemed worthy of requiring an Administrator to sign off before allowing lowly “normals” to authorize your application.</p>
<h2 id="user-vs-admin-consent">User vs Admin Consent</h2>
<p>While this article is focused on obtaining Admin Consent flow, it is also important to understand the Consent process and the differences between User and Admin Consent.</p>
<p>For a more detailed explanation, see <a href="/microsoft-v2-endpoint-admin-consent">v2 Endpoint & Consent</a></p>
<h2 id="delegated-vs-application-scopes-scopes">Delegated vs Application Scopes Scopes</h2>
<p>Graph has two categories of permission scopes; Application & Delegated. These serve two distinct purposes:</p>
<ul>
<li>
<p><strong>Delegated:</strong> If your application acts on “behalf” of a single user, you’re looking for delegated permissions. Many Delegated permissions can be consented to by normal users. Other higher-privileged permissions require administrator consent, however.</p>
</li>
<li>
<p><strong>Application:</strong> If your application runs without a user context such as a background service or daemon, you’re looking for Application permissions. Unlike Delegated permission, Application permissions <em>always require</em> administrator consent.</p>
</li>
</ul>
<blockquote>
<p>Which scopes get applied to your token depends on which type of OAuth Grant you used to request that token. When you’re using <a href="/microsoft-v2-endpoint-primer"><code class="language-plaintext highlighter-rouge">Authorization Code</code></a> or <a href="/microsoft-v2-endpoint-implicit-grant"><code class="language-plaintext highlighter-rouge">Implicit</code></a> grants then you’ll be using Delegated scopes. If you’re using <code class="language-plaintext highlighter-rouge">Client Credentials</code> then you’re using Application scopes.</p>
</blockquote>
<p>For the moment, I’m going to assume the application in question here is a traditional web app that happens to require access to higher-privileged scopes. This scenario is also used for daemon apps but there are enough nuances around daemons to deserve its own article.</p>
<h2 id="scope-differences">Scope Differences</h2>
<p>When using the v2 Endpoint, you can dynamically request scopes during authorization. This allows you to only request the minimum access required for a given user. For example, if you’re application supports syncing both Calendars and Contacts but your user only wants to leverage the Calendar integration, you can forgo requesting access to Contacts. This provides some additional assurance to the user that your application is behaving as expected.</p>
<p>Things operate a bit differently when Admin Consent is required. These scopes must be defined with your <a href="https://apps.dev.microsoft.com">application registration</a>. This ensures administrators that there is some stability around the permissions you’re requesting. It is important to note that this doesn’t change how dynamic scopes operate; <em>you can still dynamically choose to not request these admin scopes</em>.</p>
<p>Defining these scopes is done within your <a href="https://apps.dev.microsoft.com">application registration</a>. You can define both Delegated and Application permissions here:</p>
<p><img src="/assets/images/app-reg-graph-permissions.png" alt="permissions" /></p>
<p>Clicking add will display a list of available permissions:</p>
<p><img src="/assets/images/app-reg-graph-permissions-dialog.png" alt="permissions" /></p>
<p>At a minimum, you need to declare any scopes that require administrative consent. While you’re certainly able to define other scopes, this isn’t a requirement.</p>
<h2 id="obtaining-consent">Obtaining Consent</h2>
<p>Before any normal (non-admin) users can an application that requires higher-privileged scopes, an admin must first provide consent. This is a one-time event. Once an admin provides the “thumbs up”, every user within that organization will be able to authorize your application.</p>
<p>Admin Consent is kicked off with a simple GET request (typically just a link that an Admin follows) to <code class="language-plaintext highlighter-rouge">https://login.microsoftonline.com/common/adminconsent</code> along with the following query parameters:</p>
<table>
<thead>
<tr>
<th style="text-align: left">Property</th>
<th style="text-align: left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">client_id</code></td>
<td style="text-align: left">This is your Application ID (see <a href="/microsoft-v2-endpoint-primer">Microsoft v2 Endpoint Primer</a> for more information)</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">redirect_uri</code></td>
<td style="text-align: left">This is the URI you want to redirect them too after consent (more in a moment)</td>
</tr>
</tbody>
</table>
<p>The prototype for this call looks like this:</p>
<pre><code class="language-none">https://login.microsoftonline.com/common/adminconsent?
client_id=[APPLICATION ID]&redirect_uri=[REDIRECT URI]
</code></pre>
<p>When the admin logs in they will be presented with the list of permission you selected during registration. Once they accept the scopes, they will be returned to the redirect_uri you specified.</p>
<p>During redirection, you will receive some additional data in for form of query parameters:</p>
<pre><code class="language-none">http://localhost:3000/consentReturn/?tenant=[tenant id]&admin_consent=[True/False]
</code></pre>
<ul>
<li><code class="language-plaintext highlighter-rouge">tenant</code> - This is the GUID for the tenant that was authorized. This allows you to capture which tenant consent was granted for.</li>
<li><code class="language-plaintext highlighter-rouge">admin_consent</code> - This returns true if they consented and false if they declined</li>
</ul>
<h2 id="post-consent">Post Consent</h2>
<p>Once the tenant has consented to your permissions, you can begin authenticating users using the traditional OAuth workflow. Features such as dynamic scopes and refresh tokens continue to operate in the same way as well.</p>
<blockquote class="warning">
<p>One common issue that folks run into, particularly early on in development and testing, are errors being raised after changing the application’s scopes. It is important to remember that Consent is granted for a fixed set of scopes. If those scopes should change, <em>additional consent is required</em>.</p>
<p>For User Consent this typically isn’t an issue since the user is simply presented with an updated Consent page the next time they authenticate. For Admin Consent, however, you will need to repeat the Admin Consent process in order to cover those new scopes.</p>
</blockquote>
<h2 id="microsoft-v2-endpoint-series">Microsoft v2 Endpoint Series</h2>
<p>*<a href="/microsoft-v2-endpoint-primer">Microsoft v2 Endpoint Primer</a>
*<a href="/microsoft-v2-endpoint-implicit-grant">v2 Endpoint & Implicit Grant</a>
*<a href="/microsoft-v2-endpoint-admin-consent">v2 Endpoint & Consent</a>
*<a href="/microsoft-v2-endpoint-admin-consent">v2 Endpoint & Admin Consent</a></p>
<h2 id="further-reading">Further Reading</h2>
<ul>
<li><a href="https://joonasw.net/view/azure-ad-v2-and-msal-from-dev-pov">Azure AD v2 and MSAL from a developer’s point of view</a> by Joonas Westlin</li>
</ul>Marc LaFleurThis is a continuation of my Microsoft v2 Endpoint Series. If you haven’t read this article yet, I highly recommend starting there. I will be glossing over several bits of configuration we previously covered.Moved My Blog (again)2017-06-07T00:00:00+00:002017-06-07T00:00:00+00:00https://massivescale.com/moved-my-blog<p>I’ve switched blogging platforms quite a few times over the years. I’ve tried everything from <a href="http://www.livejournal.com/">LiveJournal</a> to <a href="http://www.orchardproject.net/">Orchard</a> and, for the last several years, <a href="https://wordpress.org/">WordPress</a>. And for the most part, WordPress has severed me extremely well. It has a huge community with more <a href="https://wordpress.org/plugins/">plugins</a> than you can through a stick at. It does, however, have its drawbacks; particularly it’s a rather sizable footprint and <a href="https://premium.wpmudev.org/blog/wordpress-bloat/">bloat</a>.</p>
<p>For a while now I’ve been eyeing a move to a static page model. My site doesn’t require a lot of dynamic content and given that I’ve been <a href="https://github.com/OfficeDev/office-js-docs">writing in Markdown</a> for several years already, my workflow wouldn’t have to change too much.</p>
<p>Switching also allows me to dramatically reduce my costs. By leveraging <a href="https://pages.github.com/">GitHub Pages</a> I’m able to turn off the web and database servers required by WordPress. While not excessively expensive, it certainly isn’t free.</p>
<p>Behind the scenes, GitHub is using <a href="http://jekyllrb.com/">Jekyll</a> which is a very popular static site generator. Jekyll handles most of the heavy lifting for me. I pass in my templates, my posts, and some settings and it spits out an entire site almost instantly. It is remarkably robust.</p>
<blockquote>
<p>As an aside, Jekyll was actually what held me back for so long. It is <a href="http://jekyllrb.com/docs/windows/#installation">notoriously tricky</a> to get running on Windows and I wanted the ability to stage my site locally. So what changed? <a href="https://msdn.microsoft.com/commandline/wsl/about">Bash baby, Bash!</a> Jekyll is running happily on Windows 10 thanks to Windows Subsystem for Linux.</p>
</blockquote>
<p>GitHub provides a number of <a href="https://github.com/pages-themes/">built-in themes</a> but none of them caught my eye. Jekyll has a number of themes available as gem packages but these are unfortunately not supported by GitHub.</p>
<p>Thankfully I found Start Bootstrap’s <a href="https://github.com/BlackrockDigital/startbootstrap-clean-blog-jekyll">Clean Blog template</a> that provided exactly what I needed; a minimalist foundation using Bootstrap that I could easily extend. After a few tweaks, I was up and running in no time.</p>
<p>The most notable difference? My page load times are improved by orders of magnitude!</p>Marc LaFleurI’ve switched blogging platforms quite a few times over the years. I’ve tried everything from LiveJournal to Orchard and, for the last several years, WordPress. And for the most part, WordPress has severed me extremely well. It has a huge community with more plugins than you can through a stick at. It does, however, have its drawbacks; particularly it’s a rather sizable footprint and bloat.Office Update History2017-02-04T00:00:00+00:002017-02-04T00:00:00+00:00https://massivescale.com/office-update-history<p>I had an issue come up recently with a developer reporting that their Office Add-in wasn’t working properly in Outlook 2013. They had defined a number of Add-in Commands but one user was reporting that they weren’t showing up in their ribbon. After a little bit of digging, we uncovered the root cause was an unpatched version of Outlook. This feature was introduced in the December 2015 patch but the customer was still running the original GA build from 2012. The fix was simply to update Office 2013 to the most recent build.</p>
<p>With Office’s monthly release cadence, there are a log of versions floating out in the field. Understanding how “old” a given build is can be extremely helpful in tracking down unexpected add-in behavior. Thankfully there are some resources available:</p>
<ul>
<li><a href="https://technet.microsoft.com/en-us/office/mt465751">Office 2016 Releases</a></li>
<li><a href="https://support.office.com/en-us/article/Update-history-for-Office-2013-19214f38-85b7-4734-b2f8-a6a598bb0117?ui=en-US&rs=en-US&ad=US&fromAR=1">Update history for Office 2013</a></li>
</ul>
<p>You’ll notice that Office 2016 is a bit more complicated than 2013. This stems from the various “release channels” that a user may be pulling from. These work similar to Windows 10’s “rings” used by Windows Insider: Current (mainstream), First (slow ring), Insider (fast ring).</p>Marc LaFleurWith Office's monthly release cadence, there are a log of versions floating out in the field. Understanding how "old" a given build is can be extremely helpful in tracking down unexpected add-in behavior. Thankfully there are some resources available.v2 Endpoint & Implicit Grant2016-06-28T00:00:00+00:002016-06-28T00:00:00+00:00https://massivescale.com/microsoft-v2-endpoint-implicit-grant<p><em>This is a continuation of my <a href="#microsoft-v2-endpoint-series">Microsoft v2 Endpoint Series</a>. If you haven’t read this article yet, I highly recommend starting there. I will be glossing over several bits of configuration we previously covered.</em></p>
<p>First, a disclaimer. I am not a fan of the <a href="http://tools.ietf.org/html/rfc6749#section-1.3.2">Implicit Grant</a>. It has some severe limitations and is <a href="http://tools.ietf.org/html/rfc6749#section-10.3">less</a> <a href="http://tools.ietf.org/html/rfc6749#section-10.16">secure</a> than the <a href="http://tools.ietf.org/html/rfc6749#section-1.3.1">Authentication Code Grant</a>. There are absolutely cases where an Implicit Grant is required but in most cases it should be avoided whenever possible.</p>
<h2 id="what-is-the-implicit-grant-workflow">What is the Implicit Grant workflow</h2>
<p>The <a href="http://tools.ietf.org/html/rfc6749#section-1.3.2">Implicit Grant</a> is a simplified version of the <a href="http://tools.ietf.org/html/rfc6749#section-1.3.1">Authentication Code Grant</a>. It eliminates some steps from the process that would be impossible to support in certain scenarios. In particular, it eliminates the exchanging of an authentication code for a bearer token. Instead it returns the bearer token directly to the client but adding it as a <a href="https://en.wikipedia.org/wiki/Fragment_identifier">fragment identifier</a> to the Redirect URI.</p>
<blockquote>
<p>The term fragment identifier may be unfamiliar to a lot folks (I counted myself among them). Changes are they are you know what they’re for, most of us just didn’t know what they were called. Fragment IDs are similar to query params in that they are data appended the URI. Instead of using a question mark (<code class="language-plaintext highlighter-rouge">http://url.com?query-param</code>) they use a hash (<code class="language-plaintext highlighter-rouge">http://url.com#fragment-id</code>). So there you are fearless reader, your useless URI trivia for the day.</p>
</blockquote>
<p>To understand why this workflow is needed, it is helpful to consider the actors involved in the Authentication Code Grant workflow. As I covered in my <a href="/microsoft-v2-endpoint-primer">previous article</a>, we have three actors (the User, the Service and the Provider). Another way to describe these actors might be as the Browser, the Website and the Provider.</p>
<p>This Browser/Website/Provider accurately describes the actors within the context of a Web App. It doesn’t however align very well to other scenarios such a local/native application or a single-page application written entirely in JavaScript. Normally the Service holds the “Secret” that is used to convert the authentication code to a bearer token. In this scenario the secret is rather pointless since it is easily discoverable by the end-user. If the User knows the Application ID and the Secret why bother having the Secret in the first place? Enter the Implicit Grant workflow.</p>
<h2 id="supporting-implicit-grant">Supporting Implicit Grant</h2>
<p>In order to support Implicit Grants, you must have a Platform in your Application Registration configured to support it. Assuming you followed the registration process in the <a href="/microsoft-v2-endpoint-primer">Primer</a> this is simply a matter of checking off the “Allow Implicit Flow” option in the Web Platform Properties:</p>
<p><img src="/assets/images/apps-dev-web-platform.png" alt="App Registration - Web Platform Properties" /></p>
<p>Once this option is selected and saved, the v2 Endpoint will begin supporting Implicit Grants for your Application ID. From here we need to make a couple of adjustments to the token request method we used in the previous article.</p>
<h2 id="requesting-a-token">Requesting a Token</h2>
<p>The Implicit workflow has only two stages. The first is a redirect to the Provider which is where the user will enter their credentials. This process will return an Bearer Token back to the Client as a <a href="https://en.wikipedia.org/wiki/Fragment_identifier">fragment identifier</a> . The Client must then parse the token out of the URL and included with any API calls back to Microsoft.</p>
<p>The first call into the v2 Endpoint is a simple GET request (typically just a link the user clicks) to <code class="language-plaintext highlighter-rouge">https://login.microsoftonline.com/common/oauth2/v2.0/authorize</code> along with several query parameters:</p>
<table>
<thead>
<tr>
<th style="text-align: left">Property</th>
<th style="text-align: left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">client_id</code></td>
<td style="text-align: left">This is your Application ID from above</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">response_type</code></td>
<td style="text-align: left"> For this example it should always be “<strong>token</strong>”</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">redirect_uri</code></td>
<td style="text-align: left">This must be the same URI you entered earlier in the Platform configuration.</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">scope</code></td>
<td style="text-align: left">This tells the Provider what permissions you need for the APIs (more on this in a moment)</td>
</tr>
</tbody>
</table>
<p>The prototype for this call looks like this:</p>
<pre><code class="language-none">https://login.microsoftonline.com/common/oauth2/v2.0/authorize?
client_id=[APPLICATION ID]&response_type=token&
redirect_uri=[REDIRECT URI]&scope=[SCOPE]
</code></pre>
<blockquote>
<p><strong>Scopes</strong>: Because the Implicit Flow does not do a token exchange, scopes that depend on them such as offline_access and openid will simply be ignored by the Endpoint.</p>
</blockquote>
<p>Once the user has completed signing in, the Provider will redirect back to your Redirect URI. The Provider will add a <a href="https://en.wikipedia.org/wiki/Fragment_identifier">fragment identifier</a> this URI. You will need to extract these values from the URI to retrieve the Bearer Token.</p>
<p>The prototype of the Implicit Grant’s fragment ID is as follows:</p>
<pre><code class="language-none">[REDIRECT URI]#access_token=[ACCESS TOKEN]&token_type=Bearer
&expires_in=[EXPIRES]&scope=[SCOPE]
</code></pre>
<table>
<thead>
<tr>
<th style="text-align: left">Property</th>
<th style="text-align: left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">access_token</code></td>
<td style="text-align: left">This is the actual token.</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">expires_in</code></td>
<td style="text-align: left">Number of seconds until this token expires and can no longer be used.</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">token_type</code></td>
<td style="text-align: left">This tells you what type of token you have. It should always be “bearer”.</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">scope</code></td>
<td style="text-align: left">This is the list of scopes you have been granted access too.</td>
</tr>
</tbody>
</table>
<p>In you’re using JavaScript in a browser, you can grab this data from the URI using window.location.hash. If you’re using <a href="http://jquery.org">jQuery</a> then I would highly recommend looking into <a href="http://benalman.com/projects/jquery-bbq-plugin/">jQuery BBQ</a>.</p>
<h2 id="microsoft-v2-endpoint-series">Microsoft v2 Endpoint Series</h2>
<ul>
<li><a href="/microsoft-v2-endpoint-primer">Microsoft v2 Endpoint Primer</a></li>
<li><a href="/microsoft-v2-endpoint-implicit-grant">v2 Endpoint & Implicit Grant</a></li>
<li><a href="/microsoft-v2-endpoint-admin-consent">v2 Endpoint & Consent</a></li>
<li><a href="/microsoft-v2-endpoint-admin-consent">v2 Endpoint & Admin Consent</a></li>
</ul>
<h2 id="further-reading">Further Reading</h2>
<ul>
<li><a href="http://tools.ietf.org/html/rfc6749">The OAuth 2.0 Authorization Framework (RFC-6749)</a></li>
<li><a href="https://joonasw.net/view/azure-ad-v2-and-msal-from-dev-pov">Azure AD v2 and MSAL from a developer’s point of view</a> by Joonas Westlin</li>
<li><a href="http://tools.ietf.org/html/rfc6749#section-10.3">Access Token Security with Implicit Grants</a></li>
<li><a href="http://tools.ietf.org/html/rfc6749#section-10.16">Misuse of Access Token to Impersonate Resource Owner in Implicit Flow</a></li>
</ul>Marc LaFleurBuilding on my previous v2 Endpoint Primer, here we discuss using the Implicit Grant.Microsoft v2 Endpoint Primer2016-06-10T00:00:00+00:002016-06-10T00:00:00+00:00https://massivescale.com/microsoft-v2-endpoint-primer<p>Until recently Microsoft had two very distinct systems for authenticating users; <a href="https://en.wikipedia.org/wiki/Microsoft_account">Microsoft Account</a> (or MSA) and <a href="https://azure.microsoft.com/documentation/articles/active-directory-whatis/">Azure Active Directory</a> (or Azure AD). Both served the same purpose but for very different audiences; MSA for consumer services and Azure AD for enterprise services. For a while this separation worked reasonably well. While application required different integrations, most applications tended to sit squarely in one market or the other.</p>
<p>Over time however, the distinction between “Enterprise” and “Consumer” applications has eroded. Today we it is extremely common to find publishers targeting both markets with the same solution and users with multiple accounts for work and personal use. Having to support distinct authentication integrations quickly became a pain point. The solution to this pain is the <a href="https://azure.microsoft.com/documentation/articles/?product=active-directory&term=app+model+v2.0">Microsoft v2 Endpoint</a> (previously known as “Converged Authentication”).</p>
<h2 id="what-is-v2-endpoint">What is v2 Endpoint</h2>
<p>The v2 Endpoint allows applications to authenticate both Microsoft Accounts and Azure AD accounts using a single <a href="https://en.wikipedia.org/wiki/OAuth">OAUTH2</a> endpoint. This allows developers to build applications that are entirely account-agnostic. You no longer need to know which type of account the user owns. More importantly, you no longer need to ask to user to tell you what type of account they have (which to be honest, 90% of users likely have no idea how to answer).</p>
<p>Obviously there are some APIs may have different data sets depending on the account type. <a href="https://graph.microsoft.io/">Microsoft Graph</a> for example provides access to a number of APIs common to MSA and Azure AD accounts (Outlook, OneDrive, etc.) but some APIs only apply to enterprise accounts (Delve, SharePoint, etc.). To support these scenarios your application can determine the account type at run-time. The key difference here being that your application can pragmatically find the account type than relying on the user to tell your code.</p>
<h2 id="oauth--grant-types">OAUTH & Grant Types</h2>
<p>The v2 Endpoint uses OAUTH2 for authorization and supports most OAuth Grant types (<a href="https://docs.microsoft.com/azure/active-directory/develop/active-directory-v2-protocols-oauth-code">Authorization Code</a>, <a href="https://docs.microsoft.com/azure/active-directory/develop/active-directory-v2-protocols-implicit">Implicit</a> and <a href="https://docs.microsoft.com/azure/active-directory/develop/active-directory-v2-protocols-oauth-client-creds">Client Credentials</a>). If you’re not familiar with OAUTH (and OAUTH2 specifically), a Grant Types defines the workflow used for a particular OAUTH transaction. They all provide the same output, a token representing the authenticated user. Where they differ is how that token is obtained. In an effort to avoid going down a rabbit hole, I won’t go into the details of OAUTH and the various grant flows. I will instead point you to an <a href="https://aaronparecki.com/2012/07/29/2/oauth2-simplified">excellent article on the topic</a>.</p>
<p>OAUTH is based on verifiable trusts. The User trusts the OAUTH Provider (they opened an account there), the Service trusts the OAUTH Provider (they redirect the user there to authenticate) and the Provider trusts both the User and the Service. This is where Application Registration comes into play. Just as the Provider trusts the User because the User has an account on their system, the Service must do the same. Think of Application Registration as an account record for your application and just as the user has a “User ID”, your application will receive an “Application ID”.</p>
<p>In essence, we have a triangle with assumed trusts between the User and the Provider (the user’s account) and the Service and the Provider (the app registration).</p>
<p><img src="/assets/images/oauth-triangle-broken1.png" alt="oauth-triangle-broken" /></p>
<p>In order to complete the triangle and receive an authorization token we need to verify trust between the User and the Service.</p>
<p><img src="/assets/images/oauth-triangle-complete.png" alt="oauth-triangle-complete" /></p>
<p>That third leg of trust is represented by an authorization token passed between the User and the Service with each call.</p>
<h2 id="application-registration">Application Registration</h2>
<p>Microsoft provides a portal for registering your application at <a href="https://apps.dev.microsoft.com">https://apps.dev.microsoft.com</a>. After opening this page and logging in with your Microsoft Account, you are presented with a list of registered application and a button labeled “Add an app”.</p>
<blockquote class="tip">
<p>If you’ve used an Work/School Account (AAD) to login to <code class="language-plaintext highlighter-rouge">apps.dev.microsoft.com</code> then you will see two groups of applications: <strong>Converged applications</strong> and <strong>Azure AD only applications</strong>. The v2 Endpoint is a “Converged” endpoint, meaning that it supports both AAD and MSA accounts. For our purposes here, we will only be using <strong>Converged applications</strong>.</p>
</blockquote>
<p><img src="/assets/images/apps-dev-landing-page.png" alt="App Registration - Initial Page" /></p>
<p>Clicking this button will prompt you for the name of your application:</p>
<p><img src="/assets/images/apps-dev-new-app-dialog.png" alt="App Registration – New App Dialog" /></p>
<p>Once you’ve named your application and clicked “Create application” you are redirected to your application’s profile page.</p>
<blockquote>
<p><strong>v2 Registration vs. Azure AD Registration</strong></p>
<p>If you’ve worked with Azure AD in the past you will notice some similarities here. The general architecture is the same, the user experience however is far more straightforward. If you’ve not worked with Azure AD in the past then you’ll have to trust me, this <em>is</em> a simplified experience.</p>
</blockquote>
<p>The first item to take note of is the Application ID. This is one of the elements required for the OAUTH workflow. You will also see options for generating Client Secrets (aka Passwords). A single applications can have multiple Client Secrets assigned. This allows you to use the same registration for both traditional web sites, single-page apps and native clients.</p>
<p><img src="/assets/images/apps-dev-appid.png" alt="App Registration - Application ID" /></p>
<p>For this example I’m going to focus on the traditional web scenario. We will need two components for this workflow, a Password (aka Client Secret) and a Platform record. To get started we will need to click “Generate New Password”. This will open a dialog with your new Password. Save this in a safe place because once you close this dialog you <em>will never see this password again</em>.</p>
<p><img src="/assets/images/apps-dev-new-password.png" alt="App Registration - New Password" /></p>
<p>Next we will need to click “Add Platform”. This presents a dialog with two options. We’ll select “Web” for this example.</p>
<p><img src="/assets/images/apps-dev-new-platform.png" alt="App Registration - New Platform Dialog" /></p>
<p>This will add a new Web platform card to your registration page. This card has two properties, Allow Implicit Flow and Redirect URI. We will not be implementing the Implicit workflow in our example so you can uncheck that option. The Redirect URI is where the user will be redirected back to once they have authenticated. The value will depend on your implementation and if you’re using a framework (such as <a href="https://github.com/simov/grant">Grant</a>) you will need to check with the framework documentation. For my simple example here I am using redirecting to a local server at <code class="language-plaintext highlighter-rouge">http://localhost:3000/returned</code>.</p>
<p><img src="/assets/images/apps-dev-web-platform.png" alt=" App Registration - Web Platform Properties" /></p>
<blockquote class="tip">
<p>If you want to play around with the <a href="http://massivescale.com/microsoft-v2-endpoint-implicit-grant/">Implicit Grant</a> workflow, you can leave <code class="language-plaintext highlighter-rouge">Allow Implicit Flow</code> checked. It doesn’t impact the Authorization Code Grant workflow and I’ve only disabled it for the sake of completeness. Leaving it enabled won’t cause problem here. In production it should only be enabled if you truly need it since it does allow for a less secure workflow.</p>
</blockquote>
<h2 id="requesting-a-token">Requesting a Token</h2>
<p>In this example I’m going to focus on using the Authorization Code Grant workflow with a traditional web app/site. This is the most common of the grant type and is a bit more secure Implicit Grant workflow. If your application can support Authorization Code Grant than you app should use it. It is also the most complex of the workflows which makes it a great place to start. Once you understand how the Authorization workflows functions it is relatively easy to transition to the Implicit workflow.</p>
<p>The Authorization workflow has four components. The first is a redirect to the Provider which is where the user will enter their credentials. This process will return an Authorization Code back to the Service. The Service will then send an HTTP POST back to the Provider where that Authorization Code is converted into a Bearer Token. This Token is returned to the Service and will be included with any API calls back to Microsoft.</p>
<p><img src="/assets/images/oauth-steps.png" alt="oauth-steps" /></p>
<p>The first call into the v2 Endpoint is a simple GET request (typically just a link the user clicks) to <code class="language-plaintext highlighter-rouge">https://login.microsoftonline.com/common/oauth2/v2.0/authorize</code> along with several query parameters:</p>
<table>
<thead>
<tr>
<th style="text-align: left">Property</th>
<th style="text-align: left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">client_id</code></td>
<td style="text-align: left">This is your Application ID from above</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">response_type</code></td>
<td style="text-align: left">For this example it should always be “code”</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">redirect_uri</code></td>
<td style="text-align: left">This must be the same URI you entered earlier in the Platform configuration.</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">scope</code></td>
<td style="text-align: left">This tells the Provider what permissions you need for the APIs (more on this in a moment)</td>
</tr>
</tbody>
</table>
<p>The prototype for this call looks like this:</p>
<pre><code class="language-none">https://login.microsoftonline.com/common/oauth2/v2.0/authorize?
client_id=[APPLICATION ID]&response_type=code&
redirect_uri=[REDIRECT URI]&scope=[SCOPE]
</code></pre>
<blockquote>
<p><strong>Scopes</strong></p>
<p>The scope parameter is a space delimited list of permission scopes your application requires.</p>
<p>For example, if you are looking for permission to read the current user’s profile you would send <code class="language-plaintext highlighter-rouge">scope=User.Read</code>. If you also need access to the user’s inbox, you would add <code class="language-plaintext highlighter-rouge">Mail.Read</code>, changing your <code class="language-plaintext highlighter-rouge">scope</code> parameter to <code class="language-plaintext highlighter-rouge">scope=User.Read Mail.Read</code>.</p>
<p>In situations where you need to communicate with multiple APIs, specifying <code class="language-plaintext highlighter-rouge">User.Read</code> by itself could result in ambiguity between which API you’re requesting that scope from. To resolve this ambiguity, you can prefix each scope with the resource you’re requesting it from. For Microsoft Graph this is <code class="language-plaintext highlighter-rouge">https://graph.microsoft.com</code>. Using the example scopes above, your <code class="language-plaintext highlighter-rouge">scope</code> parameter would be</p>
<pre><code class="language-none">scope=https://graph.microsoft.com/User.Read https://graph.microsoft.com/Mail.Read
</code></pre>
<p>For more information on scopes and what access they provide, please see <a href="https://developer.microsoft.com/graph/docs/concepts/permissions_reference">Microsoft Graph permission scopes</a>.</p>
</blockquote>
<p>Once the user has completed signing in, the Provider will redirect back to your Redirect URI. The Provider will add a “code” query parameter to this URI. The value of this parameter is your authorization code. You will need to extract this value from the URI so you can use it in the next stage, requesting the Bearer Token.</p>
<p>Once you have the Authorization Code you will need to make an HTTP POST back to the provider. The POST’s body must be encoded as “application/x-www-form-urlencoded” and contain the following parameters:</p>
<table>
<thead>
<tr>
<th style="text-align: left">Property</th>
<th style="text-align: left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">grant_type</code></td>
<td style="text-align: left">Should be authorization_code</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">code</code></td>
<td style="text-align: left">The auth code you received from the Provider</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">client_id</code></td>
<td style="text-align: left">This is your Application ID from above</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">client_secret</code></td>
<td style="text-align: left">This is the Password we generated previously</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">scope</code></td>
<td style="text-align: left">This should match the same set of scopes you initial requested</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">redirect_uri</code></td>
<td style="text-align: left">This is the redirect URI defined in your application registration</td>
</tr>
</tbody>
</table>
<p>This body will be POSTed up to <code class="language-plaintext highlighter-rouge">https://login.microsoftonline.com/common/oauth2/v2.0/token</code>. The prototype for this call should look like:</p>
<pre><code class="language-none">https://login.microsoftonline.com/common/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=[AUTHORIZATION CODE]&
client_id=[APPLICATION ID]&
client_secret=[PASSWORD]&
scope=[SCOPE]&
redirect_uri=[REDIRECT URI]
</code></pre>
<p>Once the Provider has processed this request, it will return a JSON object containing the following properties:</p>
<table>
<thead>
<tr>
<th style="text-align: left">Property</th>
<th style="text-align: left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">access_token</code></td>
<td style="text-align: left">This is the actual token.</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">expires_in</code></td>
<td style="text-align: left">Number of seconds until this token expires and can no longer be used.</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">token_type</code></td>
<td style="text-align: left">This tells you what type of token you have. It should always be “bearer”.</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">scope</code></td>
<td style="text-align: left">This is the list of scopes you have been granted access too.</td>
</tr>
</tbody>
</table>
<p><strong>Example JSON</strong></p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="dl">"</span><span class="s2">access_token</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">eyJ0eXAiOiJKV1QiLCJ...</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">expires_in</span><span class="dl">"</span><span class="p">:</span> <span class="mi">3599</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">token_type</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Bearer</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">scope</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">https://graph.microsoft.com/mail.read https://graph.microsoft.com/user.read</span><span class="dl">"</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="refreshing-a-token">Refreshing a Token</h2>
<p>By default, Access/Bearer tokens have a lifetime of 1 hour. After this time they are no longer valid. There are two options at this point, you can ask the user to re-authenticate (less than ideal) or you can use a Refresh Token to get an updated token.</p>
<p>Refresh Tokens are only returned when you include offline_access in your first scopes list. This is a special scope that does not need a full URI. Adding this scope will result in an additional property called refresh_token being returned by the provider. This refresh_token can be used to repeat the previous POST process to retrieve a newly minted bearer token.</p>
<blockquote class="info">
<p>Refresh Tokens are also only available in the Authorization Code workflow. If you are using the Implicit workflow you are limited to the initial lifetime of the token. Refresh Tokens operation similarly to the initial Authorization Code, they are exchanged with the Provider for an updated bearer token. Given that the Implicit Grant was designed to skip the Authorization Code exchange, they also cannot participate in the Refresh Token exchange.</p>
</blockquote>
<p>To exorcise your Refresh Token, we need to make another HTTP POST back to the provider. The POST’s body must be encoded as <a href="https://en.wikipedia.org/wiki/Percent-encoding#The_application.2Fx-www-form-urlencoded_type">application/x-www-form-urlencoded</a>” and contain the following parameters:</p>
<table>
<thead>
<tr>
<th style="text-align: left">Property</th>
<th style="text-align: left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">grant_type</code></td>
<td style="text-align: left">Must be refresh_token</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">refresh_token</code></td>
<td style="text-align: left">The refresh token value you received from the Provider</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">client_id</code></td>
<td style="text-align: left">This is your Application ID from above</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">client_secret</code></td>
<td style="text-align: left">This is the Password we generated before</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">scope</code></td>
<td style="text-align: left">This should match the same set of scopes you first requested</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">redirect_uri</code></td>
<td style="text-align: left">This is the redirect URI defined in your application registration</td>
</tr>
</tbody>
</table>
<p>This body will be POSTed up to <a href="https://login.microsoftonline.com/common/oauth2/v2.0/token">https://login.microsoftonline.com/common/oauth2/v2.0/token</a>. The prototype for this call should look like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://login.microsoftonline.com/common/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&
refresh_token=[REFRESH TOKEN]&
client_id=[APPLICATION ID]&
client_secret=[PASSWORD]&
scope=[SCOPE]&
redirect_uri=[REDIRECT URI]
</code></pre></div></div>
<p>Once the Provider has processed this request, it will return a JSON object containing the following properties:</p>
<table>
<thead>
<tr>
<th style="text-align: left">Property</th>
<th style="text-align: left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">access_token</code></td>
<td style="text-align: left">This is the actual token.</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">expires_in</code></td>
<td style="text-align: left">Number of seconds until this token expires and can no longer be used.</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">token_type</code></td>
<td style="text-align: left">This tells you what type of token you have. It should always be “bearer”.</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">scope</code></td>
<td style="text-align: left">This is the list of scopes you have been granted access too.</td>
</tr>
<tr>
<td style="text-align: left"><code class="language-plaintext highlighter-rouge">refresh_token</code></td>
<td style="text-align: left">A new refresh token to be used for the next round</td>
</tr>
</tbody>
</table>
<p><strong>Example JSON</strong></p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="dl">"</span><span class="s2">access_token</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">eyJ0eXAiOiJKV1QiLCJ...</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">expires_in</span><span class="dl">"</span><span class="p">:</span> <span class="mi">3599</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">token_type</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Bearer</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">scope</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">https://graph.microsoft.com/mail.read https://graph.microsoft.com/user.read</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">refresh_token</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">OAAABAAAAiL9Kn2Z27...</span><span class="dl">"</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="token-lifetime">Token Lifetime</h2>
<p>Each of the tokens described above (auth_code, access_token, refresh_token) have a defined lifetime. Once they have expired they can no longer be used. This is important to understand when you architect your authorization workflow. You need to have a strategy for handling initial authorization, invalid tokens, refreshing of tokens, etc. With refresh tokens in particular, you may need a background process automatically handling token renewal prior to them expiring.</p>
<p><strong>Microsoft Account (MSA) Tokens:</strong></p>
<table>
<thead>
<tr>
<th style="text-align: left">Token</th>
<th style="text-align: left">Lifespan</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">Authorization Codes</td>
<td style="text-align: left">5 minutes</td>
</tr>
<tr>
<td style="text-align: left">Access Tokens</td>
<td style="text-align: left">1 hour</td>
</tr>
<tr>
<td style="text-align: left">Refresh Tokens</td>
<td style="text-align: left">up to 1 year</td>
</tr>
</tbody>
</table>
<p><strong>Azure Active Directory (AAD) Tokens:</strong></p>
<table>
<thead>
<tr>
<th style="text-align: left">Token</th>
<th style="text-align: left">Lifespan</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">Authorization Codes</td>
<td style="text-align: left">10 minutes</td>
</tr>
<tr>
<td style="text-align: left">Access Tokens</td>
<td style="text-align: left">1 hour</td>
</tr>
<tr>
<td style="text-align: left">Refresh Tokens</td>
<td style="text-align: left">Until Revoked</td>
</tr>
</tbody>
</table>
<h2 id="example-application">Example Application</h2>
<p>I’ve created a very simple Node.js application using Express and Request that walks you through the entire workflow (including refreshing the token). The entire application is available on GitHub at <a href="https://github.com/mlafleur/node-v2endpoint-example">https://github.com/mlafleur/node-v2endpoint-example</a>.</p>
<p>In order to use this sample you will need to complete the Application Registration process outlined above. Once completed, you will need to add you Application ID and Password to the variables defined at the top of <a href="https://github.com/mlafleur/node-v2endpoint-example/blob/master/index.js">index.js</a>. Running the sample will spin up a local web server at <code class="language-plaintext highlighter-rouge">http://localhost:3000</code>. Browsing to this page will present a page with a single link. This link represents the first stage (redirection to the Provider) of the process. Once the token is obtained it is rendered in the browser (along with a link to trigger a token refresh).</p>
<h2 id="microsoft-v2-endpoint-series">Microsoft v2 Endpoint Series</h2>
<ul>
<li><a href="/microsoft-v2-endpoint-primer">Microsoft v2 Endpoint Primer</a></li>
<li><a href="/microsoft-v2-endpoint-implicit-grant">v2 Endpoint & Implicit Grant</a></li>
<li><a href="/microsoft-v2-endpoint-admin-consent">v2 Endpoint & Consent</a></li>
<li><a href="/microsoft-v2-endpoint-admin-consent">v2 Endpoint & Admin Consent</a></li>
</ul>
<h2 id="further-reading">Further Reading</h2>
<ul>
<li><a href="https://joonasw.net/view/azure-ad-v2-and-msal-from-dev-pov">Azure AD v2 and MSAL from a developer’s point of view</a> by Joonas Westlin</li>
</ul>Marc LaFleurFor some time now, Microsoft has had two distinct systems for authenticating users; Microsoft Account (or MSA) and Azure Active Directory (or Azure AD); MSA for consumer services and Azure AD for enterprise services. The v2 Endpoint allow applications to authenticate both Microsoft Accounts and Azure AD accounts using a single OAUTH 2 endpoint. This allows developers to build applications that are entirely account-agnostic. This article covers the basics of using the v2 Endpoint and OAUTH2 to authenticate users.Super Simple Outlook Add-in2016-03-16T00:00:00+00:002016-03-16T00:00:00+00:00https://massivescale.com/super-simple-outlook-add-in<p>I spend the bulk of my life working on <a href="http://dev.office.com">Office Extensibility</a> projects, otherwise known as <a href="https://docs.microsoft.com/office/dev/add-ins/">Office Add-ins</a>. Add-Ins allow developers to extend the functionality of Office clients (Word, PowerPoint, Excel, Outlook, etc.).</p>
<p>Of all of the Office extensibility stories, my personal favorite is <a href="https://dev.office.com/docs/add-ins/outlook/overview">Outlook</a>. As much as I love all of the new communication tools that have cropped up in recent years, Outlook is still where I spend the bulk of my time. It remains the system of record for the torrent of emails, appointments and contacts I have to manage each day.</p>
<p>The best part about building these add-ins, they are incredibly simple to build. They are stripped down web apps. If you have an API and some basic HTML/JS chops, then you’re well on your way.</p>
<p>In fact, I’m going to show you just how easy this is. I’m going to build a very simple add-in using a single HTML page, a couple of images assets and an XML manifest file that tells Outlook what to do.</p>
<p>To start, we need to put together the list of libraries we need. Some are required, others we’re using to make life a little easier. In each case however we are leveraging a 3rd party’s CDN to host them.</p>
<ul>
<li>
<p><a href="http://jquery.com/">JQuery</a> - jquery is so common it may as well be baked into browsers at this point (required).</p>
</li>
<li>
<p><a href="https://dev.office.com/reference/add-ins/javascript-api-for-office">Office.js</a> - this is the core Office lib and provides the API and object model we need for interacting with Outlook (required).</p>
</li>
<li>
<p><a href="https://github.com/janl/mustache.js">Mustache.js</a> - mustache is a stripped down HTML/JS template engine that makes rendering list of objects a heck of a lot easier (optional).</p>
</li>
<li>
<p><a href="http://dev.office.com/fabric">Office UI Fabric</a> - a UX framework designed for Office add-ins. I’m using this to handle rendering my Persona Cards (optional).</p>
</li>
</ul>
<p>My add-in here will be very simple. I’m going to inspect an email or appointment and render anyone on the from, to or cc line as a <a href="http://dev.office.com/fabric/components/personacard">Persona Card</a> using the Office UI Fabric. All of this will be done in a single HTML page. It will support the bare-bones API ensuring this add-in will work across platforms (Outlook for Windows, Outlook for Mac, Outlook.com, OWA, etc.).</p>
<p>To start, we’ll build out the basic page framework:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html></span>
<span class="nt"><head></span>
<span class="nt"><title></title></span>
<span class="nt"><meta</span> <span class="na">charset=</span><span class="s">"utf-8"</span> <span class="nt">/></span>
<span class="c"><!-- Office UX Frabic --></span>
<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"//appsforoffice.microsoft.com/fabric/2.0.1/fabric.min.css"</span><span class="nt">></span>
<span class="nt"><link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"//appsforoffice.microsoft.com/fabric/2.0.1/fabric.components.min.css"</span><span class="nt">></span>
<span class="nt"></head></span>
<span class="nt"><body</span> <span class="na">class=</span><span class="s">"ms-font-m"</span><span class="nt">></span>
<span class="nt"><div></span>
<span class="c"><!-- Page Content --></span>
<span class="nt"></div></span>
<span class="c"><!-- JQuery v2.2.0 (via ASP.NET's' CDN) --></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"//ajax.aspnetcdn.com/ajax/jQuery/jquery-2.2.0.min.js"</span><span class="nt">></script></span>
<span class="c"><!-- Office.js v1 (via Office's' CDN) --></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"//appsforoffice.microsoft.com/lib/1/hosted/office.js"</span> <span class="na">type=</span><span class="s">"text/javascript"</span><span class="nt">></script></span>
<span class="c"><!-- Mustache.js v2.2.1 (via CouldFlare's' CDN) --></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"//cdnjs.cloudflare.com/ajax/libs/mustache.js/2.2.1/mustache.min.js"</span><span class="nt">></script></span>
<span class="nt"><script </span><span class="na">type=</span><span class="s">"text/javascript"</span><span class="nt">></span>
<span class="c1">// Office.initialize fires as soon as our add-in is wired up and ready to execute</span>
<span class="nx">Office</span><span class="p">.</span><span class="nx">initialize</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">reason</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="p">});</span>
<span class="p">};</span>
<span class="nt"></script></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre></div></div>
<p>Next we’ll add a <code class="language-plaintext highlighter-rouge"><div></code> to hold our Persona Cards. We’ll use the ms-Grid class to manage rendering the cards in a responsive grid.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"ms-Grid"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"ms-Grid-row"</span> <span class="na">id=</span><span class="s">"persona-cards"</span><span class="nt">></span>
<span class="nt"></div></span>
<span class="nt"></div></span>
</code></pre></div></div>
<p>We also need to add a template for rendering each persona card. This is where we’ll leverage mustache.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<!-- Mustache template we'll use for rendering each Persona Cards -->
<script id="template" type="x-tmpl-mustache">
<div class="ms-Grid-col" style="padding:5px">
<div class="ms-PersonaCard">
<div class="ms-PersonaCard-persona">
<div class="ms-Persona ms-Persona--xl">
<div class="ms-Persona-imageArea">
<div class="ms-Persona-initials ms-Persona-initials--blue">{{initials}}</div>
</div>
<div class="ms-Persona-details">
<div class="ms-Persona-primaryText">{{name}}</div>
<div class="ms-Persona-secondaryText">{{address}}</div>
<div class="ms-Persona-tertiaryText">{{subject}}</div>
</div>
</div>
</div>
</div>
</div>
</script>
</code></pre></div></div>
<p>Finally, we will execute some javascript during initialization that will parse the recipients. This is done by making calls to the Office.js API asking for specific metadata from the current mail item.</p>
<blockquote>
<p><strong>Note</strong>: We need determine if the <code class="language-plaintext highlighter-rouge">itemType</code> is a <code class="language-plaintext highlighter-rouge">Office.MailboxEnums.ItemType.Message</code> or <code class="language-plaintext highlighter-rouge">Office.MailboxEnums.ItemType.Appointment</code> since they have uniquire property sets. Where Messages contain <code class="language-plaintext highlighter-rouge">from</code>, <code class="language-plaintext highlighter-rouge">to</code> and <code class="language-plaintext highlighter-rouge">cc</code> properties, Appointments contain <code class="language-plaintext highlighter-rouge">organizer</code>, <code class="language-plaintext highlighter-rouge">requiredAttendees</code> and <code class="language-plaintext highlighter-rouge">optionalAttendees</code>. In the ene, we render them using the same template, we simply populate the template fields with the approtiate property set.</p>
</blockquote>
<p>After building up an array of recipients, we use mustache to render our template and append this to the ms-Grid row we defined earlier.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><script </span><span class="na">type=</span><span class="s">"text/javascript"</span><span class="nt">></span>
<span class="c1">// Compile the mustache template </span>
<span class="kd">var</span> <span class="nx">template</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#template</span><span class="dl">'</span><span class="p">).</span><span class="nx">html</span><span class="p">();</span>
<span class="c1">// Office.initialize fires as soon as our add-in is wired up and ready to execute</span>
<span class="nx">Office</span><span class="p">.</span><span class="nx">initialize</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">reason</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="c1">// Parse the Mustache template (optional but speeds things up)</span>
<span class="nx">Mustache</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">template</span><span class="p">);</span>
<span class="kd">var</span> <span class="k">from</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">to</span> <span class="o">=</span> <span class="p">[];</span>
<span class="kd">var</span> <span class="nx">cc</span> <span class="o">=</span> <span class="p">[];</span>
<span class="c1">// Recast the item to make things easier to work with </span>
<span class="kd">var</span> <span class="nx">item</span> <span class="o">=</span> <span class="nx">Office</span><span class="p">.</span><span class="nx">cast</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">toItemRead</span><span class="p">(</span><span class="nx">Office</span><span class="p">.</span><span class="nx">context</span><span class="p">.</span><span class="nx">mailbox</span><span class="p">.</span><span class="nx">item</span><span class="p">);</span>
<span class="c1">// If this is an Email Maessage </span>
<span class="k">if</span> <span class="p">(</span><span class="nx">item</span><span class="p">.</span><span class="nx">itemType</span> <span class="o">===</span> <span class="nx">Office</span><span class="p">.</span><span class="nx">MailboxEnums</span><span class="p">.</span><span class="nx">ItemType</span><span class="p">.</span><span class="nx">Message</span><span class="p">)</span> <span class="p">{</span>
<span class="k">from</span> <span class="o">=</span> <span class="nx">Office</span><span class="p">.</span><span class="nx">cast</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">toMessageRead</span><span class="p">(</span><span class="nx">item</span><span class="p">).</span><span class="k">from</span><span class="p">;</span>
<span class="nx">to</span> <span class="o">=</span> <span class="nx">Office</span><span class="p">.</span><span class="nx">cast</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">toMessageRead</span><span class="p">(</span><span class="nx">item</span><span class="p">).</span><span class="nx">to</span><span class="p">;</span>
<span class="nx">cc</span> <span class="o">=</span> <span class="nx">Office</span><span class="p">.</span><span class="nx">cast</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">toMessageRead</span><span class="p">(</span><span class="nx">item</span><span class="p">).</span><span class="nx">cc</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Else of this is an Appointment</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">item</span><span class="p">.</span><span class="nx">itemType</span> <span class="o">===</span> <span class="nx">Office</span><span class="p">.</span><span class="nx">MailboxEnums</span><span class="p">.</span><span class="nx">ItemType</span><span class="p">.</span><span class="nx">Appointment</span><span class="p">)</span> <span class="p">{</span>
<span class="k">from</span> <span class="o">=</span> <span class="nx">Office</span><span class="p">.</span><span class="nx">cast</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">toAppointmentRead</span><span class="p">(</span><span class="nx">item</span><span class="p">).</span><span class="nx">organizer</span><span class="p">;</span>
<span class="nx">to</span> <span class="o">=</span> <span class="nx">Office</span><span class="p">.</span><span class="nx">cast</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">toAppointmentRead</span><span class="p">(</span><span class="nx">item</span><span class="p">).</span><span class="nx">requiredAttendees</span><span class="p">;</span>
<span class="nx">cc</span> <span class="o">=</span> <span class="nx">Office</span><span class="p">.</span><span class="nx">cast</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">toAppointmentRead</span><span class="p">(</span><span class="nx">item</span><span class="p">).</span><span class="nx">optionalAttendees</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Render the Persona Cards for everyone involved</span>
<span class="nx">renderPersona</span><span class="p">(</span><span class="k">from</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="k">in</span> <span class="nx">to</span><span class="p">)</span> <span class="p">{</span> <span class="nx">renderPersona</span><span class="p">(</span><span class="nx">to</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span> <span class="p">};</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="k">in</span> <span class="nx">cc</span><span class="p">)</span> <span class="p">{</span> <span class="nx">renderPersona</span><span class="p">(</span><span class="nx">cc</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span> <span class="p">};</span>
<span class="p">});</span>
<span class="c1">// This function handles rendering of the persona card</span>
<span class="c1">// including generation of the recipient's initials </span>
<span class="kd">function</span> <span class="nx">renderPersona</span><span class="p">(</span><span class="nx">persona</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">initials</span> <span class="o">=</span> <span class="nx">persona</span><span class="p">.</span><span class="nx">displayName</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/</span><span class="se">\b\w</span><span class="sr">/g</span><span class="p">);</span>
<span class="nx">initials</span> <span class="o">=</span> <span class="p">(</span><span class="nx">initials</span><span class="p">.</span><span class="nx">shift</span><span class="p">()</span> <span class="o">+</span> <span class="nx">initials</span><span class="p">.</span><span class="nx">pop</span><span class="p">()).</span><span class="nx">toUpperCase</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">rendered</span> <span class="o">=</span> <span class="nx">Mustache</span><span class="p">.</span><span class="nx">render</span><span class="p">(</span><span class="nx">template</span><span class="p">,</span> <span class="p">{</span>
<span class="na">initials</span><span class="p">:</span> <span class="nx">initials</span><span class="p">,</span>
<span class="na">name</span><span class="p">:</span> <span class="nx">persona</span><span class="p">.</span><span class="nx">displayName</span><span class="p">,</span>
<span class="na">address</span><span class="p">:</span> <span class="nx">persona</span><span class="p">.</span><span class="nx">emailAddress</span><span class="p">,</span>
<span class="p">});</span>
<span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#persona-cards</span><span class="dl">'</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="nx">rendered</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="nt"></script></span>
</code></pre></div></div>
<p>We then define a manifest file that tells Outlook the who, what, where and when of rendering this add-in. This is pretty self-explanatory and mostly boilerplate so I won’t go into detail here. For reference however, you can see the finished manifest on <a href="https://github.com/mlafleur/super-simple-outlook-add-in/blob/master/manifest.xml">GitHub</a>.</p>
<p>This entire project lives is available at <a href="https://github.com/mlafleur/super-simple-outlook-add-in.">https://github.com/mlafleur/super-simple-outlook-add-in.</a></p>
<p>If your interested in trying this out for yourself, you can see it in action. You can <a href="https://outlook.office.com/owa/?path=/options/manageapps">manage add-ins from your O365 mailbox settings</a>. From here, select “Add from URL” and paste in <a href="https://officekiss.azurewebsites.net/manifest.xml"><code class="language-plaintext highlighter-rouge">https://officekiss.azurewebsites.net/manifest.xml</code></a>. You will then see Persona Cards show up in all of your Outlook clients.</p>Marc LaFleurBuilding a very simple add-in using just a single HTML page, a couple of images assets and an XML manifest file that tells Outlook what to do. All stored on GitHub and hosted on Azure for free.Edge Zoom Level2015-09-14T00:00:00+00:002015-09-14T00:00:00+00:00https://massivescale.com/edge-zoom-level<p>I recently ran into a odd problem where the Edge browser’s default zoom level got stuck at 80%. The frustrating bit was that changing it only effected the current page, clicking any link to a new page resulted that page displaying at 80%. In my search for a fix I found a <a href="http://answers.microsoft.com/en-us/windows/forum/apps_windows_10-msedge/microsoft-edge-keep-changing-page-zoom/a6ea0236-cfd6-471e-b050-4f8776d257da">few others</a> with the <a href="http://forums.windowscentral.com/windows-10/381291-edge-default-page-zoom-windows-10-a.html">same issue</a> (although everyone seems to have a different percentage, and most seemed to be > 100%).</p>
<p>Before I explain how I resolved the problem there are a couple of vital caveats:</p>
<ul>
<li>
<p>I’m running an <a href="https://insider.windows.com/">Insider Build</a> so issues like this, while admittedly frustrating, are not expected. I signed up to eat the dog food, I can’t really complain when it tastes like…. well, dog food.</p>
</li>
<li>
<p>Fixing this requires editing the registry. <strong>If you are not sure you should do this then you shouldn’t</strong>. Period. End of story. Got that? Going to ignore my warning anyway? No worries. I’ve got kids, I’m used to having my warnings ignored.</p>
</li>
</ul>
<p>At the moment I’m unclear exactly how I got into a broken zoom state, but the fix seems to have held. I’ve been trying for a while to reproduce the problem but at the moment it seems to be holding steady.</p>
<p>Edge is a UWP application. As such, it’s registry settings are a bit buried; they live under HKEY_CLASSES_ROOT rather than CURRENT_USER or LOCAL_MACHINE. In particular they can be found at <code class="language-plaintext highlighter-rouge">HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Storage\microsoft.microsoftedge_8wekyb3d8bbwe\MicrosoftEdge</code>.</p>
<p><img src="/assets/images/registry-edge-1.png" alt="registry-edge-1" /></p>
<p>From there you will find several subkeys, including one named Zoom. Zoom has a single DWORD value named ZoomFactor.</p>
<p><img src="/assets/images/registry-edge-2.png" alt="registry-edge-2" /></p>
<p>The value directly relates to the zoom percentage (which in my case was 80,000). Setting this value to 100,000 results in a 100% zoom.</p>
<p><img src="/assets/images/registry-edge-3.png" alt="registry-edge-3" /></p>
<p>Once I changes this to 100000, Edge began giving me the default I expected. For those looking for the “quick fix”, here is a Gist of the regex export:</p>
<script src="https://gist.github.com/fc183f36e4ac85e926d5.js"> </script>Marc LaFleurI recently ran into a odd problem where the Edge browser's default zoom level got stuck at 80%. This is how I fixed it. Full disclosure, this happened on an Insider build. As I signed up to eat this dog food, I can't really complain when it tastes like... well, dog food.