0.9 Version of Azure Active Directory Graph now available!


We have recently released 0.9 version of the Azure Active Directory Graph Service. The update includes some key improvements but it also comes with some significant breaking changes. We have just updated the .Net MVC Sample to work with the 0.9 version of the Service. As part of the sample, there is a thin helper library which includes the Service Reference for 0.9 version and includes some helper methods for setting up the required headers, query parameters etc.

Changes included in 0.9 Version of the Graph Service

  • Change in how you specify the Graph service version: Until 0.8 version of Graph service, it was required that the Version being targeted be specified as a Request Header. For 0.8 version, the header looked like ‘x-ms-dirapi-data-contract-version:0.8’. Starting with 0.9 version of the service, you need to specify the version via a Request parameter. For example, https://graph.windows.net/graphdir1.microsoft.com/users?api-version=0.9.
  • Support for Key as Segment feature: The way you would have requested a User with a particular UPN previously would be to specify the UPN with in parenthesis. An example of the previous syntax: https://graph.windows.net/graphdir1.onmicrosoft.com/users(‘Adam@GraphDir1.onmicrosoft.com’) . We received significant feedback that the URL syntax is not intuitive and it would be nicer if the UPN value (or the Key value) be specified as another segment in the URL. We are happy to inform that this support is included in the 0.9 version. Now you can specify the same request using the following syntax: https://graph.windows.net/graphdir1.onmicrosoft.com/users/Adam@GraphDir1.onmicrosoft.com.
  • Camel casing of Properties, Collection names etc.: All the property names, EntitySet/Collection names have been changed to use Camel casing instead of the Pascal casing. So previously if the property name was ObjectId, it will be objectId in 0.9 version.
  • New JSON formats: We have started supporting three different formats of JSON with this version – Verbose, minimal metadata and no metadata (which in turn have been introduced by WCF Data Services). In the next version of the Service, we will make JSON – minimal metadata the default format but if you want to start using it now, you can do it by passing in the accept header with the value: ‘application/json;odata=minimalmetadata’. If you want to see how the response looks in this format, you can try it in Graph explorer which has been changed to use this format by default.
  • Support for Password Reset Bypass on Next Login: Password property has changed to being a complex type with two primitive properties. The complex property name on User has the name ‘passwordProfile’ and it has two primitive properties – password and a boolean property – forceChangePasswordNextLogin which is useful when you want to set or unset the policy of a User being forced to change the password on first login for a particular user.
  • Collections for Subtypes( users, groups etc.) missing in .Net generated proxy class and $metadata: OData does not have good support for exposing different top level collections for types belonging to the same inheritance hierarchy like directoryObjects, users, contacts etc. We tried to work around this in the service layer but this was leading to various classes of bugs. In order to have a clean service implementation, we have removed these collections from the $metadata and thus you won’t see them in the generated proxy. If you want to access the users collection, you can access them by using code like the following: return this.directoryObjects.OfType<User>() as DataServiceQuery<User>; . While we don’t support these collections( users, groups etc.) in $metadata, we do support hitting the Service with the specific collections without any problem. So the following HTTP request is still supported: https://graph.windows.net/graphdir1.onmicrosoft.com/users/Adam@GraphDir1.onmicrosoft.com. On the Service side, we rewrite the request to https://graph.windows.net/GraphDir1.onmicrosoft.com/directoryObjects/$/Microsoft.WindowsAzure.ActiveDirectory.User/ Adam@GraphDir1.onmicrosoft.com which is the same as the HTTP request produced by the OfType Linq query.
  • WCF 5.2 or above tools needed to generate the Proxy: Because of the support for specifying the Key as segment, you need to regenerate the proxy if you want to target 0.9 version of the Schema. You can get the latest WCF tools from here.

As you can see, there are some significant changes in 0.9 version of the Service. If you hit any issues in upgrading your application to use 0.9 version, do let us know and we will be happy to help.


