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
- Anonymous
March 03, 2013
The comment has been removed - Anonymous
March 04, 2013
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. - Anonymous
March 18, 2013
The comment has been removed - Anonymous
March 20, 2013
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/.../sourcecodeThanks,Srikanth - Anonymous
March 20, 2013
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' - Anonymous
March 21, 2013
Hi Srikanth, The sample code supports only password Service Principal, but i'm using symmetric key Service Principalplease let me know how its possible via symmetric key service principal?thanks,Snegha - Anonymous
March 22, 2013
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 - Anonymous
March 22, 2013
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?ThanksSrikanth - Anonymous
March 23, 2013
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 thumbnailphotopublic 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 advanceKind regards,Snegha - Anonymous
March 25, 2013
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 - Anonymous
March 25, 2013
Hi Robert, It is working fine after catching the exception at response = dataService.GetReadStream( user, "thumbnailPhoto", new DataServiceRequestArgs()); when thumbnailPhoto is emptyThanks,Snegha - Anonymous
April 01, 2013
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 advanceKind regards,Snegha - Anonymous
April 02, 2013
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 - Anonymous
April 02, 2013
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 - Anonymous
April 03, 2013
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 - Anonymous
April 03, 2013
Hi Robert, Thank you so much Robert for the updates.kind regards,Snegha - Anonymous
April 22, 2013
The comment has been removed - Anonymous
April 23, 2013
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 ).ThanksSrikanth - Anonymous
February 25, 2014
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 advanceKind regards,Snegha - Anonymous
February 25, 2014
Hi, Error is thrown in the GetAuthorizationHeader() methodprivate 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 getActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.on lineAuthenticationContext _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 versionkind regards,Snegha - Anonymous
April 08, 2014
Hi Everyone!I need to retrieve ObjectId of an application I tried following waysGraph Explorer : Sign In-> Gives me server error2.With URL in the browser: graph.windows.net/.../applicationswhich 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? - Anonymous
October 01, 2014
Hello ,I have the same issue , and with same code from the sample :github.com/.../Office-365-APIs-Starter-Project-for-Windowstry{ 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 - Anonymous
October 01, 2014
Hello ,I have the same issue , and with same code from the sample :github.com/.../Office-365-APIs-Starter-Project-for-Windowstry{ 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