Programmatically register native apps in Azure AD or Office 365

Posted on 1/7/2015 @ 12:22 AM in #Azure by | Feedback | 2893 views

This blog post applies to both Office 365 and Azure AD since Office 365 lives in Azure AD. And with some tweaks you can easily make this work for non-native apps also (web apps that are single or multi-tenant, and web apis that are single or multi-tenant).

Native Apps are multi-tenant by default. WebApps and WebAPIs are single tenant or multi-tenant – your choice. But as a vendor, when you are shipping a single tenant app, sometimes you need to smooth the process of registering the app in a tenant’s AD. This can be done programmatically. Native apps on the other hand, need to be registered once in the publisher’s tenant. Then when any user in a different tenant tries to use the native app, the app will register itself in their tenant.

Anyway, here is how you programmatically register a native app in Azure AD.

Step #1 – Authenticate to Azure AD. You will need the ability to call the Azure AD graph. You will get an access token with these rights.
Step #2 – Issue a call like this,

POST https://graph.windows.net/<tenantid>/directoryObjects

   1:  <?xml version="1.0" encoding="utf-8"?>
   2:  <entry xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml">
   3:    <category term="Microsoft.WindowsAzure.ActiveDirectory.Application" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
   4:    <id />
   5:    <title />
   6:    <updated>2015-01-06T04:49:50Z</updated>
   7:    <author>
   8:      <name />
   9:    </author>
  10:    <content type="application/xml">
  11:      <m:properties>
  12:        <d:availableToOtherTenants m:type="Edm.Boolean">true</d:availableToOtherTenants>
  13:        <d:displayName>App1.Windows</d:displayName>
  14:        <d:homepage>http://someurl</d:homepage>
  15:        <d:publicClient m:type="Edm.Boolean">true</d:publicClient>
  16:        <d:replyUrls m:type="Collection(Edm.String)">
  17:          <d:element>redirecturispecifictoplatformandapp</d:element>
  18:        </d:replyUrls>
  19:      </m:properties>
  20:    </content>
  21:  </entry>

(It goes without saying that you need to also pass in the access token)

The properties we are sending in are,

  1. availableToOtherTenants – Native App is going to be multi-tenant, so that is true
  2. homePage – mostly this will be the same as redirect URI, but you can put in an app description html page link here also
  3. publicClient – true for apps you intend to put in appstores
  4. replyURLs – what azure AD will redirect to after authentication, for windows apps this is app specific, for iOS and Android this is protocol specific.

If all goes well, this should reply with a HTTP201 “Created” message. with the following payload,

   1:  <content type="application/xml">
   2:    <m:properties>
   3:      <d:objectType>Application</d:objectType>
   4:      <d:objectId>uniqueguidforurregisteredapp</d:objectId>
   5:      <d:softDeletionTimestamp m:null="true" />
   6:      <d:appId>this is the client id that you would have otherwise hand typed in</d:appId>
   7:      <d:appPermissions m:type="Collection(Microsoft.WindowsAzure.ActiveDirectory.AppPermission)" />
   8:      <d:availableToOtherTenants m:type="Edm.Boolean">true</d:availableToOtherTenants>
   9:      <d:displayName>nameofurapp</d:displayName>
  10:      <d:errorUrl m:null="true" />
  11:      <d:groupMembershipClaims m:null="true" />
  12:      <d:homepage>homepage</d:homepage>
  13:      <d:identifierUris m:type="Collection(Edm.String)" />
  14:      <d:keyCredentials m:type="Collection(Microsoft.WindowsAzure.ActiveDirectory.KeyCredential)" />
  15:      <d:knownClientApplications m:type="Collection(Edm.Guid)" />
  16:      <d:logoutUrl m:null="true" />
  17:      <d:passwordCredentials m:type="Collection(Microsoft.WindowsAzure.ActiveDirectory.PasswordCredential)" />
  18:      <d:publicClient m:type="Edm.Boolean">true</d:publicClient>
  19:      <d:replyUrls m:type="Collection(Edm.String)">
  20:        <d:element>redirecturi</d:element>
  21:      </d:replyUrls>
  22:      <d:requiredResourceAccess m:type="Collection(Microsoft.WindowsAzure.ActiveDirectory.RequiredResourceAccess)" />
  23:      <d:samlMetadataUrl m:null="true" />
  24:    </m:properties>
  25:  </content>

So your app is now created, you have a unique GUID representing the app, AND, you have the AppID. Hooray! Just one problem!

You still need to grant this app rights! Like, WTF can this app do? (This app can access Office 365 Sites API, and Discovery Service for instance).

So for that, you need to grant your app a permission. The App Permission, is basically adding to the already created app above – see that element “requiredResourceAccess”? You just need to send in a MERGE packet (Isn’t REST API lovely?) to add an element to the above, like this - (Note that I have simplified the XML payload so this blogpost is readable)

   1:  <content type="application/xml">
   2:    <m:properties>
   3:      <d:requiredResourceAccess m:type="Collection(Microsoft.WindowsAzure.ActiveDirectory.RequiredResourceAccess)">
   4:        <d:element>
   5:          <d:requiredAppPermissions m:type="Collection(Microsoft.WindowsAzure.ActiveDirectory.RequiredAppPermission)">
   6:            <d:element>
   7:              <d:directAccessGrant m:type="Edm.Boolean">false</d:directAccessGrant>
   8:              <d:impersonationAccessGrants m:type="Collection(Edm.String)">
   9:                <d:element>User</d:element>
  10:              </d:impersonationAccessGrants>
  11:              <d:permissionId m:type="Edm.Guid">4e0d77b0-96ba-4398-af14-3baa780278f4</d:permissionId>
  12:            </d:element>
  13:          </d:requiredAppPermissions>
  14:          <d:resourceAppId>00000003-0000-0ff1-ce00-000000000000</d:resourceAppId>
  15:        </d:element>
  16:      </d:requiredResourceAccess>
  17:    </m:properties>
  18:  </content>

What do we see above?
#a – the resourceAPPID – which is the infamous Office products GUID, and
#b – a permissionId .. where the heck do I get that GUID from?

Well – first of all, every permission in Azure AD, is just another object. And you can query for objects .. uhh .. permissions .. by querying the .. you guessed it :-) Azure AD Graph.

So, to get permissions for regular Azure AD graph, you query -

GET https://graph.windows.net/yourtentantid/servicePrincipals()?$filter=appId%20eq%20'00000002-0000-0000-c000-000000000000

and to query for Office 365 specific permission IDs, you query -

GET https://graph.windows.net/yourtenantid/servicePrincipals()?$filter=appId%20eq%20'00000003-0000-0ff1-ce00-000000000000

Damn that Office GUID again :-)

This should return you a list of permissions, you can grab the permission GUID from here, and stick it in your Merge Payload to embellish permissions of your app.

Phew!

Now that we know it's doable, damn this is a lot of work for such a simple functionality. Well whatever! At least its possible.

Sound off but keep it civil:

Older comments..