Comments (23)

  1. Mark Jones says:

    repost from: social.msdn.microsoft.com/…/c3a671fd-4a81-4476-bce5-6d4996ca7eb7

    I am not able to get the api-version parameter to work with AAL

    e.g. if I want to query user with a given upn – using the / format insead of ('')

    notice I have the lower case user now instead of "Users"

    userQuery = String.Format("{0}/{1}", "users", user.userPrincipalName)

    .CreateQuery(Of User)(userQuery) _

      .AddQueryOption("api-version", dataContractVersion) _

      .ToList().First()

    Results in this URL

    graph.windows.net/…/adam@graphdir1.onmicrosoft.com()

    Notice the extra () appended – by AAL ?

    An error occurred while processing this request.

    at System.Data.Services.Client.DataServiceRequest.Execute[TElement](DataServiceContext context, QueryComponents queryComponents)

       at System.Data.Services.Client.DataServiceQuery`1.Execute()

       at System.Data.Services.Client.DataServiceQuery`1.GetEnumerator()

       at System.Collections.Generic.List1..ctor(IEnumerable1 collection)

       at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)

    Also – when Creating a NEW user, it is not clear where to put the AddQueryOption for the api-version, because I don't have a DataServiceQuery –  

    it calls .AddObject and then .SaveChanges

  2. Mark,

    The best way to add the api-version parameter to all your requests would be to add it in the new event called OnBuildingRequest. You do need to regenerate your proxy using WCF 5.2 toolset or later( as mentioned in this blog post). Below I have copied and pasted code from the DirectoryDataService_partial.cs file in the helper library from Sample Application. Since the sample does not use AAL, the way the token is fetched will change a little. Other than that you should be able to use this code.

          public DirectoryDataService(string tenantName, AADGraphServiceAuthenticationToken token)

               : this(new Uri("graph.windows.net/" + tenantName))

           {

               this.authenticationToken = token;

               // Register the event handler that adds the headers for HTTP requests including the Authorization header.

               this.BuildingRequest += new EventHandler<BuildingRequestEventArgs>(OnBuildingRequest);

           }

    internal void OnBuildingRequest(object sender, BuildingRequestEventArgs args)

           {

               // Add the data contract version as a query argument

               args.RequestUri = GetRequestUriWithDataContractVersion(args.RequestUri, "0.9");

               // Add an Authorization header that contains an OAuth WRAP access token to the request.

               string authzHeader = String.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", authenticationToken.TokenType, " ", authenticationToken.AccessToken);

               args.Headers.Add("Authorization", authzHeader);

           }

           private static Uri GetRequestUriWithDataContractVersion(Uri origRequestUri, string apiVersion)

           {

               // Handle the api-version query argument to specify the data contract version

               NameValueCollection queryArguments = HttpUtility.ParseQueryString(origRequestUri.Query);

               if (String.IsNullOrEmpty(queryArguments["api-version"]))

               {

                   queryArguments["api-version"] = apiVersion;

               }

               UriBuilder uriBuilder = new UriBuilder(origRequestUri);

               uriBuilder.Query = queryArguments.ToString();

               return uriBuilder.Uri;

           }

    Thanks,

    Srikanth.

  3. Hi,

     I was trying to retrieve thumbNailPhoto of an user using this piece of code and I was able to retrieve via 0.8 version but not via 0.9 version.

             DataServiceStreamResponse response = null;

               try

               {

                   response = dataService.GetReadStream(

                           user, ThumbnailPhotoStreamName, new DataServiceRequestArgs());

               }

    I get an error

    {"error":{"code":"Request_DataContractVersionMissing","message":{"lang":"en","value":"The specified data contract version is invalid. The value must match one of the following supported versions: '0.5,0.8,0.9'."}}}

    can any one please tell me how to specify the "api-version=0.9" for the above piece of code.

    Thanks,

    Snegha

  4. The sample at the following location: code.msdn.microsoft.com/Write-Sample-App-for-79e55502  has been updated for 0.9 version. Look at the code of OnBuildingRequest event handler in this file for specifically adding the api-version query parameter: code.msdn.microsoft.com/…/sourcecode

    Thanks,

    Srikanth

  5. Hi, Why I can't filter the objectType?

    graph.windows.net/…/users(&$filter=objectType eq 'Group'

    it will will say "Applying filters to reference property queries is currently not supported"

    But I can filter the objectId:

    graph.windows.net/…/users(&$filter=objectId eq '1747ad35-dd4c-4115-8604-09b54f89277d'

  6. Hi Srikanth,

      The sample code supports only password Service Principal, but i'm using symmetric key Service Principal

    please let me know how its possible via symmetric key service principal?

    thanks,

    Snegha

  7. Hi Srikanth,

    Now I get this error

    {"odata.error":{"code":"Request_ResourceNotFound","message":{"lang":"en","value":"Resource 'thumbnailPhoto' does not exist or one of its queried reference-property objects are not present."}}}

    why its not possible to retrieve thumbnailPhoto?

    kind regards,

    Snegha

  8. Jack – Not all properties can be used in Filter clause. We are going to document what properties can be filtered on. In the mean time, if you can tell me about the scenario you are trying to enable, I will try to see if there is a work around.

    Snegha – Are you using the code from sample? Can you provide a snippet of code that's producing this error?Are you sure that there is a thumbnail photo for the user you are querying for?

    Thanks

    Srikanth

  9. Hi Srikanth,

    I'm sure that not all users containing thumbnailPhoto but few users has their photo.

    moreover I'm trying to retrieve all the users and their thumbnailPhoto.

      I use the following snippet to retrieve thumbnailphoto

    public static byte[] GetUserPhoto(User user)

           {

               DataServiceStreamResponse response = null;

               try

               {

                   if (user.thumbnailPhoto.ContentType != null)

                   {

                   response = dataService.GetReadStream(

                           user, "thumbnailPhoto", new DataServiceRequestArgs());

                   }

               }

               catch (DataServiceClientException ex)

               {

                   ParsedException pex = ParsedException.Parse(ex);

                   if (pex.Code == Constants.MessageIdResourceNotFound ||

                       pex.Code == Constants.MessageIdBadRequest)

                        return null;

               }

               byte[] buffer = new byte[32768];

               // Read the stream from response

               using (MemoryStream ms = new MemoryStream())

               {

                   while (true)

                   {

                       int read = response.Stream.Read(buffer, 0, buffer.Length);

                       if (read <= 0)

                       {

                           return ms.ToArray();

                       }

                       ms.Write(buffer, 0, read);

                   }

               }

           }

    Many thanks in advance

    Kind regards,

    Snegha

  10. Hi Snegha,

    The error 404 (Resource Not Found) that you previously mentioned is the expected error response when the targeted user object does not have a thumbnailPhoto value.  Are you certain that the targeted user does have a thumbnailPhoto in this case?

    Regarding the default DataServiceRequestArgs object in your GetReadStream request – in this case, it is good to change the Accept content type such that in the event of an error, the error response is returned in XML format, as the ParsedException class being used above assumes an XML format.  For example:

       DataServiceRequestArgs dataServiceRequestArgs = new DataServiceRequestArgs();

       // Include application/xml so that if an error results, it comes in XML format.

       dataServiceRequestArgs.AcceptContentType = "/, application/xml";

    Regarding your "if (user.thumbnailPhoto.ContentType != null)" check, if you retrieved a set of users all at once, and are enumerating those users and obtaining the photo for each, this check might be a problem, as there is a difference between cases where you query for a particular user vs. a request that returns multiple users.  When multiple users are returned, the service will always return a general thumbnailPhoto content type value for all users.  This makes it ambiguous for you to determine whether there really is a thumbnailPhoto or not.  If you query for a specific user alone, you can be sure that the presence of a thumbnailPhoto content type reflects whether there is an associated image or not.  We will be addressing this in an upcoming release.

    Note that with WCF Data Services 5.2, the DataServiceContext.BuildingRequest event was added.  This is a good place to log debug information about every request that is sent to the server, including the request method (e.g. GET/POST/etc.), URI, and headers.  Logging this information should prove helpful with general debugging of such issues.

    Thanks,

    Robert

  11. Hi Robert,

         It is working fine after catching the exception at    

    response = dataService.GetReadStream(

                          user, "thumbnailPhoto", new DataServiceRequestArgs()); when thumbnailPhoto is empty

    Thanks,

    Snegha

  12. Hi,

      Today I get the following error while accessing WAAD graph API

    "<?xml version="1.0" encoding="utf-8"?>

    <m:error xmlns:m="schemas.microsoft.com/…/metadata"><m:code>

    Authentication_MissingOrMalformed</m:code><m:message xml:lang="en">Access Token missing or malformed.

    </m:message></m:error>"

    which was working fine till this afternoon and later on i get the above error. Is anything changed in Graph API?

    Many thanks in advance

    Kind regards,

    Snegha

  13. Hi Snegha,

    No changes were made to the service such that this error would be caused.  If you are still experiencing errors, can you please provide details as to how you are constructing the Authorization header value?

    Thanks,

    Robert

  14. Hi Robert,

            All of a sudden it started to work fine from yesterday evening, so I'm pretty sure its not my code problem.

    moreover I tried the same in Graph Explorer and i got the above error as I mentioned and I couldn't get the Login screen where I used to provide my symmetric key and App Principal Id.

    later evening yesterday everything works fine. I really don't have any clue where the problem was created.

    kind regards,

    Snegha

  15. Thanks for the update Snegha.  Yesterday, in the early morning PST when you had reported this problem, there had been an transient issue with a set of authentication service servers, and I suspect that this was the source of your errors.  Glad that you're up and running again!

    Thanks,

    Robert

  16. Hi Robert,

      Thank you so much Robert for the updates.

    kind regards,

    Snegha

  17. i am getting this error

    "the specified data contract version is invalid. The value must match one of the following supported versions: '0.5,0.8,0.9,1.0'."

    i have mentioned the api-version in my url.

    0.9 api-version was working fine few days before but now its not working

    i have mentioned like this

    "/users('User_fcee1c5c-………-48f5f0e3e69b')?api-version=0.9"

    pl help through this

  18. Ashik,

    I could not reproduce the error. Can you try to capture the full URL and post it. The only time I see this error is when the api-version value is wrong( which is not true for your case since 0.9 is a valid value) or when api-version is specified more than once( like '/users('…..')?api-version=0.9&api-version=0.8 ).

    Thanks

    Srikanth

  19. snegha says:

    Hi,

     From yesterday I was having trouble while accessing WAAD graph API and I get the below error.

    "<?xml version="1.0" encoding="utf-8"?>

    <m:error xmlns:m="schemas.microsoft.com/…/metadata"><m:code>

    Authentication_MissingOrMalformed</m:code><m:message xml:lang="en">Access Token missing or malformed.

    </m:message></m:error>"

    which was working fine till 2 days before and past 2 days i get the above error. Is anything changed in Graph API?

    this is my graph api query

    {graph.windows.net/…/directoryObjects$/Microsoft.WindowsAzure.ActiveDirectory.User()?api-version=0.9}

    Many thanks in advance

    Kind regards,

    Snegha

  20. snegha says:

    Hi,

      Error is thrown in the GetAuthorizationHeader() method

    private static string GetAuthorizationHeader()

           {

               // AAL

               string authzHeader = null;

               AuthenticationContext _authContext = new AuthenticationContext(fullTenantName);

               try

               {

                   SymmetricKeyCredential credential = new SymmetricKeyCredential(issuingResource,

                                                                                  Convert.FromBase64String(

                                                                                      servicePrincipalKey));

                   AssertionCredential _assertionCredential = _authContext.AcquireToken(serviceRealm, credential);

                   authzHeader = _assertionCredential.CreateAuthorizationHeader();

               }

               catch (Exception ex)

               {

                   AALException aex = ex as AALException;

                   string a = aex.Message;

               }

               return authzHeader;

           }

    I get

    ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.

    on line

    AuthenticationContext _authContext = new AuthenticationContext(fullTenantName);

    and

    {"AAL 0x80100018: Token request from Access Control service failed.  Check InnerException for more details"}

    Inner exception

    {"ACS50027: JWT token is invalid.rnTrace ID: d6179f78-7fbc-4ac0-bff7-a917161b1193rnCorrelation ID: 6d2b7b8c-2678-4a50-ad29-fcb7d442575ernTimestamp: 2014-02-25 16:24:07Z"}

    on line

     AssertionCredential _assertionCredential = _authContext.AcquireToken(serviceRealm, credential);

    does anything changed recently ?

    Im currently using 0.9 graph API version

    kind regards,

    Snegha

  21. Mayuri M says:

    Hi Everyone!

    I need to retrieve ObjectId of an application I tried following ways

    1. Graph Explorer : Sign In-> Gives me server error

    2.With URL in the browser: graph.windows.net/…/applications

    which says -> Authentication_MissingOrMalformedAccess Token missing or malformed.

    2 I tried Get method using fiddler -> Gives me same error ->Authentication_MissingOrMalformedAccess Token missing or malformed.

    Can someone help me for this?

  22. ahmad Abu Ghoush says:

    Hello ,

    I have the same issue , and with same code from the sample :

    github.com/…/Office-365-APIs-Starter-Project-for-Windows

    try

    {

       using (var dssr = await user.ThumbnailPhoto.DownloadAsync())

       {

           var stream = dssr.Stream;

           var buffer = new byte[stream.Length];

           await stream.ReadAsync(buffer, 0, (int) stream.Length);

           ProfileImage = buffer;

       }

    }

    catch (Exception ex)

    {

       Debug.WriteLine(ex);

    }

    however each time i try to bring user's thumbnail photo , I get the following error :

    "Resource 'thumbnailPhoto' does not exist or one of its queried reference-property objects are not present."

    I'm using an Admin user (Global Admin) in the add connected service and for sign in .

    I searched for what you said :

    "and these photos are actually stored in the Exchange mailbox itself rather than thumbnailPhoto in Azure AD or your local AD (this may be the key piece you are looking for).  So, it is most likely that the photo is stored in exchange and actually replicated or copied to the AAD."

    but i didn't find any thing useful  .

    will you please help me

  23. Ahmad Abu Ghoush says:

    Hello ,

    I have the same issue , and with same code from the sample :

    github.com/…/Office-365-APIs-Starter-Project-for-Windows

    try

    {

       using (var dssr = await user.ThumbnailPhoto.DownloadAsync())

       {

           var stream = dssr.Stream;

           var buffer = new byte[stream.Length];

           await stream.ReadAsync(buffer, 0, (int) stream.Length);

           ProfileImage = buffer;

       }

    }

    catch (Exception ex)

    {

       Debug.WriteLine(ex);

    }

    however each time i try to bring user's thumbnail photo , I get the following error :

    "Resource 'thumbnailPhoto' does not exist or one of its queried reference-property objects are not present."

    I'm using an Admin user (Global Admin) in the add connected service and for sign in .

    I searched for what you said :

    "and these photos are actually stored in the Exchange mailbox itself rather than thumbnailPhoto in Azure AD or your local AD (this may be the key piece you are looking for).  So, it is most likely that the photo is stored in exchange and actually replicated or copied to the AAD."

    but i didn't find any thing useful  .

    will you please help me