Extend Azure Active Directory Schema using Graph API (preview)

Edit - Please refer to https://msdn.microsoft.com/en-us/library/azure/dn720459.aspx for official documentation about directory extensions.

Directory schema extensions enable application developers to extend the directory and develop richer applications without worrying about the limitations imposed by an external store. This preview provides REST interfaces for an application to register, unregister, enumerate, read, write, and filter by extension values. Applications that register extensions in the directory are referenced from all the tenants consenting to that Application. Once a customer tenant has consented to an Application (even for read) the extensions registered on that Application are available in the consenting tenant for reading/writing by any Application that has the appropriate access. If the app developer wants to add more extension attributes, she can update her Application (in her developer tenant) and any tenants that are currently consented to this Application will be enabled for the new attributes. If consent is removed, if the extension is deleted, or if the Application is deleted, the extension values will no longer be accessible on the corresponding directory objects.

Types and Limitations
Currently “User”, “Group”, “TenantDetail”, “Device”, “Application” and “ServicePrincipal” entities can be extended with the following single-valued attributes types:

  • Integer
  • LargeInteger
  • DateTime (must be sent in ISO 8601 - DateTime will be stored in UTC)
  • Binary
  • Boolean
  • String

String type extensions can have maximum of 256 characters and binary extensions are limited to 256 bytes. 100 extension values (across ALL types and ALL applications) can be written to any single object. Directory extensions are available only in Graph api-version 1.21-preview. The application must be consented for write access to register an extension.

Registering an Extension
Let’s walk through an example. Contoso has built an OrgChart application and wants to allow users to make Skype calls from it. AAD does not expose a SkypeID user property. The OrgChart developer could use a separate store such as SQL Azure to store a record for each user’s SkypeID. Instead, the developer registers an extension that can expand User type with a String property. He does this by creating an “extensionProperty” on the Application using Graph API.
POST https://graph.windows.net/contoso.com/applications/<applicationObjectID>/extensionProperties?api-version=1.21-preview
{
“name”: “skypeId”,
“dataType”: “String”,
“targetObjects”: [“User”]
}
If the operation is successful, it will return 201 along with the fully qualified extension property name to be used for updating the intended types.
201 Created
{
“objectId”: “5ea3a29b-8efd-46bf-9dc7-f226e839d146”,
“objectType”: “ExtensionProperty”,
“name”: “extension_d8dde29f1095422e91537a6cb22a2f74_skypeId”,
“dataType”: “String”,        
“targetObjects”: [“User”]
}
 
Viewing Extensions Registered by your Application
You can view extensions registered by your application by issuing a GET of the extension properties of the application. This will provide object ID, data type, and target objects for each extension registered by the application.
GET https://graph.windows.net/contoso.com/applications/<applicationObjectID>/extensionProperties?api-version=1.21-preview

Unregistering an Extension
You can unregister an extension registered by your application by issuing a DELETE of the extension object ID as follows:
DELETE https://graph.windows.net/contoso.com/applications/<applicationObjectID>/extensionProperties/<extensionObjectID>?api-version=1.21-preview

Writing Extension Values
Once this application is consented by the admin, any user in the tenant can be updated to include this new property. For example,
PATCH https://graph.windows.net/contoso.com/users/joe\@contoso.com?api-version=1.21-preview
{
“extension_d8dde29f1095422e91537a6cb22a2f74_skypeId”: “joe.smith”
}
The server will return a 204 if user was successfully updated. The extension value can be removed by sending the same PATCH request with “null” value.
PATCH https://graph.windows.net/contoso.com/users/joe\@contoso.com?api-version=1.21-preview
{
“extension_d8dde29f1095422e91537a6cb22a2f74_skypeId”: null
}

Reading Extension Values
When directory objects are retrieved, they automatically include the extension values. For example:
GET https://graph.windows.net/contoso.com/users/joe\@contoso.com?api-version=1.21-preview
200 OK
{
“objectId”: “ff7cd54a-84e8-4b48-ac5a-21abdbaef321”,
“displayName”: “Joe Smith”,
“userPrincipalName”: <“joe@contoso.com>“,
“objectType”: “User”,
“mail”: “null”,
“accountEnabled”: “True” ,
“extension_d8dde29f1095422e91537a6cb22a2f74_skypeId”: “joe.smith”
}
 
