Extend Azure Active Directory Schema using Graph API (preview)


Edit – Please refer to http://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 /extensionProperties?api-version=1.21-preview”>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/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 (33)

  1. Anonymous says:

    Wow! Thanks.

    //Adam

  2. Anonymous says:

    Can these Extension values be included automatically in the claims, once a user authenticates using Azure AD?

  3. Pavan Kompelli - MSFT says:

    Right now extension values cannot be included in the claims. Can you please let us know the scenario where you need this?

  4. Anonymous says:

    It saves us from having to create a custom ClaimsAuthorizationManager that reads these Extended Properties and adds them to the "default" claims

  5. Anonymous says:

    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).

  6. Anonymous says:

    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?

  7. Anonymous says:

    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.

  8. Pavan Kompelli - MSFT says:

    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.

  9. Anonymous says:

    Hi Guys, I found this article very useful! Thanks a ton!

    However after downloading c# application, I am having two problems

    1. Unable to locate 'F:TestExtensionAttributeWindowsAzureAD-GraphAPI-Sample-OrgChart-masterWindowsAzureAD-GraphAPI-Sample-OrgChart-master.nugetNuGet.exe' GraphHelper
    2. Also 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.

  10. Anonymous says:

    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?

  11. Mayuri M,

    1. Ed has just fixed this problem in GitHub – github.com/…/6c80718f69f5f23eab5142b6156ba43e7fc1b89e
    2. GraphHelper 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

  12. Anonymous says:

    Are multi-valued string attributes supported currently? If so, what does it take to create one such attribute?

    Thanks,

    Jameel

  13. Pavan Kompelli - MSFT says:

    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).

    Thanks

    Pavan

  14. Anonymous says:

    Hi, I am trying to create an extension as mentioned in the example above using the Advanced REST client extension of Chrome.  When I POST the request, I get an error stated below.

    My Application's object ID and all other values are correct. And the JSON format is the same as stated in the blog above. What could be missing here? Could you please help?

    {

    odata.error: {

    code: "Request_BadRequest"

    message: {

    lang: "en"

    value: "Invalid JSON. The property name '' is not valid. The name of a property cannot be empty."

    }-

    }-

    }

    Thanks

    Vibhuti

  15. Pavan Kompelli - MSFT says:

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

  16. Anonymous says:

    Hi Vibhuti, i created successfully with Postman extension on Chrome.

    Here is my raw request:

    POST graph.windows.net/…/extensionProperties HTTP/1.1

    Host: graph.windows.net

    Connection: keep-alive

    Content-Length: 70

    Cache-Control: no-cache

    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36

    Origin: chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcm

    Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhb[***]I6z3fKeI8e-kyq3Td8Dg

    Content-Type: application/json

    Accept: /

    Accept-Encoding: gzip,deflate,sdch

    Accept-Language: en-US,en;q=0.8,vi;q=0.6

    {

    "name": "skypeId",

    "dataType": "String",

    "targetObjects": ["User"]

    }

  17. Abhay (Bushel Tech) says:

    Hi,

    Does the Graph API not support content encoding to compress the response in gzip or some other format?

    1. 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.

    1. Could you please update the MSDN documentation above to detail the operations on Applications and Devices as well?

    Thanks

    Abhay

  18. Robert Ward - MSFT says:

    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

  19. Abhay (Bushel Tech) says:

    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.

    Thanks

    Abhay

  20. Abhay (Bushel Tech) says:

    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?

    Thanks

    Abhay

  21. Anonymous says:

    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.

  22. Anonymous says:

    Great Article!!

    Do we have this functionality available in the GraphClient library?

  23. Anonymous says:

    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!!!

  24. Anonymous says:

    Hi, how can we use extensions with v.2.0.0.?

    Regards,

    Alessandro

  25. Anonymous says:

    Any help for v2.0.2?

    thanks,

    Alessandro

  26. Anonymous says:

    Will there are be support for extensions that can store more than 256 bytes?

  27. Linda says:

    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.

  28. Rakesh Varna says:

    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

  29. Vikram Gore says:

    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..

  30. Vikram says:

    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?

  31. TSCH says:

    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.

  32. Eric Vogelpohl says:

    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.

  33. Phil0002 says:

    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.