{"id":2098,"date":"2013-04-16T23:44:51","date_gmt":"2013-04-17T08:44:51","guid":{"rendered":"http:\/\/www.cloudidentity.com\/blog\/?p=2098"},"modified":"2013-04-17T14:33:43","modified_gmt":"2013-04-17T23:33:43","slug":"the-windows-azure-ad-application-model","status":"publish","type":"post","link":"https:\/\/www.cloudidentity.com\/blog\/2013\/04\/16\/the-windows-azure-ad-application-model\/","title":{"rendered":"The Windows Azure AD Application Model"},"content":{"rendered":"<p>In the various <a href=\"https:\/\/www.cloudidentity.com\/blog\/2013\/04\/08\/windows-azure-active-directory-reaches-general-availability\/\">announcements<\/a> and <a href=\"https:\/\/www.cloudidentity.com\/blog\/2013\/04\/08\/walkthrough-1-adding-sign-on-to-your-web-application-using-windows-azure-ad\/\">walkthroughs<\/a> you had the chance to experience the changes in Windows Azure AD\u2019s product surface. In this (hopefully not too long, it\u2019s midnight already) post I am going to touch on some of the deeper changes that took place beneath the surface. Albeit less evident at first, some of those have far-reaching consequences you should be aware of \u2013 especially if you plan to write multitenant applications.<\/p>\n<h3>Applications vs. ServicePrincipal<\/h3>\n<p>Remember&nbsp; the <a href=\"https:\/\/www.cloudidentity.com\/blog\/2012\/07\/12\/single-sign-on-with-windows-azure-active-directory-a-deep-dive-2\/\">mega post<\/a> (<em>oh pioneers!<\/em>) I wrote when we released the first Web SSO preview, or any of the <a href=\"http:\/\/channel9.msdn.com\/Events\/Patterns-Practices-Symposium-Online\/pattern-practices-symposium-2013\/Windows-Azure-Active-Directory\">presentation recordings<\/a> since then?<br \/>One of the key concepts introduced at the time was the idea of ServicePrincipal: an entry in your Windows Azure AD tenant, much like the traditional User Principals, used to describe applications. At the time you just had PowerShell cmdlets to provision applications, and there was no way for you to avoid operating at the ServicePrincipal level.<\/p>\n<p>ServicePrincipals are still the way in which applications are concretely provisioned in a directory. For example, it is the set of AD roles an application\u2019s ServicePrincipal belongs to that determines what the application can do in term of directory access (SSO only, SSO+read access, etc.). <br \/>That said: with the GA release, Windows Azure AD introduced a further abstraction level which decouples high level application definition operations from the low-level provisioning of ServicePrincipals.<\/p>\n<p>When you go through an application registration flow in the Windows Azure portal, such as the one described <a href=\"https:\/\/www.cloudidentity.com\/blog\/2013\/04\/08\/walkthrough-1-adding-sign-on-to-your-web-application-using-windows-azure-ad\/\">here<\/a>, you are really doing the moral equivalent of two distinct operations in one.<\/p>\n<ul>\n<li>You are creating an object of type <strong>Application<\/strong>, which describes the main application coordinates (such as URL to use for Web SSO, app id URI to identify the app, client ID and key to be used in OAuth2 flows for invoking the Graph, etc.) and come config settings (such as if the app is single tenant or available for other tenants via consent flows)\n<li>In the same process, <em>the portal is using that Application object as a blueprint for creating a new ServicePrincipal <\/em>in your tenant. Such ServicePrincipal will have the same coordinates (URLs, URIs, IDs, keys) as the corresponding <strong>Application <\/strong>and will also have the access level (SSO, SSO+read, SSO+read+write) you established at creation <\/li>\n<\/ul>\n<p>The advantage of that will become clear in a moment; before delving into that, let\u2019s stop for a moment and see the above in practice.<\/p>\n<p>Head to your own portal and create an test application. Below you can see what I used for mine:<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/04\/image18.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/04\/image_thumb18.png\" width=\"579\" height=\"757\"><\/a><\/p>\n<p>If I take a look at the entities in the directory after I\u2019ve done that, I\u2019ll find in the applications collection the following new entry:<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre id=\"codeSnippet\" style=\"border-top-style: none; overflow: visible; font-size: 8pt; border-left-style: none; font-family: 'Courier New', courier, monospace; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; width: 100%; background-color: #f4f4f4\">    {<br>      <span style=\"color: #006080\">\"odata.type\"<\/span>: <span style=\"color: #006080\">\"Microsoft.WindowsAzure.ActiveDirectory.Application\"<\/span>,<br>      <span style=\"color: #006080\">\"objectType\"<\/span>: <span style=\"color: #006080\">\"Application\"<\/span>,<br>      <span style=\"color: #006080\">\"objectId\"<\/span>: <span style=\"color: #006080\">\"b4d66176-4654-4b7d-892e-d3b564bc7910\"<\/span>,<br>      <span style=\"color: #006080\">\"appId\"<\/span>: <span style=\"color: #006080\">\"7042160d-2d58-4528-878f-b05b0edc799e\"<\/span>,<br>      <span style=\"color: #006080\">\"availableToOtherTenants\"<\/span>: <span style=\"color: #0000ff\">false<\/span>,<br>      <span style=\"color: #006080\">\"displayName\"<\/span>: <span style=\"color: #006080\">\"My test app\"<\/span>,<br>      <span style=\"color: #006080\">\"errorUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"homepage\"<\/span>: <span style=\"color: #006080\">\"https:\/\/localhost:2121\/\"<\/span>,<br>      <span style=\"color: #006080\">\"identifierUris\"<\/span>: [<br>        <span style=\"color: #006080\">\"https:\/\/cloudidentity.net\/testapp1\"<\/span><br>      ],<br>      <span style=\"color: #006080\">\"keyCredentials\"<\/span>: [],<br>      <span style=\"color: #006080\">\"logoutUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"passwordCredentials\"<\/span>: [],<br>      <span style=\"color: #006080\">\"publicClient\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"replyUrls\"<\/span>: [<br>        <span style=\"color: #006080\">\"https:\/\/localhost:2121\/\"<\/span><br>      ],<br>      <span style=\"color: #006080\">\"samlMetadataUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span><br>    }<\/pre>\n<\/div>\n<p>Pretty interesting stuff <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" style=\"border-top-style: none; border-left-style: none; border-bottom-style: none; border-right-style: none\" alt=\"Smile\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/04\/wlEmoticon-smile5.png\"> I won\u2019t (yet) go into the details of everything you see there, but for the time being: please note that it closely mirrors the coordinates provided in the portal.<\/p>\n<p>Now, let\u2019s take a peek at the ServicePrincipals collection.&nbsp; Among the many built-in principals you\u2019ll find the following entry:<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre id=\"codeSnippet\" style=\"border-top-style: none; overflow: visible; font-size: 8pt; border-left-style: none; font-family: 'Courier New', courier, monospace; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; width: 100%; background-color: #f4f4f4\">{<br>     <span style=\"color: #006080\">\"odata.type\"<\/span>: <span style=\"color: #006080\">\"Microsoft.WindowsAzure.ActiveDirectory.ServicePrincipal\"<\/span>,<br>     <span style=\"color: #006080\">\"objectType\"<\/span>: <span style=\"color: #006080\">\"ServicePrincipal\"<\/span>,<br>     <span style=\"color: #006080\">\"objectId\"<\/span>: <span style=\"color: #006080\">\"a3518a24-2017-4c63-89d3-adec4eeaa9ad\"<\/span>,<br>     <span style=\"color: #006080\">\"accountEnabled\"<\/span>: <span style=\"color: #0000ff\">true<\/span>,<br>     <span style=\"color: #006080\">\"appId\"<\/span>: <span style=\"color: #006080\">\"7042160d-2d58-4528-878f-b05b0edc799e\"<\/span>,<br>     <span style=\"color: #006080\">\"displayName\"<\/span>: <span style=\"color: #006080\">\"My test app\"<\/span>,<br>     <span style=\"color: #006080\">\"errorUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>     <span style=\"color: #006080\">\"homepage\"<\/span>: <span style=\"color: #006080\">\"https:\/\/localhost:2121\/\"<\/span>,<br>     <span style=\"color: #006080\">\"keyCredentials\"<\/span>: [],<br>     <span style=\"color: #006080\">\"logoutUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>     <span style=\"color: #006080\">\"passwordCredentials\"<\/span>: [],<br>     <span style=\"color: #006080\">\"publisherName\"<\/span>: <span style=\"color: #006080\">\"Vittorio.Bertocci\"<\/span>,<br>     <span style=\"color: #006080\">\"replyUrls\"<\/span>: [<br>       <span style=\"color: #006080\">\"https:\/\/localhost:2121\/\"<\/span><br>     ],<br>     <span style=\"color: #006080\">\"samlMetadataUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>     <span style=\"color: #006080\">\"servicePrincipalNames\"<\/span>: [<br>       <span style=\"color: #006080\">\"https:\/\/cloudidentity.net\/testapp1\"<\/span>,<br>       <span style=\"color: #006080\">\"7042160d-2d58-4528-878f-b05b0edc799e\"<\/span><br>     ],<br>     <span style=\"color: #006080\">\"tags\"<\/span>: [<br>       <span style=\"color: #006080\">\"WindowsAzureActiveDirectoryIntegratedApp\"<\/span><br>     ]<br>}<\/pre>\n<\/div>\n<p>Yep, that\u2019s a SP with the same coordinates and some extra info. For example, there is a field \u201cpublisher\u201d which corresponds to the name of my directory tenant (confusingly named as myself, sorry about that, details <a href=\"https:\/\/www.cloudidentity.com\/blog\/2013\/04\/14\/adding-a-custom-domain-to-your-windows-azure-ad\/\">here<\/a>).<br \/>There\u2019s more! Let\u2019s take a look at the roles this SP belongs to: it\u2019s easy, you just GET the following: <a title=\"https:\/\/graph.windows.net\/cloudidentity.net\/servicePrincipals\/a3518a24-2017-4c63-89d3-adec4eeaa9ad\/memberOf\" href=\"https:\/\/graph.windows.net\/cloudidentity.net\/servicePrincipals\/a3518a24-2017-4c63-89d3-adec4eeaa9ad\/memberOf\">https:\/\/graph.windows.net\/cloudidentity.net\/servicePrincipals\/a3518a24-2017-4c63-89d3-adec4eeaa9ad\/memberOf<\/a> where the GUID is the ObjectID. The result:<\/p>\n<div>\n<pre id=\"codeSnippet\" style=\"border-top-style: none; overflow: visible; font-size: 8pt; border-left-style: none; font-family: 'Courier New', courier, monospace; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; width: 100%; background-color: #f4f4f4\">{<br>  <span style=\"color: #006080\">\"odata.metadata\"<\/span>: <span style=\"color: #006080\">\"https:\/\/graph.windows.net\/cloudidentity.net\/$metadata#directoryObjects\"<\/span>,<br>  <span style=\"color: #006080\">\"value\"<\/span>: [<br>    {<br>      <span style=\"color: #006080\">\"odata.type\"<\/span>: <span style=\"color: #006080\">\"Microsoft.WindowsAzure.ActiveDirectory.Role\"<\/span>,<br>      <span style=\"color: #006080\">\"objectType\"<\/span>: <span style=\"color: #006080\">\"Role\"<\/span>,<br>      <span style=\"color: #006080\">\"objectId\"<\/span>: <span style=\"color: #006080\">\"88d8e3e3-8f55-4a1e-953a-9b9898b8876b\"<\/span>,<br>      <span style=\"color: #006080\">\"description\"<\/span>: <span style=\"color: #006080\">\"Allows access to various read only tasks in the directory. \"<\/span>,<br>      <span style=\"color: #006080\">\"displayName\"<\/span>: <span style=\"color: #006080\">\"Directory Readers\"<\/span>,<br>      <span style=\"color: #006080\">\"isSystem\"<\/span>: <span style=\"color: #0000ff\">true<\/span>,<br>      <span style=\"color: #006080\">\"roleDisabled\"<\/span>: <span style=\"color: #0000ff\">false<\/span><br>    }<br>  ]<br>}<\/pre>\n<\/div>\n<div>Yes, this SP has the access level we specified at apps\u2019s creation: that means that <em>the access level to the directory is not an intrinsic property of the application<\/em>. Want circumstantial evidence? Let\u2019s see what we get if we try to see if the corresponding Application belongs to anything, by GETting <a title=\"https:\/\/graph.windows.net\/cloudidentity.net\/applications\/b4d66176-4654-4b7d-892e-d3b564bc7910\/memberOf\" href=\"https:\/\/graph.windows.net\/cloudidentity.net\/applications\/b4d66176-4654-4b7d-892e-d3b564bc7910\/memberOf\">https:\/\/graph.windows.net\/cloudidentity.net\/applications\/b4d66176-4654-4b7d-892e-d3b564bc7910\/memberOf<\/a>.<\/div>\n<div>Surprise!<\/div>\n<div id=\"codeSnippetWrapper\">\n<pre id=\"codeSnippet\" style=\"border-top-style: none; overflow: visible; font-size: 8pt; border-left-style: none; font-family: 'Courier New', courier, monospace; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; width: 100%; background-color: #f4f4f4\"><p>{<br>   <span style=\"color: #006080\">\"Status Code\"<\/span> : <span style=\"color: #006080\">\"BadRequest\"<\/span>,<br>   <span style=\"color: #006080\">\"Description\"<\/span> : <span style=\"color: #006080\">\"The remote server returned an error: (400) Bad Request.\"<\/span>,<br>   <span style=\"color: #006080\">\"Response\"<\/span> : <span style=\"color: #006080\">\"{\"<\/span>odata.error<span style=\"color: #006080\">\":{\"<\/span>code<span style=\"color: #006080\">\":\"<\/span>Request_BadRequest<span style=\"color: #006080\">\",\"<\/span>message<span style=\"color: #006080\">\":<\/span><\/p><p><span style=\"color: #006080\">{\"<\/span>lang<span style=\"color: #006080\">\":\"<\/span>en<span style=\"color: #006080\">\",<\/span><\/p><p><span style=\"color: #006080\">\"<\/span>value<span style=\"color: #006080\">\":\"<\/span>Unsupported directory <span style=\"color: #0000ff\">object<\/span> <span style=\"color: #0000ff\">class<\/span> <span style=\"color: #006080\">'Application'<\/span> <span style=\"color: #0000ff\">in<\/span> query against link <span style=\"color: #006080\">'memberOf'<\/span>.<span style=\"color: #006080\">\"}}}\"<\/span><br>}<br><\/p><\/pre>\n<\/div>\n<p>The entity type does not even support memberOf. I stand my case.<\/p>\n<p>OK, now let\u2019s make things more interesting. Let\u2019s go back to the portal and make the app available to other tenants. In my case I already picked the URI in the right format (see <a href=\"https:\/\/www.cloudidentity.com\/blog\/2013\/04\/09\/walkthrough-3-developing-multi-tenant-web-applications-with-windows-azure-ad\/\">this<\/a>) hence all I need to do is flipping the \u201cExternal access\u201d switch in the config page for the application.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/04\/image19.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/04\/image_thumb19.png\" width=\"604\" height=\"442\"><\/a><\/p>\n<p>Let\u2019s take a look again at the Application object:<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre id=\"codeSnippet\" style=\"border-top-style: none; overflow: visible; font-size: 8pt; border-left-style: none; font-family: 'Courier New', courier, monospace; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; width: 100%; background-color: #f4f4f4\">{<br>  <span style=\"color: #006080\">\"odata.type\"<\/span>: <span style=\"color: #006080\">\"Microsoft.WindowsAzure.ActiveDirectory.Application\"<\/span>,<br>  <span style=\"color: #006080\">\"objectType\"<\/span>: <span style=\"color: #006080\">\"Application\"<\/span>,<br>  <span style=\"color: #006080\">\"objectId\"<\/span>: <span style=\"color: #006080\">\"b4d66176-4654-4b7d-892e-d3b564bc7910\"<\/span>,<br>  <span style=\"color: #006080\">\"appId\"<\/span>: <span style=\"color: #006080\">\"7042160d-2d58-4528-878f-b05b0edc799e\"<\/span>,<br>  <span style=\"color: #006080\">\"availableToOtherTenants\"<\/span>: <span style=\"color: #0000ff\">true<\/span>,<br>  <span style=\"color: #006080\">\"displayName\"<\/span>: <span style=\"color: #006080\">\"My test app\"<\/span>,<br>  <span style=\"color: #006080\">\"errorUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>  <span style=\"color: #006080\">\"homepage\"<\/span>: <span style=\"color: #006080\">\"https:\/\/localhost:2121\/\"<\/span>,<br>  <span style=\"color: #006080\">\"identifierUris\"<\/span>: [<br>    <span style=\"color: #006080\">\"https:\/\/cloudidentity.net\/testapp1\"<\/span><br>  ],<br>  <span style=\"color: #006080\">\"keyCredentials\"<\/span>: [],<br>  <span style=\"color: #006080\">\"logoutUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>  <span style=\"color: #006080\">\"passwordCredentials\"<\/span>: [],<br>  <span style=\"color: #006080\">\"publicClient\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>  <span style=\"color: #006080\">\"replyUrls\"<\/span>: [<br>    <span style=\"color: #006080\">\"https:\/\/localhost:2121\/\"<\/span><br>  ],<br>  <span style=\"color: #006080\">\"samlMetadataUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span><br>}<\/pre>\n<\/div>\n<p>Yes, AvailableToOtherTenants is now set to true. <br \/>The corresponding SP? No changes.<\/p>\n<h3>Application, ServicePrincipal and Consent Operations<\/h3>\n<p>The introduction of the Application object was largely made for facilitating things in multitenant scenarios. Let\u2019s take a look at that in some details.<\/p>\n<p>I am going to navigate to the app\u2019s consent page and sign in as another tenant\u2019s admin; and I am going to consent to give the app to access the new admin\u2019s directory. At that point, we\u2019ll take a look at what happened in the directory itself.<\/p>\n<p>I\u2019ll start by grabbing the consent URL from the portal and edit it to request a different access level (DirectoryWriters) just to stress my point that it\u2019s independent from the app itself. I\u2019ll ignore the return URL part, we don\u2019t need to write a single line of code for messing with the app settings. The result:<\/p>\n<p><a title=\"https:\/\/go.microsoft.com\/fwLink\/?LinkID=286623&amp;clcid=0x409&amp;ClientID=7042160d-2d58-4528-878f-b05b0edc799e&amp;RequestedPermissions=DirectoryWriters&amp;ConsentReturnURL=https%3A%2F%2Flocalhost%3A2121%2F\" href=\"https:\/\/go.microsoft.com\/fwLink\/?LinkID=286623&amp;clcid=0x409&amp;ClientID=7042160d-2d58-4528-878f-b05b0edc799e&amp;RequestedPermissions=DirectoryWriters&amp;ConsentReturnURL=https%3A%2F%2Flocalhost%3A2121%2F\">https:\/\/go.microsoft.com\/fwLink\/?LinkID=286623&amp;clcid=0x409&amp;ClientID=7042160d-2d58-4528-878f-b05b0edc799e&amp;RequestedPermissions=DirectoryWriters&amp;ConsentReturnURL=https%3A%2F%2Flocalhost%3A2121%2F<\/a><\/p>\n<p>Let\u2019s open another IE in private mode (or another browser type) and paste the consent URL. Then, let\u2019s sign in as the admin of another Windows Azure AD tenant (in my case, treyresearch1.onmicrosoft.com).<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/04\/image20.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/04\/image_thumb20.png\" width=\"604\" height=\"456\"><\/a><\/p>\n<p>Note the access levels.<\/p>\n<p>Click Grant. Of course you\u2019ll be bounced to nowhere, given that the reply URL is bogus, but at that point the consent has been already registered.<\/p>\n<p>Want proof? In the same inPrivate browser navigate to the Windows Azure portal, AD tab, integrated apps: the test app will be there.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/04\/image21.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/04\/image_thumb21.png\" width=\"604\" height=\"179\"><\/a><\/p>\n<p>As you can see, it\u2019s easy to tell that app apart from the ones developed directly in the tenant. That gets even more evident if you click on its entry:<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/04\/image22.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/04\/image_thumb22.png\" width=\"604\" height=\"386\"><\/a><\/p>\n<p>Note that the access level that \u201cmy test app\u201d has in treyresearch1.onmicrosoft.com is different than the one it had in the tenant it was created in, cloudidentity.net.<\/p>\n<p>OK, let\u2019s take a peek under the hood.<\/p>\n<p>First interesting fact: <em>If I query treyresearch1\u2019s <strong>applications <\/strong>entities I do not find an entry for \u201cmy test app\u201d<\/em>.<\/p>\n<p>Do you want to take a guess about if I\u2019ll find something in the serviceprincipals? You got it, it\u2019s there!<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre id=\"codeSnippet\" style=\"border-top-style: none; overflow: visible; font-size: 8pt; border-left-style: none; font-family: 'Courier New', courier, monospace; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; width: 100%; background-color: #f4f4f4\">{<br>      <span style=\"color: #006080\">\"odata.type\"<\/span>: <span style=\"color: #006080\">\"Microsoft.WindowsAzure.ActiveDirectory.ServicePrincipal\"<\/span>,<br>      <span style=\"color: #006080\">\"objectType\"<\/span>: <span style=\"color: #006080\">\"ServicePrincipal\"<\/span>,<br>      <span style=\"color: #006080\">\"objectId\"<\/span>: <span style=\"color: #006080\">\"6629ad6c-891c-4b2c-97d3-63ec3c6b2579\"<\/span>,<br>      <span style=\"color: #006080\">\"accountEnabled\"<\/span>: <span style=\"color: #0000ff\">true<\/span>,<br>      <span style=\"color: #006080\">\"appId\"<\/span>: <span style=\"color: #006080\">\"7042160d-2d58-4528-878f-b05b0edc799e\"<\/span>,<br>      <span style=\"color: #006080\">\"displayName\"<\/span>: <span style=\"color: #006080\">\"My test app\"<\/span>,<br>      <span style=\"color: #006080\">\"errorUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"homepage\"<\/span>: <span style=\"color: #006080\">\"https:\/\/localhost:2121\/\"<\/span>,<br>      <span style=\"color: #006080\">\"keyCredentials\"<\/span>: [],<br>      <span style=\"color: #006080\">\"logoutUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"passwordCredentials\"<\/span>: [],<br>      <span style=\"color: #006080\">\"publisherName\"<\/span>: <span style=\"color: #006080\">\"Vittorio.Bertocci\"<\/span>,<br>      <span style=\"color: #006080\">\"replyUrls\"<\/span>: [<br>        <span style=\"color: #006080\">\"https:\/\/localhost:2121\/\"<\/span><br>      ],<br>      <span style=\"color: #006080\">\"samlMetadataUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"servicePrincipalNames\"<\/span>: [<br>        <span style=\"color: #006080\">\"https:\/\/cloudidentity.net\/testapp1\"<\/span>,<br>        <span style=\"color: #006080\">\"7042160d-2d58-4528-878f-b05b0edc799e\"<\/span><br>      ],<br>      <span style=\"color: #006080\">\"tags\"<\/span>: [<br>        <span style=\"color: #006080\">\"WindowsAzureActiveDirectoryIntegratedApp\"<\/span><br>      ]<br> }<\/pre>\n<\/div>\n<p>Let\u2019s paste again the one we got in cloudidentity.net to spot the differences:<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre id=\"codeSnippet\" style=\"border-top-style: none; overflow: visible; font-size: 8pt; border-left-style: none; font-family: 'Courier New', courier, monospace; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; width: 100%; background-color: #f4f4f4\">{<br>      <span style=\"color: #006080\">\"odata.type\"<\/span>: <span style=\"color: #006080\">\"Microsoft.WindowsAzure.ActiveDirectory.ServicePrincipal\"<\/span>,<br>      <span style=\"color: #006080\">\"objectType\"<\/span>: <span style=\"color: #006080\">\"ServicePrincipal\"<\/span>,<br>      <span style=\"color: #006080\">\"objectId\"<\/span>: <span style=\"color: #006080\">\"2be215fa-6970-4ca7-bb1f-2ca304196655\"<\/span>,<br>      <span style=\"color: #006080\">\"accountEnabled\"<\/span>: <span style=\"color: #0000ff\">true<\/span>,<br>      <span style=\"color: #006080\">\"appId\"<\/span>: <span style=\"color: #006080\">\"7042160d-2d58-4528-878f-b05b0edc799e\"<\/span>,<br>      <span style=\"color: #006080\">\"displayName\"<\/span>: <span style=\"color: #006080\">\"My test app\"<\/span>,<br>      <span style=\"color: #006080\">\"errorUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"homepage\"<\/span>: <span style=\"color: #006080\">\"https:\/\/localhost:2121\/\"<\/span>,<br>      <span style=\"color: #006080\">\"keyCredentials\"<\/span>: [],<br>      <span style=\"color: #006080\">\"logoutUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"passwordCredentials\"<\/span>: [],<br>      <span style=\"color: #006080\">\"publisherName\"<\/span>: <span style=\"color: #006080\">\"Vittorio.Bertocci\"<\/span>,<br>      <span style=\"color: #006080\">\"replyUrls\"<\/span>: [<br>        <span style=\"color: #006080\">\"https:\/\/localhost:2121\/\"<\/span><br>      ],<br>      <span style=\"color: #006080\">\"samlMetadataUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"servicePrincipalNames\"<\/span>: [<br>        <span style=\"color: #006080\">\"https:\/\/cloudidentity.net\/testapp1\"<\/span>,<br>        <span style=\"color: #006080\">\"7042160d-2d58-4528-878f-b05b0edc799e\"<\/span><br>      ],<br>      <span style=\"color: #006080\">\"tags\"<\/span>: [<br>        <span style=\"color: #006080\">\"WindowsAzureActiveDirectoryIntegratedApp\"<\/span><br>      ]<br> }<\/pre>\n<\/div>\n<p>The only difference is the ObjectId, which of course must be globally unique for every object everywhere. Apart from that, and the different role memberships determined at creation\/consent time, the two are identical projections from the original Application\u2019s entry back in the cloudidentity.net tenant.<\/p>\n<h3>Things to Note<\/h3>\n<p>Here there are few things you want to keep an eye on.<\/p>\n<h4>Multitenant Apps Are Tied to Their Origin\u2019s Tenant<\/h4>\n<p>The Application object is the enabler of the consent flow magic. Also, the Application object lives in the tenant in which the app was originally created.<\/p>\n<p>That means that the app destiny is tied to the tenant\u2019s, hence you might want to take that into account when taking lifecycle decisions (migrating to new tenants and similar).<\/p>\n<h4>Once a ServicePrincipal is Created Via Consent, Ties to the Original Application Object Are Severed<\/h4>\n<p>Say that you want to change something in your app, like the app URL. Let\u2019s actually do it and see what happens. Head back to the windows azure portal of the original tenant, and modify the app\u2019s reply URL.<\/p>\n<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/04\/image23.png\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/04\/image_thumb23.png\" width=\"604\" height=\"252\"><\/a><\/p>\n<p>Let\u2019s take a look at the Application object.<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre id=\"codeSnippet\" style=\"border-top-style: none; overflow: visible; font-size: 8pt; border-left-style: none; font-family: 'Courier New', courier, monospace; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; width: 100%; background-color: #f4f4f4\">{<br>  <span style=\"color: #006080\">\"odata.type\"<\/span>: <span style=\"color: #006080\">\"Microsoft.WindowsAzure.ActiveDirectory.Application\"<\/span>,<br>  <span style=\"color: #006080\">\"objectType\"<\/span>: <span style=\"color: #006080\">\"Application\"<\/span>,<br>  <span style=\"color: #006080\">\"objectId\"<\/span>: <span style=\"color: #006080\">\"b4d66176-4654-4b7d-892e-d3b564bc7910\"<\/span>,<br>  <span style=\"color: #006080\">\"appId\"<\/span>: <span style=\"color: #006080\">\"7042160d-2d58-4528-878f-b05b0edc799e\"<\/span>,<br>  <span style=\"color: #006080\">\"availableToOtherTenants\"<\/span>: <span style=\"color: #0000ff\">true<\/span>,<br>  <span style=\"color: #006080\">\"displayName\"<\/span>: <span style=\"color: #006080\">\"My test app\"<\/span>,<br>  <span style=\"color: #006080\">\"errorUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>  <span style=\"color: #006080\">\"homepage\"<\/span>: <span style=\"color: #006080\">\"https:\/\/localhost:2121\/\"<\/span>,<br>  <span style=\"color: #006080\">\"identifierUris\"<\/span>: [<br>    <span style=\"color: #006080\">\"https:\/\/cloudidentity.net\/testapp1\"<\/span><br>  ],<br>  <span style=\"color: #006080\">\"keyCredentials\"<\/span>: [],<br>  <span style=\"color: #006080\">\"logoutUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>  <span style=\"color: #006080\">\"passwordCredentials\"<\/span>: [],<br>  <span style=\"color: #006080\">\"publicClient\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>  <span style=\"color: #006080\">\"replyUrls\"<\/span>: [<br>    <span style=\"color: #006080\">\"https:\/\/localhost:2121<font style=\"background-color: #ffff00\">\/tornaacasalasssie<\/font>\"<\/span><br>  ],<br>  <span style=\"color: #006080\">\"samlMetadataUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span><br>}<br><\/pre>\n<\/div>\n<p>Yep, updated.<\/p>\n<p>What about the ServicePrincipal, still in this tenant?<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre id=\"codeSnippet\" style=\"border-top-style: none; overflow: visible; font-size: 8pt; border-left-style: none; font-family: 'Courier New', courier, monospace; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; width: 100%; background-color: #f4f4f4\">{<br>      <span style=\"color: #006080\">\"odata.type\"<\/span>: <span style=\"color: #006080\">\"Microsoft.WindowsAzure.ActiveDirectory.ServicePrincipal\"<\/span>,<br>      <span style=\"color: #006080\">\"objectType\"<\/span>: <span style=\"color: #006080\">\"ServicePrincipal\"<\/span>,<br>      <span style=\"color: #006080\">\"objectId\"<\/span>: <span style=\"color: #006080\">\"83ac2b6e-d20f-4770-874d-4e0859579476\"<\/span>,<br>      <span style=\"color: #006080\">\"accountEnabled\"<\/span>: <span style=\"color: #0000ff\">true<\/span>,<br>      <span style=\"color: #006080\">\"appId\"<\/span>: <span style=\"color: #006080\">\"7042160d-2d58-4528-878f-b05b0edc799e\"<\/span>,<br>      <span style=\"color: #006080\">\"displayName\"<\/span>: <span style=\"color: #006080\">\"My test app\"<\/span>,<br>      <span style=\"color: #006080\">\"errorUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"homepage\"<\/span>: <span style=\"color: #006080\">\"https:\/\/localhost:2121\/\"<\/span>,<br>      <span style=\"color: #006080\">\"keyCredentials\"<\/span>: [],<br>      <span style=\"color: #006080\">\"logoutUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"passwordCredentials\"<\/span>: [],<br>      <span style=\"color: #006080\">\"publisherName\"<\/span>: <span style=\"color: #006080\">\"Vittorio.Bertocci\"<\/span>,<br>      <span style=\"color: #006080\">\"replyUrls\"<\/span>: [<br>        <span style=\"color: #006080\">\"https:\/\/localhost:2121<font style=\"background-color: #ffff00\">\/tornaacasalasssie<\/font>\"<\/span><br>      ],<br>      <span style=\"color: #006080\">\"samlMetadataUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"servicePrincipalNames\"<\/span>: [<br>        <span style=\"color: #006080\">\"https:\/\/cloudidentity.net\/testapp1\"<\/span>,<br>        <span style=\"color: #006080\">\"7042160d-2d58-4528-878f-b05b0edc799e\"<\/span><br>      ],<br>      <span style=\"color: #006080\">\"tags\"<\/span>: [<br>        <span style=\"color: #006080\">\"WindowsAzureActiveDirectoryIntegratedApp\"<\/span><br>      ]<br> }<br><\/pre>\n<\/div>\n<p>Updated as well, as expected.<\/p>\n<p>What about the ServicePrincipal provisioned via consent flow in the tenant treyresearch1.onmicrosoft.com?<\/p>\n<div id=\"codeSnippetWrapper\">\n<pre id=\"codeSnippet\" style=\"border-top-style: none; overflow: visible; font-size: 8pt; border-left-style: none; font-family: 'Courier New', courier, monospace; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; width: 100%; background-color: #f4f4f4\">{<br>      <span style=\"color: #006080\">\"odata.type\"<\/span>: <span style=\"color: #006080\">\"Microsoft.WindowsAzure.ActiveDirectory.ServicePrincipal\"<\/span>,<br>      <span style=\"color: #006080\">\"objectType\"<\/span>: <span style=\"color: #006080\">\"ServicePrincipal\"<\/span>,<br>      <span style=\"color: #006080\">\"objectId\"<\/span>: <span style=\"color: #006080\">\"6629ad6c-891c-4b2c-97d3-63ec3c6b2579\"<\/span>,<br>      <span style=\"color: #006080\">\"accountEnabled\"<\/span>: <span style=\"color: #0000ff\">true<\/span>,<br>      <span style=\"color: #006080\">\"appId\"<\/span>: <span style=\"color: #006080\">\"7042160d-2d58-4528-878f-b05b0edc799e\"<\/span>,<br>      <span style=\"color: #006080\">\"displayName\"<\/span>: <span style=\"color: #006080\">\"My test app\"<\/span>,<br>      <span style=\"color: #006080\">\"errorUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"homepage\"<\/span>: <span style=\"color: #006080\">\"https:\/\/localhost:2121\/\"<\/span>,<br>      <span style=\"color: #006080\">\"keyCredentials\"<\/span>: [],<br>      <span style=\"color: #006080\">\"logoutUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"passwordCredentials\"<\/span>: [],<br>      <span style=\"color: #006080\">\"publisherName\"<\/span>: <span style=\"color: #006080\">\"Vittorio.Bertocci\"<\/span>,<br>      <span style=\"color: #006080\">\"replyUrls\"<\/span>: [<br>        <span style=\"color: #006080\">\"<font style=\"background-color: #ff0000\" color=\"#ffffff\">https:\/\/localhost:2121\/<\/font>\"<\/span><br>      ],<br>      <span style=\"color: #006080\">\"samlMetadataUrl\"<\/span>: <span style=\"color: #0000ff\">null<\/span>,<br>      <span style=\"color: #006080\">\"servicePrincipalNames\"<\/span>: [<br>        <span style=\"color: #006080\">\"https:\/\/cloudidentity.net\/testapp1\"<\/span>,<br>        <span style=\"color: #006080\">\"7042160d-2d58-4528-878f-b05b0edc799e\"<\/span><br>      ],<br>      <span style=\"color: #006080\">\"tags\"<\/span>: [<br>        <span style=\"color: #006080\">\"WindowsAzureActiveDirectoryIntegratedApp\"<\/span><br>      ]<br> }<\/pre>\n<\/div>\n<p>Yes, <em>the ServicePrincipal in the other tenant is unaffected by the change in the Application object in the original tenant<\/em>.<\/p>\n<p>The consent operation uses the state of the Application object at the consent instant and creates a new ServicePrincipal out of it, but after that there is no further synchronization involved. Excluding the programmatic routes, the main way of applying the new settings is to revoke the consent to the app and grant it again.<br \/>My interpretation here (remember my disclaimer!!! this is my personal blog!) is that in the power balance between tenant administrator and ISV the directory favors the admin by default. The tenant admin is the king of his own castle, and changes are always under his\/her explicit imprimatur: that includes app lifecycle changes such as this one.<\/p>\n<p>This is one of the reasons for which it is super-important for you to avoid exposing unnecessary implementation details when defining the application coordinates. A classic example would be to specify a special page\/action in the reply URL instead of referring to the app\u2019s root URL; any changes in that, something that would normally be an implementation detail private to your app, would cause you unnecessary churn.<\/p>\n<p>&nbsp;<\/p>\n<p>Bottom line:&nbsp; Before you promote an app to be externally available, it is good practice to ensure that its protocol coordinates are stable; furthermore, it is very important for you to ensure that you have a way of contacting your customers should you need to apply emergency changes. The consent flow is extremely powerful, and allows you to onboard organizational customers with unprecedented ease, but it remains a business critical feature and as such requires thorough planning.<\/p>\n<p>&nbsp;<\/p>\n<p>Well, it did take more than one hour to write: I\u2019ll have to crank up the alarm\u2019s volume a bit\u2026 but I hope this will come in handy <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-winkingsmile\" style=\"border-top-style: none; border-left-style: none; border-bottom-style: none; border-right-style: none\" alt=\"Winking smile\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2013\/04\/wlEmoticon-winkingsmile3.png\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the various announcements and walkthroughs you had the chance to experience the changes in Windows Azure AD\u2019s product surface. In this (hopefully not too long, it\u2019s midnight already) post I am going to touch on some of the deeper changes that took place beneath the surface. Albeit less evident at first, some&#8230;<\/p>\n","protected":false},"author":1,"featured_media":2090,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2098","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/2098","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/comments?post=2098"}],"version-history":[{"count":2,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/2098\/revisions"}],"predecessor-version":[{"id":2100,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/2098\/revisions\/2100"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/media\/2090"}],"wp:attachment":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/media?parent=2098"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/categories?post=2098"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/tags?post=2098"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}