Filtering by Extension Values
The extension values can also be used as a part of $filter to search directory similar to any existing property. For example:
GET https://graph.windows.net/contoso.com/users?api-version=1.21-preview&$filter=extension_d8dde29f1095422e91537a6cb22a2f74_skypeId eq 'joe.smith'
Prefix searches on extensions are limited to 71 characters for string searches and 207 bytes for searches on binary extensions.

Sample Code
We have published a couple of samples to GitHub to showcase and illustrate the use of directory extensions. We plan to enhance them based on your feedback and as the feature evolves.

PHP Sample
https://github.com/AzureADSamples/WebApp-GraphAPI-Extensions-PHP

This sample shows how to create and use extensions using PHP. The wiki of this GitHub project helps in understanding the sample and steps to modify/use it.

C# Sample
https://github.com/AzureADSamples/WebApp-GraphAPI-OrgChart-DotNet

This sample displays an AAD tenant org chart and allows reading extension values right out of the box. The wiki of this GitHub project helps in understanding the sample and steps to modify/use it.

We would love to hear about the scenarios that you can enable using extensions or any feedback about the usage in our forum. This is just a first step in integrating on-premises AD with Azure AD.

Thanks

Pavan Kompelli
Rakesh Varna
Arvind Suthar

Azure Active Directory Team

Comments

  • Anonymous
    March 05, 2014
    Wow! Thanks.//Adam

  • Anonymous
    March 07, 2014
    Can these Extension values be included automatically in the claims, once a user authenticates using Azure AD?

  • Anonymous
    March 07, 2014
    Right now extension values cannot be included in the claims. Can you please let us know the scenario where you need this?

  • Anonymous
    March 10, 2014
    It saves us from having to create a custom ClaimsAuthorizationManager that reads these Extended Properties and adds them to the "default" claims

  • Anonymous
    March 12, 2014
    Adding to my comment above.  It will be great if the extensions are automatically included in the claims obtained form using the Active Directory Authentication Library (ADAL).

  • Anonymous
    March 19, 2014
    I am trying to incorporate the pattern in the C# sample code into my MVC Application but when I run it I get an error when I call the createExtension logic. The error reads Resource <ObjectId> does not exist or one of its queried reference-property objects are not present. I have used the ObjectId after noting it down from the powershell command: get-msolserviceprincipal for my application. This is an MVC application that I have registered on my trial Azure subscription. What could I be doing wrong? Could you please advice?

  • Anonymous
    March 21, 2014
    This has to do with the availability of extensions to code.  Do all applications that have approriate access then get the extension property or just the one that registered it.  And, if so, is it possible to use the Powershell Cmdlets to get/update the extended attributes.

  • Anonymous
    March 25, 2014
    Thanks Sam for your feedback. We will look into hat in our future releases.Shas, you need to use the objectId of the application object to add the extension. You can get the objectId of your application using graphexplorer.cloudapp.net (signin and traverse to graph.windows.net/<your tenant>/applications.Rick, All applications have access similar to the access they have for other properties. If an app is consented only for read, they can only read extension property values. If they are consented for write, they will be able to write to the extension property values. We are looking into providing powershell support for future releases.

  • Anonymous
    April 07, 2014
    Hi Guys, I found this article very useful! Thanks a ton!However after downloading c# application, I am having two problemsUnable to locate 'F:TestExtensionAttributeWindowsAzureAD-GraphAPI-Sample-OrgChart-masterWindowsAzureAD-GraphAPI-Sample-OrgChart-master.nugetNuGet.exe' GraphHelperAlso I am not able to resolve reference to Microsoft.WindowsAzure.ActiveDirectory.GraphHelper (tried searching nuget packages but could not find one which will resolve the conflict) Can you please help me for this?Thanks.

  • Anonymous
    April 11, 2014
    The application is working fine on my local server, and I have a question. there is a GraphHelper project, which is different from the GraphHelper in use in other Azure official tutorial. Wonder if it would be better merging these two GraphHelper packages so we have a single code base for that?

  • Anonymous
    April 11, 2014
    Mayuri M,Ed has just fixed this problem in GitHub - github.com/.../6c80718f69f5f23eab5142b6156ba43e7fc1b89eGraphHelper is built using the sources here - github.com/.../GraphHelper. Once the official GraphClient library supports extension attributes, we will port this demo to that library. Let us know if you see other problems.-Arvind

  • Anonymous
    April 15, 2014
    Are multi-valued string attributes supported currently? If so, what does it take to create one such attribute?Thanks,Jameel

  • Anonymous
    April 17, 2014
    Mark,We are working on releasing an official graph client library and will update the blog once it is available.Jameel,Multi-valued are not currently supported but it will be part of future releases (currently no ETA yet).ThanksPavan

  • Anonymous
    April 21, 2014
    The comment has been removed

  • Anonymous
    April 21, 2014
    Hi Vibhuti, can you please post the entire request/response (removing any PII information if necessary)?

  • Anonymous
    April 22, 2014
    Hi Vibhuti, i created successfully with Postman extension on Chrome.Here is my raw request:POST graph.windows.net/.../extensionProperties HTTP/1.1Host: graph.windows.netConnection: keep-aliveContent-Length: 70Cache-Control: no-cacheUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36Origin: chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcmAuthorization: Bearer eyJ0eXAiOiJKV1QiLCJhb[***]I6z3fKeI8e-kyq3Td8DgContent-Type: application/jsonAccept: /Accept-Encoding: gzip,deflate,sdchAccept-Language: en-US,en;q=0.8,vi;q=0.6{"name": "skypeId","dataType": "String","targetObjects": ["User"]}

  • Anonymous
    May 04, 2014
    Hi,Does the Graph API not support content encoding to compress the response in gzip or some other format?All the responses we are seeing against operations described by Graph REST API reference (msdn.microsoft.com/.../hh974478.aspx) seem to ignore the "Accept-Encoding: gzip" request header and response is no encoded at all.Given the size of some of the directories our clients are using, the response becomes very big and given the textual nature of it, compression would help a lot.Could you please update the MSDN documentation above to detail the operations on Applications and Devices as well?ThanksAbhay

  • Anonymous
    May 06, 2014
    Hi Abhay,Thanks for the feedback!  Regarding 1), as you've found, the service does not currently support compression.  I will add a work item on our side to get this completed.  In the meantime, if you are not using JSON (the default that is returned with Accept: application/json), then please do so.  ATOM/XML formats are far more verbose, as are other forms of JSON; the default JSON response relatively lightweight.Regarding 2), I'll make sure the documentation gets updated appropriately.Thanks,Robert

  • Anonymous
    May 06, 2014
    Hi Robert,Thanks for the reply. We are already using JSON but the compression will really help.  I hope to see an update on that in a few months.Especially because our application is used on mobile devices, where network and battery life matter a lot, every byte reduced helps.ThanksAbhay

  • Anonymous
    May 10, 2014
    Hi,Is there a better forum than this blog comments where we can have a discussion with the Graph API team with requests for better documentation on a particular feature or ask for new features?ThanksAbhay

  • Anonymous
    August 22, 2014
    Thank you for info. This is great. I wonder if there is any thoughts to keep extension visible to requested application only or somehow define visibility In our case User object shared between many applications and we would like to keep extensions application specific instead of sharing all user attributes across all applications.

  • Anonymous
    September 01, 2014
    Great Article!!Do we have this functionality available in the GraphClient library?

  • Anonymous
    October 07, 2014
    Thanks a lot!!! for the article.We are getting exception while adding user to an existing group from MVC app to WAAD and also while deleting a user from an exiting group. It was working fine till yesterday, and suddenly now we are getting exception saying"Resource '<group_id / objectId>' does not exist or one of its queried reference-property objects are not present"following is the piece of code:Group refreshedGroup = DirectoryDataServiceAttributes.DataService.groups.Where(it => (it.objectId == PermissionId.Trim())).SingleOrDefault();                   if (refreshedGroup.dirSyncEnabled == null)                   {                       DirectoryDataServiceAttributes.DataService.DeleteLink(refreshedGroup, "members", users);                       DirectoryDataServiceAttributes.DataService.SaveChanges();                   }we are getting error while executing SaveChanges() method. we are passing correct group id /object Id."refreshgroup" variable is having data of the groups which we expect and "users" variable is having the user details what we expect.Any help will be appreciated.Thanks!!!

  • Anonymous
    November 22, 2014
    Hi, how can we use extensions with v.2.0.0.?Regards,Alessandro

  • Anonymous
    December 15, 2014
    Any help for v2.0.2?thanks,Alessandro

  • Anonymous
    January 27, 2015
    Will there are be support for extensions that can store more than 256 bytes?

  • Anonymous
    August 21, 2015
    Once you have used the Graph API to extend the Azure AD schema with custom attributes, is there a way to delete those or change their value type if you make a mistake on some?  I have someone that created over 50 using the wrong attribute type.

  • Anonymous
    August 26, 2015
    Linda,   You should be able to delete the custom attributes and recreate them with the correct type. You can find more information in the MSDN link available at the beginning of the article. I am adding it here for easy reference - msdn.microsoft.com/.../dn720459.aspx

  • Anonymous
    August 27, 2015
    Hi, I want to get all users and their groups in one call. How the URL will be?? I tried a lott on google . Its urgent requirement in our project..

  • Anonymous
    August 30, 2015
    Hi,   I want to get all users and their groups in one URL call . Right now i am using url- "graph.windows.net/.../users$expand=memberOf&api-version=1.0" It shows all users and their groups. but groups displayed are<20. How can I display all groups of each user with minimum time?

  • Anonymous
    October 14, 2015
    I have an application in a B2C AAD with all read and write directory data permission to the AAD. I can use it to add all or a single Extended Property. When I try and use it to delete one: DELETE /[Same as with a GET]?api-version=1.5 HTTP/1.1 Host: graph.windows.net Authorization: Bearer eyJ0eXA...g Cache-Control: no-cache The response is {  "odata.error": {    "code": "Authorization_RequestDenied",    "message": {      "lang": "en",      "value": "Insufficient privileges to complete the operation."    }  } } Also, I see in my B2C Blade extended properties I have added using ADAL, under User Attributes with ATTRIBUTE TYPE of "Custom". However, if I read a user from the same B2C AAD, I do not see the extended properties listed: Get [MICROSOFT AZURE AD GRAPH API ENDPOINT from View Endpoints in Classic Azure Portal]/users/[user object id]?api-version=1.5 HTTP/1.1 Host: graph.windows.net Authorization: Bearer eyJ..Wg Cache-Control: no-cache Returns {    "odata.metadata": "graph.windows.net/279925ff-681f-45c8-a464-f1bd61fd9d9b$metadata#directoryObjects/Microsoft.DirectoryServices.User/@Element",    "odata.type": "Microsoft.DirectoryServices.User",    "objectType": "User",    "objectId": "85d6bb2a-cb4e-4df5-96a7-0e270b06cbfb",    "deletionTimestamp": null,    "accountEnabled": true, ...    "userType": "Member" } without any extended properties listed.

  • Anonymous
    October 29, 2015
    I'm not a programmer, nor do I have rights to extend schemas that I know of.. However, I'm using Excel PowerQuery and the Azure AD REST graph API to retrieve user data from our AD.   I need to both retrieve the value of ExtensionAttribute5 as well as FILTER it.   I've tried this REST query and get an error:  graph.windows.net/.../users$filter=accountEnabled+eq+true+and+ExtensionAttribute5+eq+'Vendor' (You can see, I need to get all users who are vendors, from regular employees - we store that here) Is there now, or will there be a version of the Azure Graph API that simply reveals all the attributes for a user object so one doesn't have to write code to make new apps? -Puzzled.. Thanks - Eric.

  • Anonymous
    February 18, 2016
    Really interested to know if we use directory schema extensions how do we get the new extension property of a user returned as a claim? I see from prior commentresponse that it is not possible? Seems like a massive oversight. Why would we add a property to a user if we didn't want it? Why would we want to use graph API to manually retrieve the value of said property rather than just have it properly encased within the token issued by the identity provider - containing what the identity provider knows about the individual.