Quantcast
Channel: Windows Azure Active Directory Graph Team
Viewing all 46 articles
Browse latest View live

Welcome!

$
0
0

Welcome to Windows Azure Active Directory( AAD ) Graph Team blog! The AAD Graph team is responsible for creating RESTful services that developers can consume to integrate AAD functionality into their applications.

Purpose of the blog

We will use this blog to have a regular and open communication with the developer community by providing updates on our progress, design ideas, walkthroughs, samples  etc. We would also like to hear back from you on what you would like to see in the service and in this blog - any particular functionality that you are missing, any particular samples you would like to see etc.

AAD Graph Overview

Windows Azure Active Directory( AAD ) provides identity management and access control capabilities for your cloud applications. The AAD graph API will let you access the information in the directory through a very simple RESTful service. The AAD graph service is built upon Web technologies like HTTP, JSON and OData and thus it is platform and device independent. You should be able to build your application using your favorite platform whether it be .Net, PHP, Java script, Java etc and should be able to consume the Graph API in a simple manner. There are several classes of cloud applications and the specific needs for these are vastly different. We will try to provide samples and walk throughs for some end to end scenarios on different platforms using Graph API through this blog.

What is available now?

The AAD graph service is currently available as a developer preview here. Since this is a developer preview, please expect some breaking changes as we try to incorporate additional functionality and improve the overall experience.

Additional Resources

You can find more information about Windows Azure Active Directory here.

The MSDN page for AAD graph has some useful links and also includes the documentation for the service end point.  


Azure Active Directory Graph – What’s in it for an app developer?

$
0
0

As we mentioned in the last post, Windows Azure Active Directory (AAD) Graph service lets an application developer access and manipulate the information in Azure Active Directory. But why should an application developer care about this information? Let’s start by looking at Windows Active Directory and drill down to the Graph service for Azure Active Directory which this blog will focus on.

 

What is Active Directory (AD)?

Active Directory provides authentication and authorization for users and computers in Windows Networks. For example, one of the things AD does when user logs in is it verifies that the user name and password are correct. AD also has a lot of interesting information about the user like the information you would find in an address book and the organizational information (like who he reports to, who are his direct reports) etc. This information is used to authorize user when he tries to access resources on the network. This is a very high level overview of Active Directory. There is plenty of information on MSDN, Wikipedia etc. that talks about AD in great detail.

 

What is Windows Azure Active Directory?

Windows Azure Active Directory(AAD) provides identity management and access control capabilities for cloud applications including Microsoft Cloud applications like Windows Azure, Office 365, Dynamics CRM etc. and also for 3rd party cloud applications. Like Windows Active Directory, Azure Active Directory also provides authentication services by verifying the user name and password for the users when they login. Like AD, it also has a lot of interesting information about the user like the information you would find in an address book and the organizational information (like who he reports to, who are his direct reports) etc. In addition to providing the authentication services using the credentials it stores, AAD also can authenticate users with their on premise Windows Active Directory credentials.

 

Using Azure Active Directory in Enterprise Cloud Applications

There are several advantages in integrating your application with AAD for authentication and authorization needs. Given that AAD is leveraged by Microsoft cloud services, you have the ability to provide your application users with a single sign on experience across your application and services like Office 365, Windows Azure etc. You can manage access to your application based on roles and policies that you can set up in AAD. AAD not only can help you when building Line of business (LOB) cloud applications for your organization but also when building enterprise cloud applications that you plan to sell to other organizations. Since AAD is a multi-tenant service, the authentication and access control mechanism that you build into your application can be designed in such a way that it can be used with any tenant or organization provisioned in Azure Active Directory. Given that a lot of organizations are already using Office 365, it is a great convenience for these organizations if they can reuse their information in AAD with 3rd party cloud applications.

There is a ton of useful information in Azure Active Directory that the application developers can use for many different purposes. But how do you access this information? This is where AAD Graph come into the picture.

 

Azure Active Directory Graph

The AAD Graph Service lets you access the information in the AAD through a simple RESTful service. It uses the OData protocol to implement the RESTful service. Understanding some of the basics of RESTful services and the OData protocol is important to gain a solid understanding of how to work with AAD Graph service.

 

What is a RESTful Service?

There is plenty of information about REST on MSDN, Wikipedia and other web sites. Here is a pretty short and high level overview of RESTful services.

Representational State Transfer( REST ) is an architectural style with a set of rules that can be applied when building something( from MSDN article on REST and WCF Services ).

A RESTful service exposes data as a set of Resources on web and follows these rules:

  • Is implemented using HTTP
  • Different operations like Read, Create, and Delete etc. are supported via HTTP methods like Get, Post, Delete etc.
  • The set of resources exposed by the service have a well-defined set of URIs.
  • Data encoding is in Xml or JSON formats.
  • The API is hypertext driven i.e. the response will include all the information needed to interpret it.

 

What is OData?

OData is a web protocol for querying and updating data via RESTful services. It helps you expose data over the web as a set of resources which can be accessed and manipulated using HTTP methods.

Let’s look at some of the important rules/principles for services implemented using the OData protocol:

  • The root of the service always exposes a service document. Service document exposes the list of all resource collections available in the service.
    • For example, http://services.odata.org/OData/OData.svc is the service document for a sample OData service. As you can see, Products, Services and Suppliers are the resource collections exposed by this service.
  • It defines a set of conventions to define the URIs for the resources. For example, the URI for a resource set exposed by the service has the URI of the format <ServiceDocumentURI>/<ResourceSetName>. Based on this convention, you can construct the URI for “Products” Resource Set which would be http://services.odata.org/OData/OData.svc/Products.
  • You can use HTTP operations like GET, POST, Delete etc. to support retrieving, creating, updating data exposed by the service. For example, doing a HTTP Get on the following URL: http://services.odata.org/OData/OData.svc/Products(1) will retrieve the content of the resource in the Products Resource set with Key value ‘1’.
  • Defines a set of query options that can be represented as HTTP Request parameters and are used to perform advanced query operations like Filter, Top etc. Here is an example of a query that filters the Categories Resource set for Resources whose name equals ‘Beverages’: http://services.odata.org/OData/OData.svc/Categories(1)?$filter=Name eq 'Beverages' .

 

So we have described that a RESTful service built using OData protocol can be accessed and manipulated using HTTP operations. We also mentioned that AAD Graph is one such service. In the next blog post, let’s do a deep dive into various operations you can do against the AAD Graph service using HTTP and also look at the responses we get back.

 

Understanding Azure Active Directory Graph Operations

$
0
0

We mentioned in the last post that AAD Graph is a RESTful service implemented using the OData protocol. We also provided a high level overview of RESTful services and OData protocol. In this post, we will try to provide deeper insights into the operations supported by the Graph service and how to invoke these operations.

AAD Graph Service is a multitenant service which means that the service supports multiple tenants/organizations. When you connect to the service for accessing the information, you need to provide the name of the tenant you would like to work with. For this post, we will use a Demo tenant (GraphDir1.OnMicrosoft.com) that exists in AAD. You can use the Graph Explorer tool for trying out the examples in this post against this demo tenant.

Required HTTP Request Headers and Query Parameters

All operations on Graph service can be done using standard HTTP operations like Get, Post, and Delete etc. We will see plenty of examples of what the request and response data will be for various operations. But all these HTTP requests are also required to specify one HTTP header (Authorization) and one Query parameter (api-version) in the request. Let's look at what those are.

  • Authorization header: Any application that’s accessing graph data should present a token that authorizes access to the Graph data for a tenant. We will look at how to fetch this token value and pass it in Authorization header in later posts when we start building sample applications. For now, the most important thing is to understand the URL patterns for accessing various resources in AAD Graph and the data that you will send and will receive for various operations. The name of the header is "Authorization" and the header value will be something like below.

            Authorization: Bearer eyJ.............................................Hg

  • api-version query argument: You need to specify the version of the Graph service resource you want to access. The service version information must be specified as a query parameter with name ‘api-version’ . The value will look something like below.

            https://graph.windows.net/GraphDir1.OnMicrosoft.com/users?api-version=2013-04-05

Now we are ready to look at how to do various data access and manipulation operations on the Graph service. Let’s start with the Service root document and drill down into the details.

Accessing Graph Service Root

The service root lists the set of Resource collections that are exposed by the AAD Graph service. As you can see from the response, the Resource collections (also referred to as feeds) exposed by AAD Graph service includes Users, Contacts, and Groups etc.

Request URL:

https://graph.windows.net/GraphDir1.OnMicrosoft.com/

 

HTTP Method: GET

Response:

{
  "name": "GraphDir1.OnMicrosoft.com",
  "services": [
    "https://graph.windows.net/GraphDir1.OnMicrosoft.com/users",
    "https://graph.windows.net/GraphDir1.OnMicrosoft.com/applications",
    "https://graph.windows.net/GraphDir1.OnMicrosoft.com/contacts",
    "https://graph.windows.net/GraphDir1.OnMicrosoft.com/groups",
    "https://graph.windows.net/GraphDir1.OnMicrosoft.com/roles",
    "https://graph.windows.net/GraphDir1.OnMicrosoft.com/servicePrincipals",
    "https://graph.windows.net/GraphDir1.OnMicrosoft.com/tenantDetails",
    "https://graph.windows.net/GraphDir1.OnMicrosoft.com/devices",
    "https://graph.windows.net/GraphDir1.OnMicrosoft.com/subscribedSkus",
    "https://graph.windows.net/GraphDir1.OnMicrosoft.com/permissions",
    "https://graph.windows.net/GraphDir1.OnMicrosoft.com/directoryObjects"
  ]
}

 

Accessing Resource Collection

You can construct the URL for accessing a specific Resource collection, for example users, by appending the name of the Resource collection to the service root URL. Let’s take the example of accessing the users resource collection. You can see that the response is an array of JSON objects (of length 2 in this case). The response has been trimmed down by removing some properties so that it is more readable.

Note: The first line in the response below which is the “odata.metadata” property provides information on how to access additional metadata for this response pay load if you need to. If you go to the URL below, you will see an Xml document that describes the metadata for the collections exposed by this service. You can read all about this Xml format here. In most of the applications, you will be looking at just the data part of the response and thus will ignore this property value.

Request URL: 

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users?api-version=2013-04-05

 

HTTP Method: GET

Response:

{

  "odata.metadata": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/$metadata#directoryObjects/Microsoft.WindowsAzure.ActiveDirectory.User"   

  "value":[

    {

        "objectId": "e428a9cb-7550-4991-afc3-48fe8b60be33",

        "accountEnabled": true,

        "city": "Seattle",

        "displayName": "Adam Barr"

    },

    {

        "objectId": "bafb9fea-d7f3-4cec-af6a-bca2b553e83b",

        "accountEnabled": true,

        "city": null,

        "displayName": "Admin",

    }

  ]

}

 

Accessing specific Resource using Key value

Every OData Resource (also referred to as entry) has one or more properties that are annotated as the Key property in the metadata definition. Each Resource in the Resource collection can be uniquely identified in that collection given the value of the key property. For AAD Graph Service, ObjectId property acts as the Key property for all Resource types.

You can access a specific Resource in a collection by appending the key value in the segment following the resource collection name. For users resource alone you can specify either the objectId of the User or the userPrincipalName of the user as the key value. In the below example, we are using userPrincipalName property value as the key value.

Request URL: 

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users/Adam@GraphDir1.onmicrosoft.com?api-version=2013-04-05

 

HTTP Method: GET

Response:

    "odata.metadata": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/$metadata#directoryObjects/Microsoft.WindowsAzure.ActiveDirectory.User/@Element",

    "objectId": "e428a9cb-7550-4991-afc3-48fe8b60be33",

    "accountEnabled": true,

    "city": "Seattle",

    "displayName": "Adam Barr"

}

 

Navigating to related resources through link properties

As the name suggests, AAD Graph exposes a Graph of objects interconnected via links. An entry in Graph Service can have two types of link properties - one which links it to at most one entry (example: manager property on User) and the second which links it to a collection of entries (example: directReports property on User).

For Single Valued Links

Let’s try to navigate to the manager for the User entry we retrieved in the previous section. "manager" is a single valued link since there can be at most one manager for a User. The first thing you need to find is the name of the navigation property that you would want to navigate. You can find this information in the Metadata document for Graph service at: https://graph.windows.net/GraphDir1.OnMicrosoft.com/$metadata . In this document, find the EntityType whose name is DirectoryObject. If you look at the list of the properties in this type, you can see that there are three navigation properties: manager, directReports and memberOf. The User type derives from DirectoryObject and thus inherits all the properties from DirectoryObject including these navigation properties. Now that we know the navigation property name, it is just a matter of appending this property name to the URL of the entry to navigate to the related entry. So here is the example where we will retrieve the manager entry for the User retrieved in the previous example.

Request URL: 

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users/Adam@GraphDir1.onmicrosoft.com/manager?api-version=2013-04-05

 

HTTP Method: GET

Response:

{      

    "odata.metadata": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/$metadata#directoryObjects/@Element",

    "objectId": "0758e306-1951-4761-99cc-6aecbcdf3d00",

    "displayName": "Derek Brown",

    "mail": Derek@GraphDir1.onmicrosoft.com

}

 

For Multi Valued Links

Let’s try to find the groups and roles that the User entry we retrieved in the previous section is member of. "memberOf" is a multi valued link since there can be any number of roles and groups that a user can be member of. The access pattern for single valued and multi valued links is the same. The only difference is that the response for multi-valued links is a collection instead of a single object.

Request URL: 

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users/Adam@GraphDir1.onmicrosoft.com/memberOf?api-version=2013-04-05

 

HTTP Method: GET

Response:

{
  "odata.metadata": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/$metadata#directoryObjects",
  "value": [
    {
      "odata.type": "Microsoft.WindowsAzure.ActiveDirectory.Group",
      "objectType": "Group",
      "objectId": "f9d373df-b75b-49e3-8a83-0d5dc112ef4c",
      "description": null,
      "dirSyncEnabled": null,
      "displayName": "PureEmailDistributionGroup",
      "lastDirSyncTime": null,
      "mail": "PureEmailDL@GraphDir1.onmicrosoft.com",
      "mailNickname": "PureEmailDL",
      "mailEnabled": true,
      "provisioningErrors": [],
      "proxyAddresses": [
        "SMTP:PureEmailDL@GraphDir1.onmicrosoft.com"
      ],
      "securityEnabled": false
    },
    {
      "odata.type": "Microsoft.WindowsAzure.ActiveDirectory.Group",
      "objectType": "Group",
      "objectId": "1747ad35-dd4c-4115-8604-09b54f89277d",
      "description": "Uses in Washington State",
      "dirSyncEnabled": null,
      "displayName": "Wash State",
      "lastDirSyncTime": null,
      "mail": null,
      "mailNickname": "BposMailNickName",
      "mailEnabled": false,
      "provisioningErrors": [],
      "proxyAddresses": [],
      "securityEnabled": true
    }

]
}

 

 

Fetching the related resource URL using $links

For single valued links

 

Instead of retrieving the related object through navigation property, you can also retrieve just the URL of the related Object. The URL for fetching the related object’s URL is same as the URL for fetching the related object except  in the case of fetching the URL we will have $links in between the URL for the Resource and the name of the navigation property. Going through the $links will fetch the URL of the link instead of the related object.

Request URL:

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users/Adam@GraphDir1.onmicrosoft.com/$links/manager?api-version=2013-04-05

 

HTTP Method: GET

Response:

{
 "odata.metadata": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/$metadata#directoryObjects/$links/manager",
  "url": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/directoryObjects/0758e306-1951-4761-99cc-6aecbcdf3d00/Microsoft.WindowsAzure.ActiveDirectory.User"
}

 

For multi valued links

 

The access pattern for navigating the links is the same for multi valued links( as single valued) but the response will be a collection of URLs instead of one. Let's navigate the memberOf link as an example.

 

Request URL:

 

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users/Adam@GraphDir1.onmicrosoft.com/$links/memberOf?api-version=2013-04-05

 

HTTP Method: GET

 

Response:

 

{
  "odata.metadata": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/$metadata#directoryObjects/$links/memberOf",
  "value": [
    {
      "url": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/directoryObjects/f9d373df-b75b-49e3-8a83-0d5dc112ef4c/Microsoft.WindowsAzure.ActiveDirectory.Group"
    },
    {
      "url": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/directoryObjects/1747ad35-dd4c-4115-8604-09b54f89277d/Microsoft.WindowsAzure.ActiveDirectory.Group"
    },
    {
      "url": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/directoryObjects/a2b236a0-930d-4e5e-85c3-3e9a9df6b525/Microsoft.WindowsAzure.ActiveDirectory.Group"
    },
    {
      "url": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/directoryObjects/471effd8-5056-4f13-8ebc-0dd70a838f7e/Microsoft.WindowsAzure.ActiveDirectory.Group"
    }
  ]
}

 

Query Operations

OData specifies a set of query options that can be expressed via HTTP request parameters. These query options can be used to query a service for advanced query operations like top, filter etc. Let’s look at some examples here. You can find the complete documentation for Query options in OData here. Since the semantics of these query operations are well known, let’s just look at the URLs and responses.

 

Query Operation: Top

 

Description of the Operation:

Retrieve the top 1 User from the Users Resource collection. The response will include a next link property at the end whose value you can use to get the remaining resources that were not included in this response.

Request URL: 

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users?$top=1&api-version=2013-04-05

 

HTTP Method: GET

Response:

  "odata.metadata": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/$metadata#directoryObjects/Microsoft.WindowsAzure.ActiveDirectory.User",

  "value":[

    {

        "objectType": "User",

        "objectId": "e428a9cb-7550-4991-afc3-48fe8b60be33",

        "accountEnabled": true,

        "city": "Seattle",

        "displayName": "Adam Barr"

    }

  ],

  "odata.nextLink": "Users?$skiptoken=X'445370740200….”

}

 

Query Operation: Filter

 

Description of the Operation:

Retrieve the list of Users from the users Resource collection whose displayName equals ‘Derek Brown’.

 

Request URL: 

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users?$filter=displayName%20eq%20'Derek%20Brown'&api-version=2013-04-05

 

HTTP Method: GET

Response:

{      

    "odata.metadata": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/$metadata#directoryObjects/@Element",

    "objectId": "0758e306-1951-4761-99cc-6aecbcdf3d00",

    "displayName": "Derek Brown",

    "mail": Derek@GraphDir1.onmicrosoft.com

}

 

At this point, we have covered most of the interesting access patterns for Graph service. Using these examples, you should be able to retrieve most of the information in Graph. Next let’s look at some examples for Creating, Updating and Deleting entries and links.

 

Creating an Entry

You can use a HTTP Post operation to create an Entry. The URL that you post to would be the collection URL of the resource collection to which the created entry needs to be added to. The body of the request should have the data for the entry you want to create. The object that was created by this request will be returned as response. If you look at the request, you will see that it only has 4 properties while response has many additional properties. This is because we sent only the required properties for the User entry while response included all properties of User entry including the ones that are not set or generated by the service. For example, note the objectId property of the entry in the response which was generated by the Service. For readability, we have trimmed down the properties shown in the Response.

Request URL: 

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users?api-version=2013-04-05

 

HTTP Method: POST

Request:

{

        "displayName": "TestUser22",

        "userPrincipalName": “tuser22@GraphDir1.onmicrosoft.com”,

        "password": “XXXXXXXX”

        "accountEnabled": “True”          

}

 

 

Response:

    "odata.metadata": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/$metadata#directoryObjects/Microsoft.WindowsAzure.ActiveDirectory.User",

    "objectId": "ff7cd54a-84e8-4b48-ac5a-21abdbaef321",

    "displayName": "TestUser22",

    “objectType”: “User”,

    "mail": “null”,

    "userPrincipalName": “tuser22@GraphDir1.onmicrosoft.com

}

 

Updating an Entry

You can use a HTTP Patch operation to update an Entry. The URL that you use is the URL of the entry that you want to update. The body of the request should have the updated data for the entry and optionally you can include other properties that have not changed. The Service will update the content of the entry based on the content in the message. The object that was updated by this request will be returned as response. Let’s update the User entry we just created in the previous example by updating the City value to Redmond. Again the Response data have been trimmed down to make it readable.

Request URL: 

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users/ff7cd54a-84e8-4b48-ac5a-21abdbaef321?api-version=2013-04-05

 

HTTP Method: Patch

Request:

{              

    "city": “Redmond”

}

 

Response:

    "odata.metadata": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/$metadata#directoryObjects/Microsoft.WindowsAzure.ActiveDirectory.User",

    "objectId": "ff7cd54a-84e8-4b48-ac5a-21abdbaef321",

    "displayName": "TestUser22",

    “objectType”: “User”,

    "mail": “null”,

    "userPrincipalName": “tuser22@GraphDir1.onmicrosoft.com”,

     "city": “Redmond”

}

 

Deleting an Entry

You can delete an entry by using HTTP Delete operation against the Entry URL. You don’t need to send any data in the request and you won’t get any data in the response back other than the HTTP success code if the delete succeeded.

Request URL:                                                                                                                                                                                                                                             

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users/ff7cd54a-84e8-4b48-ac5a-21abdbaef321?api-version=2013-04-05

 

HTTP Method: Delete

 

Adding a Link between entries

We saw in the example for navigating through links using navigation properties that the URL using $links can be used to get the URL for the related object( for a single valued link) or a collection of URLs of related objects( for multi valued links).You can add a new link by posting to the same URL and provide the URL of the entry you would want to link to as the request body. You won’t get any data back in the response other than the HTTP success code if the operation succeeded. Let’s see an example for adding link between a User entry and another User entry as the manager( single valued link) and add a User to a Group using members property on Group( multi valued link).

Note: You need to use “url” as the property name for the URL of the entry you would like to link to in the request pay load as shown in the example below.

Single Valued link:

 

Request URL: 

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users/Adam@GraphDir1.onmicrosoft.com/$links/manager?api-version=2013-04-05

 

HTTP Method: PUT

Request:

    {

      “url”: “https://graph.windows.net/GraphDir1.OnMicrosoft.com/users/ff7cd54a-84e8-4b48-ac5a-21abdbaef321"

    }

 

Multi Valued link:

 

Request URL: 

https://graph.windows.net/GraphDir1.OnMicrosoft.com/groups/1625fccb-5870-4a80-a031-b0ec656ca0c9/$links/members

 

HTTP Method: POST

Request:

    {

      “url”: “https://graph.windows.net/GraphDir1.OnMicrosoft.com/users/ff7cd54a-84e8-4b48-ac5a-21abdbaef321"

    }

 

 

Deleting a Link between entries

Single Valued Link

 

Deleting a link is very similar to deleting an entry. For single valued, you would send a HTTP Delete request to the URL of the link.

Request URL: 

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users/e428a9cb-7550-4991-afc3-48fe8b60be33/$links/manager?api-version=2013-04-05

 

HTTP Method: Delete

Multi Valued Link

 

For multi-valued link, the Delete request should be sent to the link that identifies the specific link we would like to delete since the link property it self would map to multiple links and delete would fail. The objectId of the instance of the object on the other end of the link( objectId of the User in case of members property) can be used to specify the specific link that needs to be deleted.

Request URL: 

https://graph.windows.net/GraphDir1.OnMicrosoft.com/groups/1625fccb-5870-4a80-a031-b0ec656ca0c9/$links/members/8004b5fc-e0fe-b46f-8c136b32d6c0?api-version=2013-04-05

 

HTTP Method: Delete

 

What’s next?

We have seen how to access collections, entries in collections and navigate through the Directory Graph. We also have seen examples of using OData query syntax to perform some advanced query operations. We also saw the examples to create, update and delete entries and links. We could do all of this using standard HTTP operations. So if you are going to integrate the AAD Graph service into your application, all you need from your development platform is support for sending http requests and reading JSON response back. As the next step, let’s build a couple of sample applications (one in .Net using MVC and one in PHP) to understand what it takes to perform these operations on different platforms. 

Walk through for building a .Net application for accessing Windows Azure Active Directory Graph Service

$
0
0

The post provides a walk through for accessing Azure Active Directory (AAD) Graph Service through a .Net application. If you would like to download a full working sample, you can download the sample MVC application from here.  If you would like to learn more about Azure Active Directory Graph Service, visit our previous blog posts.

Creating a sample tenant

You can now manage your azure active directory tenants through Windows Azure Management Portal. If you do not have an azure subscription, it is easy to sign up for one using free trial. If you login to the Azure management portal, you will see an active directory tab. If you have an existing Azure AD tenant associated with this Azure subscription, it will show up here. If you don't have one, you can create one using the UX. 

Register your application for working with AAD

The next step is to register our application with Azure AD which will let the application access the data in Azure AD through Graph. The following MSDN topic provides a step by step instruction on registering application: http://msdn.microsoft.com/library/windowsazure/dn151791.aspx#BKMK_Configuring . If you followed the steps, you will have the ClientID and Key values that you will need later as you build the application.

Getting Authentication Token from Windows Azure AD for accessing Graph Service

An authentication token from Windows Azure AD is required to access Graph Service. The authentication token proves that the application has been authorized to access the directory information for the tenant in Azure AD through Graph service. To get the token, we need to provide the TenantDomainName, AppPrincipalId/ClientId and Password/Key generated in the previous step to Azure AD via a HTTP post request. Here is a code snippet that puts together all of this information in the format that Azure AD expects and sends it over in a HTTP Post request. Windows Azure AD can provide authentication tokens for accessing different services and if you need to change the code to get token for accessing a different service than Graph, you have to provide the principal id of that service instead of the one for Graph service.

 

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(String.Format(
                          "https://login.windows.net/{0}/oauth2/token?api-version=1.0",
                          tenantName));
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
string postData = "grant_type=client_credentials";            
postData += "&resource=" + HttpUtility.UrlEncode("https://graph.windows.net");
postData += "&client_id=" + HttpUtility.UrlEncode(appPrincipalId);
postData += "&client_secret=" + HttpUtility.UrlEncode(password);
byte[] data = encoding.GetBytes(postData);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
using (Stream stream = request.GetRequestStream())
{
    stream.Write(data, 0, data.Length);
}
 
 

If successfully authenticated, we will get back a JSON response from Azure AD with token_type, access_token and a few other fields. A sample response for a successful authentication request will look like this:

{"token_type":"Bearer","access_token":"eyJ0eXA…..Q","expires_in":"43199","not_before":"1358887748","expires_on":"1358930948","resource":00000002-0000-0000-c000-000000000000/graph.windows.net@4fd2b2f2-ea27-4fe5-a8f3-7b1a7c975f34}

 

We need to take the token_type and access_token fields from this response for putting together the Authorization header for Graph service. 

using(varresponse=request.GetResponse()){using(varstream=response.GetResponseStream()){DataContractJsonSerializerser=new DataContractJsonSerializer(typeof(AADTokenFormat));AADTokenFormattoken=(AADTokenFormat)(ser.ReadObject(stream));returnString.Format(CultureInfo.InvariantCulture,"{0}{1}{2}",token.token_type," ",token.access_token);}}

The AADTokenFormat class used above has two fields token_type and access_token and is used to read the JSON response into the strongly typed object. 

[DataContract]internal classAADTokenFormat{[DataMember]internalstringtoken_type{get;set;}[DataMember]internalstringaccess_token{get;set;}} 

Creating the Service Reference for Graph Service

Now that we have the Authorization header, we are ready to access Graph service for querying and updating the information for our tenant. In the last post we saw that all operations against Graph service can be done using standard HTTP operations like GET, POST, DELETE etc. and advanced query operations can be done using OData query options via HTTP request parameters. We can definitely work with the Service using HTTPRequest and HTTPResponse by putting together the requests manually and parse the responses and extract information we need. But since Graph service is an OData service, we can use the WCF Data Services client library which provides a strongly typed access for working with OData services.

The first step for using WCF Data Services client library is to create a Service Reference. You can add a Service Reference in Visual Studio by right clicking the project and clicking “Add Service Reference”. In the Address text box, type the following URL which points to the metadata of the Graph service: https://graph.windows.net/contoso.onmicrosoft.com/$metadata ( you can replace contoso with your tenant name). The Service Reference thus generated will have a set of classes that will have 1:1 correspondence with the collections/feeds exposed by the Graph service. If you double click the Service Reference, you can browse through the classes and you will see classes for User, Group, Contact etc. Another class that’s part of the Service Reference will be a class called DirectoryDataService which derives from DataServiceContext class. The DirectoryDataService class will expose collections of types exposed by Graph service like DirectoryObjects, Applications etc. as IQueryable. You can query over these collections using LINQ queries like any other IQueryable collections. The DirectoryDataService class also has the ability to track the changes of objects and exposes a SaveChanges method that can be used to submit the appropriate POST, PATCH or DELETE requests to the Graph service.  

Adding the required HTTP request headers

Each HTTP request to the Graph service needs a couple of headers - Authorization Header and the Version of the Graph service. The easiest way to attach these headers on each request is by hooking up to an event on DirectoryDataService instance as shown in the below code.

Code to hook up the event handler:

varsvcUri=new Uri("https://graph.windows.net/"+ConfigurationManager.AppSettings["TenantDomainName"]);vargraphService=new DirectoryDataService(svcUri);// Register the event handler that adds the headers for HTTP requestsgraphService.SendingRequest+=new EventHandler<SendingRequestEventArgs>(OnSendingRequest);returngraphService;

 Code in the event handler that adds the headers:           

// Add an Authorization header that contains an OAuth WRAP access token to the request.stringauthzHeader=AzureActiveDirectoryGraphAuthorizationHelper.GetAuthorizationToken(ConfigurationManager.AppSettings["TenantDomainName"],ConfigurationManager.AppSettings["AppPrincipalId"],ConfigurationManager.AppSettings["Password"]);e.RequestHeaders.Add("Authorization",authzHeader);
  

Querying for all objects in a collection

Below is a snippet of code that gets all the Users from the Graph service. As you can see, it is as simple as writing these two lines of code to get back a collection from the Service. The reason we use a Helper method to create the instance of the DirectoryDataService is to hook up the event handler that adds the required Headers as we have seen in the previous section.  

DirectoryDataServicegraphService=DirectoryDataServiceHelper.CreateDirectoryDataService();List<User>users=graphService.directoryObjects.OfType<User>().ToList();

 So what really happened?

So what magic happened under the covers to make the things so simple? The Data Services client library produced a HTTP request under the covers based on our LINQ query (graphService.Users), read the response from the Graph service, deserialized the response stream and handed us back the strongly typed objects. Here’s a sample of the HTTP request that is sent out by the WCF client library and response that it gets back from the Graph service. Our application doesn’t need to deal with any of this but it is still good to understand what happens under the covers. Our last blog post has plenty of examples (including this one) for the HTTP request and response formats for various operations on Graph service.

Request URL: 

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users

 HTTP Method: GET

Response:

 

{

 

  "odata.metadata": "https://graph.windows.net/GraphDir1.OnMicrosoft.com/$metadata#directoryObjects/Microsoft.WindowsAzure.ActiveDirectory.User"   

 

  "value":[

 

    {

 

        "objectId": "e428a9cb-7550-4991-afc3-48fe8b60be33",

 

        "accountEnabled": true,

 

        "city": "Seattle",

 

        "displayName": "Adam Barr"

 

    },

 

    {

 

        "objectId": "bafb9fea-d7f3-4cec-af6a-bca2b553e83b",

 

        "accountEnabled": true,

 

        "city": null,

 

        "displayName": "Admin",

 

    }

 

  ]

}

 

Querying Users collection for a Key value

All Collection types exposed by Graph service have the objectId property which acts as a Key property. We can do LINQ query on the objectId property to retrieve a User entry with the given Key value. You can also see the URL that the query gets mapped to below.

LINQ Query:

Useruser=graphService.users.Where(it=>(it.objectId=='e428a9cb-7550-4991-afc3-48fe8b60be33')).SingleOrDefault();

 Request URL:

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users/e428a9cb-7550-4991-afc3-48fe8b60be33'

 

Querying Users collection for a non-Key property value

The query for a non-Key property looks very similar to the one you did for a Key property. Since it is not a Key property, you cannot expect the query to return at most one object. But the URL that the query gets mapped to under the covers is very different. While the query on a Key property gets expressed in the URL itself, all other queries are expressed via Request parameters as defined by OData specification. You can use LINQ queries for different operations supported by the AD Graph service and the WCF Data Services client library will translate them to the correct HTTP request. Currently Graph service supports “Where”, “Top” and “SkipToken” operators. We plan to support more operators like “Select”, “Expand” etc. in the near future. Here we see an example for querying Users collection for a particular displayName.

LINQ Query:

IEnumerable<User>users=graphService.directoryObjects.OfType<User>().Where(user=>user.displayName.Equals(DerekBrown));

 Request URL:

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users?$filter=displayName eq 'Derek Brown'

 

Navigating through Links

Objects exposed by the Graph service are interconnected via various links. For example the following concepts are represented by links in the Graph service, Users belong to different Roles and Groups, Groups have Members which are Users or Contacts, Roles have Members which are Users or Contacts etc. All of these links are exposed as first class properties on the generated types in the Service Reference we created. These properties are referred to as Navigation properties. These navigation properties are not fetched by default though. For example, if you fetch the User, the “memberOf” property on the User will be null. And this is to be expected since if we load all Navigation properties by default, we could end up navigating through the whole Directory. In order to fetch the Navigation property, we would need to call the LoadProperty on the DirectoryDataService. The code below fetches the “memberOf” property on the User and you can also see the URL that this call maps to under the covers.

Code to fetch the Roles that a User belongs to:

graphService.LoadProperty(user,"memberOf");
var currentRoles = user.memberOf.OfType<Role>();

Request URL:

https://graph.windows.net/GraphDir1.OnMicrosoft.com/users/e428a9cb-7550-4991-afc3-48fe8b60be33/memberOf

 

Create a new entry

You can use the SaveChanges method on DirectoryDataService class to persist any change to the Graph service. Let’s first look at an example for creating a new User object. The SaveChanges call takes the content of newly created User and submits it as the body of a HTTP Post request to the Users collection URL.

Useruser=newUser();user.displayName=""Bob";user.userPrincipalName="alias@tenant.onmicrosoft.com";user.mailNickname=""alias";user.password=""xxxxxxxx";user.accountEnabled=true;graphService.AddToDirectoryObjects(user);graphService.SaveChanges();

 

Updating an entry

Once you fetch an object through LINQ query, any changes you do on the object are tracked by the DirectoryDataService instance that was used to fetch the object. In such a case, you can just do SaveChanges and the DirectoryDataService will produce the appropriate HTTP Patch/Merge request for submitting the change. But in case you are building a Web application, you typically would not cache the instance of DirectoryDataService instance between the requests. In such a case, you can make changes to the object and explicitly tell the DirectoryDataService instance that the Object has changed and then call SaveChanges as shown in the code below.

Useruser=graphService.directoryObjects.OfType<User>().Where(it=>(it.objectId==user.objectId)).SingleOrDefault();user.displayName=newDisplayName;user.mailNickname=newAlias;graphService.UpdateObject(user);graphService.SaveChanges(SaveChangesOptions.PatchOnUpdate);

 

Deleting an entry

To delete an entry, you can call DeleteObject method on the DirectoryDataService and then call SaveChanges. The call will translate to a HTTP Delete request submitted to the Graph Service.

Useruser=graphService.directoryObjects.OfType<User>().
            Where(it=>(it.objectId==user.objectId)).SingleOrDefault();
graphService
.DeleteObject(user);graphService.SaveChanges();

 

Adding or Removing Links between existing Entries

You can add or remove a link between two existing entries via AddLink and RemoveLink methods on DirectoryDataService class respectively followed by a call to SaveChanges. The changes will be mapped to a HTTP POST for AddLink and HTTP DELETE request for RemoveLink. Below is a code snippet for adding and removing “Members” link between a User and Role entry. In this case both user and role exists in the Graph already.

 

graphService.AddLink(existingRole1,"members",existingUser1);graphService.DeleteLink(existingRole1,"members",existingUser2);graphService.SaveChanges();

 

Using the code snippets provided in this post, you should able to access and manipulate most of the information in AAD Graph service from your .Net application. If you have any questions or would like to see some particular code snippet, please leave a comment and we will be happy to add it.

PHP sample for accessing Azure AD Graph Service now available

$
0
0

We have just released a PHP sample that shows how to work with the AAD Graph Service. In a previous post we saw the format of HTTP requests and JSON responses for several interesting Graph operations. The PHP sample just shows how to put together these requests and parse the JSON responses using PHP.

Here are some things to know about this sample:

  • You can use WebMatrix to work with this PHP sample. WebMatrix is a free and light weight tool that will let you create web sites using various technologies like PHP, Asp.Net, Node.js etc. In order to run this sample in Web matrix, choose  "Open Site" -> "Folder as Site" and select the folder that contains all the downloaded files.
  • The sample comes with a demo tenant that you can use for Read operations. If you want to see Write operations like Edit User, Delete User etc. in action, you can create a sample tenant and an associated Service principal. The first section in the "walk through for building a .Net application" talks about how to create a test tenant and create an associated service principal. You can use the powershell script( CreateServicePrincipal.ps1) to create the service principal.
  • Replace the information in Settings.php file with the information you got from the Powershell script( AppId, Password and Tenant name) when you created the service principal.
  • The code in AuthorizationHelperForAADGraphService puts together the request for getting the Access token from Azure AD for accessing Graph Service for the tenant using the AppId, password and the tenant domain name.
  • All the Graph access related code is in GraphServiceAccessHelper.php and the code is pretty straight forward. It puts together HTTP GET, POST, PATCH and DELETE operations and depending on the request it parses the JSON response from the service.

 

Let us know if you have any questions about the sample code or if you have any particular requests for the sample.

Building a Multi-tenant Application with Windows Azure Active Directory (Single Sign-on, Graph access and Role based Authorization)

$
0
0

We have just updated the Multi-tenant sample application for Windows Azure AD. The sample application shows how to add the following capabilities using Windows Azure AD:

  • Enable customers from different tenants to sign up for your application.
  • Single Sign-on with Windows Azure AD.
  • Adding Claims based on information in AD fetched via Azure AD Graph API and using the Claims for Authorization.
  • Access and manage Windows Azure AD data through Graph API.

Creating a Test tenant

In order to integrate your application with Windows Azure AD, you have to register your application in a tenant that you own. If you need a test tenant, you can create it here. You will be asked to choose an organization name, a domain name and also create a user entering user name and password. The user created here will be a global administrator for your directory. 

Create an account with the Microsoft Seller dash board

To develop a multi-tenant application, you must sign up for a Microsoft Seller Dashboard account. New accounts are put into Pending state but that does not stop you from building your application and testing it. It only stops it from being visible to other customers in Azure Market place.

Get a Client ID for the Application

You need to get a ClientID for your application. You can think of this as the identifier for your application in Azure AD. You can follow the instructions here to create the ClientID. During development, you can use the following values for generating the ClientID:

App Domain: You can use localhost during development and testing. Later you need to update it to the host name of your application like contoso.com

App Redirect URL: The redirect URL is the one Azure AD will redirect the user to after they login through your application.        

Before trying to run the sample application, you need to provide the AppHostname, ClientID and SymmetricKey provided by the Seller Dash board in the web.config file.

 Enable users from different tenants to sign up for the application:

You can enable different tenants to sign up for your application by redirecting them to the Consent page for applications in Windows Azure AD. You need to provide the ClientID of your application, permissions that your application needs( Read access or Read Write Access) for the directory data of the customer consenting your application. You can also provide the redirect URL where the Consent page will redirect to. We also need to remember the list of tenants that have consented so that we don’t send the users for those tenants to the Consent page on a subsequent login and also to make sure that we only accept tokens from users belonging to tenants that have provided consent to the application. The sample application stores this information in an Xml file but for a production application you should try to use a Database.

Here’s how the Consent URL looks in the sample application:

https://activedirectory.windowsazure.com/Consent/AuthorizeApplication.aspx?ApplicationId=@System.Configuration.ConfigurationManager.AppSettings["ClientId"]&RequestedPermissions=DirectoryWriter&ConsentReturnUrl=@HttpUtility.UrlEncode(System.Text.Encoding.UTF8.GetBytes("https://localhost:44309/Account/HandleSignupConsentResponse"))

Here’s the code for HandleSignupConsentResponse in Accounts Controller which handles the Consent response. On denying consent, the sample just redirects the application to login page again which in turn redirects to Signup again. You might want to change this to show a more appropriate message rather than redirecting it to sign on when the consent is not Granted.

 

publicActionResultHandleSignupConsentResponse(stringtenantId,stringconsent){if(consent=="Granted"){TrustedIssuers.Add(tenantId);}returnRedirectToAction("LogOn","Account");}

 

Single Sign-On with Windows Azure AD:

The sample application uses WIF( Windows Identity Foundation) to achieve single sign-on which is in turn based on WS-Federation. You can read this MSDN article to get a comprehensive understanding of how Single Sign-on works through WIF. WIF will add all the required entries in the Web.config entries to enable the application for single sign-on with Windows Azure AD. But since our application is a multi-tenant application, we need to do something extra. The federation end point for Single sign on needs to include the TenantId that the user belongs to which we will only know during the runtime. Another thing we need to do is to convert the domain name of the tenant that the user provides us into a tenant id that will be used to create the URL for federation end point. We also need to verify the token that has been issued is from the list of tenants that have consented to our application.

Here is the code for LogOn Action from the sample application. It redirects to the Consent page if the tenant admin has not consented the app. It takes the user to the login page by generating the appropriate Signon Url based on the tenant id if the consent has already been granted for the tenant:

 

 publicActionResultLogOn(LogOnModelmodel){ModelValidationHelper.ValidateStringProperty(ModelState,model.domainName,"Domain Name","Domain Name is required.");if(ModelState.IsValid){stringtenantId=AuthenticationHelperMethods.DomainToTenantId(model.domainName);if(!TrustedIssuers.Contains(tenantId)){returnRedirectToAction("Signup","Account");}else{stringsignInMessage=AuthenticationHelperMethods.GenerateSignInMessage(model.domainName,HttpContext.Request.RawUrl);returnRedirect(signInMessage);}}else{returnView();}}

 

The Login page in the sample application collects the domain name of the tenant from the user. But you need to specify the TenantId for the federated login URL with Azure AD. The DomainToTenantId method calls the Azure AD federation metadata URL(https://accounts.accesscontrol.windows.net/<tenant domain name>/FederationMetadata/2007-06/FederationMetadata.xml) that provides the mapping from tenant id to domain name:

 

publicstaticstringDomainToTenantId(stringdomainName){stringrequestString=string.Format(AuthenticationConstants.FederationMetadataURL,domainName);WebRequestrequest=HttpWebRequest.Create(requestString);using(WebResponseresponse=request.GetResponse()){//Get the Response Stream from the URL using(StreamresponseStream=response.GetResponseStream()){XDocumentdoc=XDocument.Load(responseStream);using(XmlReaderreader=doc.CreateReader()){//read through all the nodeswhile(reader.Read()){if(reader.Name=="EntityDescriptor"){// Example entityId: https://sts.windows.net/f2775f3b-a5b2-4046-869b-814d26c51756/stringentityId=reader.GetAttribute("entityID");stringtenantId;if(!TryExtractTenantIdFromEntityId(entityId,outtenantId)){thrownewInvalidOperationException(string.Format(CultureInfo.InvariantCulture,"Unexpected entityId attribute value: {0}.",entityId));}returntenantId;}}returnnull;}}}}

 

Once you have the tenant id, you can generate the Sign in URL by putting together the tenant id and your client id. This call will only succeed if the tenant had signed up for your application before this point:

publicstaticstringGenerateSignInMessage(stringdomainName,stringrawUrl){FederatedAuthentication.WSFederationAuthenticationModule.Issuer=String.Format(AuthenticationConstants.FederationURL,domainName);FederatedAuthentication.WSFederationAuthenticationModule.Realm="spn:"+ConfigurationManager.AppSettings["ClientId"];SignInRequestMessagesirm=FederatedAuthentication.WSFederationAuthenticationModule.CreateSignInRequest("","",true);returnsirm.RequestUrl;}

Once signed in the redirect request to our application will have the token for the user. We need to make sure that the thumbprint for the token matches the one from Windows Azure AD( thumbprint will the same for all the tenants) and also make sure that the tenant issuing the token is from the list of tenants that have consented our application. This can be done by adding a class which derives from ValidatingIssuerNameRegistry and registering it in Web.config.

Web.config Entry:

  <system.identityModel>

    <identityConfiguration>

      <!--other stuff her-->

      <issuerNameRegistry type="GraphMultitenantMVCSample.Helpers.CustomValidatingIssuerNameRegistry, GraphMultitenantMVCSample"/>

    </identityConfiguration>

  </system.identityModel>

 

publicclassCustomValidatingIssuerNameRegistry:ValidatingIssuerNameRegistry{protectedoverrideboolIsThumbprintValid(stringthumbprint,stringissuer){stringtenant;if(!AuthenticationHelperMethods.TryExtractTenantIdFromEntityId(issuer,outtenant)){returnfalse;}if(!TrustedIssuers.Contains(tenant)||(thumbprint!=ConfigurationManager.AppSettings["acsThumbprint"])){returnfalse;}returntrue;}}

 

Using information from Graph to add Claims

We have now enabled the application for single sign-on with Azure AD. But we can do so much more. You can use the role and group membership information in Azure AD to add Claims to the incoming Principal. Once you do that, you can use the Authorize attribute to define which Roles can access to which Controller Actions. The following function gets the Role and Membership information for the currently logged in user and adds Claims based on the information. The query below which fetches the Groups and Roles that the user is MemberOf is not transitive and also adds all the information for Role and Group membership to the Claims. This is a very naïve implementation and the scenarios will dictate how you will customize it and it will be different for different scenarios.

 

Web.config Entry:

 

  <system.identityModel>

    <identityConfiguration>

      <!--other stuff here--> 

   <claimsAuthenticationManagertype="GraphMultitenantMVCSample.GraphClaimsAuthenticationManager, GraphMultitenantMVCSample" />

</identityConfiguration>

  </system.identityModel>

 
publicclassGraphClaimsAuthenticationManager:ClaimsAuthenticationManager{publicoverrideClaimsPrincipalAuthenticate(stringresourceName,ClaimsPrincipalincomingPrincipal){if(incomingPrincipal!=null&&incomingPrincipal.Identity.IsAuthenticated==true){DirectoryDataServiceds=DirectoryDataServiceHelper.CreateDirectoryDataService(AuthenticationHelperMethods.GetTenantId(incomingPrincipal));stringupn=AuthenticationHelperMethods.GetUPN(incomingPrincipal);Useruser=ds.Users.Where(it=>(it.UserPrincipalName==upn)).SingleOrDefault();ds.LoadProperty(user,"MemberOf");foreach(vardirectoryObjectinuser.MemberOf){varreferencedObject=directoryObjectasReferencedObject;((ClaimsIdentity)incomingPrincipal.Identity).AddClaim(newClaim(ClaimTypes.Role,referencedObject.DisplayName,ClaimValueTypes.String,"GraphSample"));}}returnincomingPrincipal;}}

Once you have added the Claims based on the information in Azure AD, you can use it to Authorize the users pretty easily using attributes. The following 3 controllers in sample application are available to users in Global Administrator role, users belonging to Super User group and all users.

[Authorize(Roles="Company Administrator")]

    public class UserController : Controller

    {

        ………….

    }

 

[Authorize(Roles="Super User")]

    public class SearchForAnyUserController : Controller

    {

         ………….

    }

 

    [Authorize]

    public class IndividualDetailsController : Controller

    {

         ………….

    }

 

Working with Graph

There is no real difference in working with Directory data through the Graph API whether it be single tenant or multi-tenant application. Our previous walk through explains all the required steps for creating a .Net application to consume the AAD Graph service and the multi-tenant sample application also uses identical code to access the service.

The multi-tenant sample application code should be easy to follow and this blog post hopefully answered any questions you might have had about the application. If you have any furtuer questions or feedback about the application, please post a comment and we will follow up.

0.9 Version of Azure Active Directory Graph now available!

$
0
0

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.

Windows Azure Active Directory Graph Service – Ready for Production

$
0
0

It’s a big day for us. The AAD Graph service is now Generally Available along with other Azure Active Directory Services. I am pretty sure all of you can relate to the feeling we have today – a big push with some crazy last minute issues culminating with what we feel like is a very nice release for our customers. We hope you guys love our services and continue to provide us feedback. The GA is just a milestone in a long journey. We will continue to update our service at a pretty quick cadence and we will keep updating you on our progress through this blog.

As you know, we have released a number of previews for Graph service over the past few months and a number of you have already used the APIs and provided us some great feedback. Please continue to provide the feedback and pain points and we will try to resolve them as soon as possible.

Here’s a quick overview of the changes coming with the GA release for Graph and the associated components in Azure AD. If you are a new customer and want to gain a deeper understanding of the AAD Graph Service, I suggest you to look at our previous posts. The changes for Graph service from the previous version( 0.9 version) to this version are minimal. The changes from 0.8 version to 0.9 version of Graph service were substantial and are covered in this post.

  • Version format change: We have adopted the Date version format that a number of other Azure services use. So the Version for our GA release is “2013-04-05”. Here is how a request looks like with the new version format: https://graph.windows.net/GraphDir1.OnMicrosoft.com/users?api-version=2013-04-05 .
  • New Application Registration UX: If you have previously built an application using Graph API, we required you to register the application with Windows Azure AD using Powershell for LOB application or Seller Dashboard for Multi-tenant application. For the GA release, we have significantly simplified the Application registration model and you can manage your applications and other Directory objects liker Users in the Azure management portal. If you don’t have a subscription for Windows Azure, you can start you with a Trial subscription here. “Configuring Application Authentication and Authorization for Graph API” section in the “Using the Graph API to Query Windows Azure AD” MSDN article describes the steps to configure applications for integration with Windows Azure AD for both single sign on and using Graph API. The great thing about the new Application Registration UX is that it provides a way to configure applications for both LOB and Multi-tenant applications and there is no need to use Powershell for this anymore
  • Differential Query: We have not talked about this feature a lot in this blog but this is a cool feature that lets you get the changes that happened in the Azure AD tenant through the Graph API from the time you have fetched the changes previously. We will have some detailed posts on this topic in the next couple of days.
  • JSON-Minimal metadata as the default: We support 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 this version of the service, we have made JSON - minimal metadata as the default format. The JSON content is much cleaner than the default we supported previously. 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.
  • OAuth2 End Point for retrieving token to talk to Graph API: The address for the end point to fetch the token to talk to the Graph API has changed. The new end point is https://login.Windows.net/<tenantname>/oauth2/token . The format of the request to the OAuth2 end point has also changed a little. The new helper library shipped with the updated MVC sample to work with GA version has the code to produce the request in a format that the new endpoint expects.

We have also updated our MVC Read Write, PHP and Java samples to work with the GA version of the Graph service. We also have some great documentation, walkthroughs etc. on our MSDN website to help you get started with Azure Active Directory and AAD Graph Service. Let us know if you run into any problems or have any feedback.


Differential Query in Windows Azure Active Directory Graph

$
0
0

In this post we will talk about Differential Query in Graph.

What is Differential Query

Differential Query allows Apps to query for changes that occurred on a given tenant in AAD.

This functionality is especially useful when an App needs to track changes that occurred on tenant in AAD in order to perform App specific operations. It also allows Apps which implement it own data store to effectively integrate with AAD by synchronizing changes from AAD to App's data store.

How to start using Differential Query

As a starting point, App needs to learn about all changes occurred on a given tenant since the tenant was created in AAD. Here is how an App can perform initial Differential Query request:

  GET https://graph.windows.net/contoso.com/directoryObjects?api-version=2013-04-05&deltaLink=

deltaLink parameter indicates this request to be a Differential Query. And deltaLink parameter with no assigned value indicates that this is an initial Differential Query request.

Differential Query paged responses contain all changes that occurred on the tenant until last page of changes is returned:

{
  "odata.metadata": "https://graph.windows.net/contoso.com/$metadata#directoryObjects",

  # This is the nextLink to be used for the next query
  "aad.nextLink": "https://graph.windows.net/contoso.com/directoryObjects?deltaLink=XARBN7ivjcS6QIhSZDQR3OkT15SO1eeY-01BZSS0sOct6sh5oEyqOLLKRVhRmBMLHhePUF... [Truncated]",
  "value": [

    # User object for John Smith
    {
      "odata.type": "Microsoft.WindowsAzure.ActiveDirectory.User",
      "objectType": "User",
      "objectId": "dca803ab-bf26-4753-bf20-e1c56a9c34e2",
      "accountEnabled": true,
      "displayName": "John Smith",
      "givenName": "John",
      "mailNickname": "johnsmith",
      "passwordPolicies": "None",
      "surname": "Smith",
      "usageLocation": "US",
      "userPrincipalName": "johnsmith@contoso.com"
    }
  ]
}

aad.nextLink property indicates that there are more changes on the tenant and that the App should perform subsequent Differential Query request right away and pass aad.nextLink property value from the response as deltaLink parameter value (note that api-version needs to be appended as a request parameter):

  GET https://graph.windows.net/contoso.com/directoryObjects?api-version=2013-04-05&deltaLink=XARBN7ivjcS6QIhSZDQR3OkT15SO1eeY-01BZSS0sOct6sh5oEyqOLLKRVhRmBMLHhePUF... [Truncated] 

If a response contains aad.deltaLink property, it indicates that no more changes exist on the AAD tenant in AAD.

{
  "odata.metadata": "https://graph.windows.net/contoso.com/$metadata#directoryObjects",

  # This is the deltaLink to be used for the next query
  "aad.deltaLink": "https://graph.windows.net/contoso.com/directoryObjects?deltaLink=2O3f9hvaK62A-wIr-oj5sAUMnVNv21k-kIPYZymYDryWzrd0sQrB72EX6TpzPBep2n8v... [Truncated]",
  "value": [

    # Group object for IT Administrators
    {
      "odata.type": "Microsoft.WindowsAzure.ActiveDirectory.Group",
      "objectType": "Group",
      "objectId": "7373b0af-d462-406e-ad26-f2bc96d823d8",
      "description": "IT Administrators",
      "displayName": "Administrators",
      "mailNickname": "Administrators",
      "mailEnabled": false,
      "securityEnabled": true
    }
  ]
}

Subsequent Differential Query response will contain no changed entities until more changes are made on the tenant. At this point, App should store aad.deltaLink value locally and use it in subsequent Differential Query request. This allows the App to come back after a certain interval and issue incremental Differential Query request. Incremental Differential Query request can be repeated for as long as the App is interested to learn about changes on the tenant (and is authorized to provide services on this tenant).

Differential Query response semantics

There are some key differences in the response structure that set Differential Query apart from regular Graph responses we covered in previous posts:

  • response includes deleted objects and deleted links (indicated by "aad.isDeleted" property with value set to true); this is necessary to make sure App can learn about deletes of previously created objects and links
  • response contains entities with only changed properties; e.g. for a new user created since the last time Differential Query request was made, response only includes new user entity with only properties that were set
  • links represented as entities (DirectoryLinkChange entity type); this allows response to contain only changed links. This allows changes on links to be expressed most efficiently so that, for example, if group with large number of member links was changed to add new member, only this new link could be included in Differential Query response.

Differential Query invariants

App developers should consider the following when implementing Differential Query:

  • changes returned by Differential Query represent the state of AAD objects at the time of response
  • changed objects appear approximately in the order in which they occurred in AAD with most-recently changed objects appearing last. This makes it possible for changes to be presented out of order compared to how they initially occurred in AAD
  • App should be prepared to handle a deletion change for an object it was not aware of
  • Differential Query can return a link to a source or target object that has not yet been previously returned
  • App should be prepared for receiving same change in a subsequent response (this is known as change replays); while we make a best effort to reduce replays, they are still possible.

 

This is it for today. In the next post on Differential Query we will cover a deep dive on Differential Query request/response structure and how App Developers can write simple code to process Differential Query responses.

To learn more about Differential Query, please visit http://msdn.microsoft.com/en-us/library/windowsazure/jj836245.aspx

As always, we are interested to hear your feedback. You can try Differential Query now by visiting https://graphexplorer.cloudapp.net/. Click "Use Demo company"

 

Announcing some new capabilities in Azure Active Directory Graph Service

$
0
0

We have rolled out an update to the Graph service a couple of days back. Here is a brief overview of changes included in this update. We will have individual posts on these topics in the future.

  • Using OAuth 2.0 Authorization Code Grant for delegated access of Azure Active Directory (AAD) via AAD Graph: You might have seen the recent announcement of developer preview for support of OAuth Code Grant in Azure Active Directory. You can use this to provide delegated access when accessing resources. We can use the same mechanism to access AAD resources using delegated access via Graph API. We will have a detailed post and a sample web application showing how to use this with Graph shortly.
  • Support for “MyOrganization” alias: Instead of providing the tenant name in the URL, you can now use “MyOrganization” as an alias for your tenant in the URL. The tenant name is derived by Graph based on the claims in the token. The alias is case insensitive.
  • Support for “Me” alias: If you used delegated access of directory resources mentioned in the first point above, the token presented to graph will include user information in the claims. To refer to the user object, you can use the “Me” alias. The alias is case insensitive.
  • Checking transitive group membership for multiple groups: Graph service already supported IsMemberOf function to check transitively whether a user is a member of a particular Group (will check for transitive group membership and not just direct membership in the group). But in authorization scenarios, it will be a common requirement to check for transitive group membership for multiple groups. Right now the easiest way to do it through multiple calls to isMemberOf function but it adds multiple round trips and definitely not something that is recommended. We have added a new function “checkMemberGroups” to enable this scenario. checkMemberGroups takes a collection of object IDs of groups and can be called on a user, group or service principal object that the membership check needs to happen on. The function checks whether the object belongs to each of the groups and returns the collection of object IDs of the groups that the user object belongs to.  The number of input groups is limited to 20. 
    • HTTP Method: POST
    • Request Body:

      {

          "groupIds":["fb11a9aa-d501-465e-bd19-48a511883862","24541d7f-c4c5-473e-9b78-f4fe8f0d5194"]

      }

       

      Response:

      {

          ["fb11a9aa-d501-465e-bd19-48a511883862"]

      } 

  •  Getting all groups that a user is member of: Sometimes you might want to get the information about all the group memberships of a user (or a group or a service principal) and cache it for the session (do not cache this information beyond the session). We have added a function called “getMemberGroups” for this scenario. getMemberGroups can be called on a user/ group/service principal object and returns all the object ids of the groups that the user/group/service principal is member of( either directly or indirectly). The functions takes one Boolean parameter: securityEnabledOnly.  If securityEnabledOnly is true, only security enabled groups are returned and the function can only be called on user object. The maximum number of groups that you can get back from the query is capped at 1000 when securityEnabledOnly is true and 4500 when it is specified as false.
    •  HTTP Method: POST
    • Request Body:

      {"securityEnabledOnly":false} 

    • Response:

      {

          ["fb11a9aa-d501-465e-bd19-48a511883862"]

      }

       

 As always questions and feedback are welcome.

Using OAuth 2.0 Authorization Code Grant for delegated access of Directory via AAD Graph

$
0
0

You might have seen the recent announcement of developer preview for support of OAuth Code Grant in Azure Active Directory. You can use this to provide delegated access when accessing resources. We can use the same mechanism to access AAD resources using delegated access via Graph API. We have just released a PHP sample that shows how to use this. The blog post provides details on what’s required in building such an application.

 

Using Authorization Code v/s Client Credentials

In our previous posts on this blog and our samples web site, we have shown various kinds of Applications you can build that consume information from AAD Graph. But the one thing that was common to all the applications was that they used Application credentials exclusively when talking to AAD Graph and did not use User identity. Since the user identity is not used in the Client Credentials flow, the application has to take the responsibility for making sure that the users are authenticated and are given the appropriate level of access when accessing resources in AAD. Users get different permissions in AAD depending on the role they belong to. For example, all Users get permissions to read their own information. But if a User belongs to “Company Administrator” role, he has administrator permissions and can read and write objects of other users as well. So how can application developers just depend on the Access Checks put into place in AAD Graph rather than enforcing them in their application when accessing AAD data? This is where OAuth 2.0 Authorization code grant will help us build an application using delegated access to the directory resources.

 

Overview of the PHP Sample

The functionality provided by the PHP sample is very limited. The goal is to show how Authorization Code can be used to access AAD Graph and so the application does not do much beyond that. The first thing the application asks the customer to do is to authorize via the AAD portal. Typically the application would authenticate the user via WS-Fed or SAML protocol but to make things simple, the sample application directly takes user to authorization. Once authorized, user is redirected to a page showing his/her details. There is an Edit link at the bottom of this page. You can only use this link if you have are using Administrator credentials while authorizing the application. And this access check is not done by the application but by the Graph API. Here is a brief description of the files that are part of this sample.

  • Authorize.php: It’s the home page of the application and redirects the user to the AAD portal for retrieving authorization code.
  • HandleAuthorizationResponse.php: The page handles the redirect from the AAD authorize page after the user authorizes.
  • AuthorizationHelperForGraph.php: Takes care of putting together the URL for retrieving the authorization code and also getting the access token for accessing Graph using the authorization code fetched.
  • GraphServiceAccessHelper.php: Takes care of putting together the HTTP requests to the Graph service and handling the JSON response.
  • Settings.php: This file contains the constant values for clientId, Password, RedirectURI etc. that need to be updated to match the service principal you create (explained in the next section).

 

Setting up permissions

 Every application that uses AAD Graph API to access Directory information needs to be registered before it can access information via Graph API. As we have seen in a previous blog post, Azure portal now provides the functionality to register applications and set up the appropriate permissions for accessing AAD resources via Graph. The Azure portal does not yet support registering applications and granting consent for applications that use the OAuth2 code grant which is in preview. In the meanwhile, you can use Graph Explorer for this purpose.

Steps to setting up permissions for application:

  • Go to https://graphexplorer.cloudapp.net/
  • Click the “Sign In” link on the right top hand corner.
  • Sign in using admin credentials for your AAD tenant.
  • Click the “Add Application Permission” link on the top.
  • You can choose an existing client application or create a new client application. Let’s create a new client application.
    • Choose any “Client APP Display Name” of your choice.
    • Choose a “Client APP URL” that’s unique with in your tenant and maps to the address of your application. You can choose something like https://localhost:44305 while you are developing the application and replace 44305 with the port that your application is using.
  • For the Service, select the “Microsoft.Azure.ActiveDirectory” from the dropdown. This service will exist in every tenant and represents nothing but AAD itself. We are trying to building an application that accesses AAD and thus AAD is the resource for us.
  • For Scope, enter the name of any well-known role name in Azure Active directory. There are 3 roles in particular that are interesting when talking about application permissions:
    • Directory Readers: Provides Read permission.
    • Directory Writers: Provides Read+Write access but no Delete permissions.
    • Company Administrator: Full admin permissions including deletes.
  • Uncheck the “Public Client” check box since we are creating a web application. If we were building a native application like a windows store application, we would choose “Public Client”.
  • Enter a password.( only needed for Private client)
  • Click “Create Permission” which will display the “Client Id” and “Credentials” which you need to copy and paste in the Settings.PHP file of the PHP sample.

 This is how the form looks before clicking “Create Permission”.

 

 

 

Once you click the “Create permission” button, Graph explorer will create the permission and show you the Client Id, App URL, Credentials Key for the Client application. Copy these values to settings.php file in the sample. App Url is called redirectURI in the application and Credentials Key is called password.

 Getting an Authorization code

Now that the permission object has been set up, we can proceed to access Graph from our Application. The first thing we need to do is access the Authorization end point and get the authorization code. The end point for Authorization is: https://login.windows.net/common/oauth2/authorize . We need to specify the Application Id for the client application, application URI for the resource (in our case Graph) and the redirect URI we had specified while creating the Service Principal. The user will be redirected to the Azure AD login page and the user will be redirected back to the Redirect URI we specified in the request parameter after a successful login. The redirect URL will have the Authorization code. The sample response shown below is the redirect request to the application after a successful login. The client_id and redirect_uri in the request below comes from the above step when you created permission for this application.

Request URL:

https://login.windows.net/common/oauth2/authorize

 

Request Parameters:

response_type code

client_id               599cea60-a2ba-4f9b-8f06-9f92986560f1

resource              https://graph.windows.net

redirect_uri        https://localhost:44328/HandleAuthorizeResponse.php

 

 

HTTP Method: GET

Response:

https://localhost:44305/HandleAuthorizeResponse.php?code=AAAAAAAA...............P5qd1L7x2BAP6bxxyD9KMgAA

 

Getting an Access token with Auth Code

We can now use the Authorization code in conjunction with the client application Id, client Application Secret etc. to fetch the Access token. The Access token can be used to access the data of the tenant and the permissions that the App gets will be based on the role that the User belongs to and the Role that was used in Scope parameter when creating the Permission object for this application. The client_id and client_secret are the values from the Graph explorer when we created permission for this application.

Request URL:

https://login.windows.net/common/oauth2/token

 

Request Parameters:

grant_type

authorization_code

client_id

599cfa60-a2ba-4f9b-8f06-9f92986560f1

redirect_uri

https://localhost:44328/HandleAuthorizeResponse.php

client_secret

DhjYU…kqqyHiqNm/PY=

Code

AAAAAAA…zS

 

HTTP Method: GET

Response:

 {

    "access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2CzkwXeiVQ",

    token_type":"Bearer",

    "expires_in":"28799",

    "expires_on":"1368676551",

    "refresh_token":"AAAAAAAADENAfM5UpyznQ8EHSHp0GI8GTGXonaBFZHZADR1Kf...-Q9SAA",

    "scope":"62e90394-69f5-4237-9190-012177145e10"

}

 

You can use access token in the response to talk to Graph by providing in the Authorization header(Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhb…….. VQ). The sample application does not login the user and stores a profile locally but a lot of real world application may want to do that in which case you can go back and fetch the access token without user interaction by using the refresh_token value in the response. The next section explains how to get an access token using a refresh token.

Getting an Access token with Refresh token

 

Request URL:

https://login.windows.net/common/oauth2/token

 

Request Parameters:

grant_type

refresh_token

client_id

599cea60-a2ba-4f9b-8f06-9f92986560f1

 

 

client_secret

DhjYUdb4PHA…..PY=

refresh_token

AAAAAAAAD…..A

 

HTTP Method: GET

Response:

 {

    "access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2CzkwXeiVQ",

    token_type":"Bearer",

    "expires_in":"28799",

    "expires_on":"1368676551",

    "refresh_token":"AAAAAAAADENAfM5UpyznQ8EHSHp0GI8GTGXonaBFZHZADR1Kf...-Q9SAA",

    "scope":"62e90394-69f5-4237-9190-012177145e10"

}

 

Accessing information of the user from AAD using Graph

So the user has authorized the application and we have the access token to access Graph. But how do we get the information about the currently authorized user. It’s really simple with the new functionality that just got added in the Graph service. You can refer to the current user using the “me” alias. So the following URL can be used to get all the groups and roles that the user is memberOf: https://graph.windows.net/me/isMemberOf?api-version=2013-04-05 . Graph will figure out the user information from the claims in the token. The PHP application just displays the information regarding the user and thus does the following query to the graph: https://graph.windows.net/me?api-version=2013-04-05 and provides the token retrieved previously from token end point in Authorization Header. Apart from the “me” alias, “myorganization” alias was also added recently to Graph and refers to the tenant associated with the currently authenticated user or application trying to access graph. So you can use the following query to get all users for the tenant: https://graph.windows.net/myorganization/users?api-version=2013-04-05 .

 

As always, feedback and questions are welcome.

Quick Start Guide to the Windows Azure Graph Store – An Insanely Simple Way to Store Relationships Between Things

$
0
0

Using the Windows Azure Graph Store

The Windows Azure Graph Store or WAGS is a storage service, provided by Microsoft and included as part of an Azure Active Directory (AAD) tenant which is part of Office 365 as well as Azure itself. It features a simple REST interface for retrieving and updating members of a graph (graph tuples), a simple serialization format, a simple provisioning model (like none!) and a simple security model (sure, we had to secure the data…).

The scenarios for using WAGS are numerous, including extending entities in AAD, storing configuration and general application storage in a managed service. We will make more blog posts in the near future that focus on these scenarios.

This post will describe how to use the operations of the WAGS service. Please note that at this time WAGS is offered as a preview service – we work to ensure that the service is highly available, but we make no undertaking on availability or features and no SLA will be enforced.

While we definitely believe in the simplicity of REST and JSON for creating great services interfaces, we know that .NET developers love a framework library to support their interaction with services. The OData client libraries from the .NET Framework are compatible with WAGS and so can be integrated into a client application in the same way as any other OData compliant data service. One difference, however, is that because WAGS does not impose a strong schema for the data (technically, the WAGS entity sets are defined as Open Entity Types - http://www.odata.org/documentation/odata-v3-documentation/common-schema-definition-language-csdl/#614_The_edmOpenType_Attribute) the Visual Studio tooling for generating service references will only generate proxy classes that include the mandatory key attributes. Consequently, proxy classes should be manually written to support a flexible schema – there are multiple approaches to designing proxy classes for open types which I’ll discuss in a future post.

Provisioning a New Graph

WAGS is a multi-tenanted, multi-graph store. Stores like this typically involve some provisioning steps to allocate space and setup authentication and authorization rules. In the case of WAGS, this has already been provided and so a new graph is created simply by writing your first tuple.

The security model for a new graph may subsequently be modified, but the default model that is inherited at creation time will enable the creation, reading, updating and deletion of tuples in a graph without any configuration.

Adding Tuples to a Graph

Use the HTTP POST method to add a new tuple to either an existing or an entirely new graph via the following URL:

http://graphstore.windows.net/{tenant}/{graph}

The body of the request should be a JSON object as follows:

{
"_Item1" : "first item",
"_Item2" : "second item",
"AnyAttributeIWant" : AnyPrimativeValue,
"AnotherComplexAttribute" : {
"AttributeName" : "value",
"AnotherAttribute" : "another value"
},
"ACollectionOfAttributes" : [
{
"Attribute1" : 100,
"Attribute2" : true
},
{
"Attribute1" : 200,
"Attribute2" : false
}
]
}

Other than the _Item1 and _Item2 attributes, all other payload is completely arbitrary at the discretion of the application writer. This can include primitive values (AnyAttributeIWant), complex values (AnotherComplexAttribute) or collections (ACollectionOfAttributes).

The response for a successful operation is 201 – Created. The body of the response is the fully formed entity, JSON encoded.

Updating a Graph Tuple

An existing tuple in a graph can be updated using the HTTP MERGE or PUT methods at the following URL:

http://graphstore.windows.net/{tenant}/{graph}/{item1}/{item2}

The body of the request should be a JSON-encoded object, the same as the POST operation. The _Item1 and _Item2 attributes must match the values in the URL. The difference between a MERGE and a PUT operation is that MERGE will combine the attributes in the payload with those already in the store, PUT completely replaces the tuple in the store with that passed in the request.

The response for a successful operation is 204 - No Content. There is no body included in the response. If a tuple does not already exist as specified by the URL, then 404 – Not Found will be returned.

Deleting a Graph Tuple

To delete a tuple from a graph, use the HTTP DELETE method at the following URL:

http://graphstore.windows.net/{tenant}/{graph}/{item1}/{item2}

No body is required for a DELETE request.

The response for a successful operation is 204 - No Content. There is no body included in the response. If a tuple does not already exist as specified by the URL, then 404 – Not Found will be returned.

Querying for Graph Tuples

Tuples in a graph can be queried in three different ways, all using the HTTP GET method:

    1. All the tuples in a graph

         http://graphstore.windows.net/{tenant}/{graph}

    2. All the tuples that have one part of the item key

         http://graphstore.windows.net/{tenant}/{graph}/{item}

    3. A single tuple identified by both item keys

         http://graphstore.windows.net/{tenant}/{graph}/{item1}/{item2}

The first two methods return a JSON array of tuples, which may contain zero entries if the query does not match any tuples in the store:

{
"value" : [
{
"_Item1" : "http://graph.windows.net/contoso.com/Users/joe@contoso.com",
"_Item2" : "http://www.bing.com",
"Like" : true,
"Comment" : "I really like Bing’s search results and FB integration"
},
{
"_Item1" : "http://graph.windows.net/contoso.com/Users/mary@contoso.com",
"_Item2" : "http://www.nytimes.com",
"Like" : true,
"Comment" : "I like news from the Big Apple"
}
]
}
 

The third method returns a single tuple that matches the item key parts in the URL. The tuple is not wrapped in an array. This method returns 404 – Resource Not Found is the specified tuple does not exist.

Note that tuples in WAGS are stored with an implied ‘direction’ ie. From _Item1 To _Item2. However, when querying for tuples the service performs an undirected query such that tuples are returned if they match the query value regardless of which direction the key value is stored. While this feature is generally handy and relieves the application writer from worrying about the direction of their tuples, sometimes it is important to perform a directed query. WAGS supports directed queries by using wildcard placeholders in the query URL to specify which direction is to be queried.

To perform a directed query for all tuples where only _Item1 == value, send a URL of this form:

http://graphstore.windows.net/{tenant}/{graph}/keyvalue/*

Conversely, to query tuples where only _Item2 == value, use this URL format:

http://graphstore.windows.net/{tenant}/{graph}/*/keyvalue

Note that at this time there is no way to issue a query for a directed single exact tuple.

Securing Your Graphs

Nobody’s going to store anything of value in a multi-tenanted public cloud store if you can’t secure it. WAGS supports a security model that may be applied to an individual graph or to all graphs under a given tenant.

The URL for the permissions document for a graph is:

http://graphstore.windows.net/{tenant}/{graph}/$permissions

The URL for the tenant permissions document is:

http://graphstore.windows.net/{tenant}/$permissions

The permissions document in the response is the same for both requests:

{
"GraphName" : "graph",
"AnonymousRead" : true,
"AnonymousWrite" : false,
"AllowNonTlsChannel" : true,
"ReadClaim" : null,
"WriteClaim" : null
}

The permissions document specifies the name of claims that must be present in a bearer token obtained from Windows Azure Access Control Service (ACS). For information about obtaining access tokens from ACS, see the following link; http://go.microsoft.com/fwlink/p/?LinkID=228494.

The REST calls to WAGS may be authenticated by sending the ACS token in the ‘Authorization’ request header or as the ‘Authorization’ query parameter. The audience/realm of the access token must match the URL of the graph being queried. If the audience of the token matches the queried graph, then the claim name configured in the permissions document for the graph for the requested operation (ReadClaim or WriteClaim) is checked to be present in the supplied token. A HTTP response code of 403 – Forbidden is returned if the supplied access token does not contain the required claim.

If anonymous access is permitted to a graph via the permissions document for a given operation (AnonymousRead or AnonymousWrite are true), the REST request need not include an access token.

Also note that http or https access to a graph is controlled via the ‘AllowNonTlsChannel’ attribute. If this attribute is true (the default for all graphs), all REST requests must be made via https. This ensures that potentially sensitive data is encrypted in transit as well as providing a mechanism for the caller to verify the server to mitigate against Man In The Middle security attacks (see here for more information on MITM: http://en.wikipedia.org/wiki/Man-in-the-middle_attack).

The permissions document for a graph or a tenant may only be modified by a user that is an administrator in the AAD tenant of the same name. Administrators must use the web site at https://graphstore.windows.net/default.aspx and sign in with their AAD credentials to update the document (only the MERGE method is supported – you cannot delete a permissions document for a graph).

What’s Next?

Now that I have shown you the basic operation of WAGS, I encourage you to just try it out. Give us your feedback on how simple you find it to use and how well it fits your needs.

By way of a sample app that leverages WAGS, checkout http://www.orgshare.net/premier-lumber.com (sign in as joe@premier-lumber.com, gustavo@premier-lumber.com or ivo@premier-lumber.com all passwords: P@ssword). I’ll go through this sample in more detail in a future post, but for now have a look at what storing relationships between things can do!

Additionally, the Windows Azure Graph Store has a simple web interface for browsing and managing graphs. Go to https://graphstore.windows.net/default.aspx and start entering queries and issuing updates as described above directly into the app. Sign in using your AAD credentials to start managing the permissions of your graphs (if you’re a company administrator).

In my next post, I will discuss how we use WAGS to extend entities within the Windows Azure Graph.

 

Welcome!

$
0
0

Welcome to Windows Azure Active Directory( AAD ) Graph Team blog! The AAD Graph team is responsible for creating RESTful services that developers can consume to integrate AAD functionality into their applications.

Purpose of the blog

We will use this blog to have a regular and open communication with the developer community by providing updates on our progress, design ideas, walkthroughs, samples  etc. We would also like to hear back from you on what you would like to see in the service and in this blog - any particular functionality that you are missing, any particular samples you would like to see etc.

AAD Graph Overview

Windows Azure Active Directory( AAD ) provides identity management and access control capabilities for your cloud applications. The AAD graph API will let you access the information in the directory through a very simple RESTful service. The AAD graph service is built upon Web technologies like HTTP, JSON and OData and thus it is platform and device independent. You should be able to build your application using your favorite platform whether it be .Net, PHP, Java script, Java etc and should be able to consume the Graph API in a simple manner. There are several classes of cloud applications and the specific needs for these are vastly different. We will try to provide samples and walk throughs for some end to end scenarios on different platforms using Graph API through this blog.

What is available now?

The AAD graph service is currently available as a developer preview here. Since this is a developer preview, please expect some breaking changes as we try to incorporate additional functionality and improve the overall experience.

Additional Resources

You can find more information about Windows Azure Active Directory here.

The MSDN page for AAD graph has some useful links and also includes the documentation for the service end point.  

Azure Active Directory Graph – What’s in it for an app developer?

$
0
0

As we mentioned in the last post, Windows Azure Active Directory (AAD) Graph service lets an application developer access and manipulate the information in Azure Active Directory. But why should an application developer care about this information? Let’s start by looking at Windows Active Directory and drill down to the Graph service for Azure Active Directory which this blog will focus on.

 

What is Active Directory (AD)?

Active Directory provides authentication and authorization for users and computers in Windows Networks. For example, one of the things AD does when user logs in is it verifies that the user name and password are correct. AD also has a lot of interesting information about the user like the information you would find in an address book and the organizational information (like who he reports to, who are his direct reports) etc. This information is used to authorize user when he tries to access resources on the network. This is a very high level overview of Active Directory. There is plenty of information on MSDN, Wikipedia etc. that talks about AD in great detail.

 

What is Windows Azure Active Directory?

Windows Azure Active Directory(AAD) provides identity management and access control capabilities for cloud applications including Microsoft Cloud applications like Windows Azure, Office 365, Dynamics CRM etc. and also for 3rd party cloud applications. Like Windows Active Directory, Azure Active Directory also provides authentication services by verifying the user name and password for the users when they login. Like AD, it also has a lot of interesting information about the user like the information you would find in an address book and the organizational information (like who he reports to, who are his direct reports) etc. In addition to providing the authentication services using the credentials it stores, AAD also can authenticate users with their on premise Windows Active Directory credentials.

 

Using Azure Active Directory in Enterprise Cloud Applications

There are several advantages in integrating your application with AAD for authentication and authorization needs. Given that AAD is leveraged by Microsoft cloud services, you have the ability to provide your application users with a single sign on experience across your application and services like Office 365, Windows Azure etc. You can manage access to your application based on roles and policies that you can set up in AAD. AAD not only can help you when building Line of business (LOB) cloud applications for your organization but also when building enterprise cloud applications that you plan to sell to other organizations. Since AAD is a multi-tenant service, the authentication and access control mechanism that you build into your application can be designed in such a way that it can be used with any tenant or organization provisioned in Azure Active Directory. Given that a lot of organizations are already using Office 365, it is a great convenience for these organizations if they can reuse their information in AAD with 3rd party cloud applications.

There is a ton of useful information in Azure Active Directory that the application developers can use for many different purposes. But how do you access this information? This is where AAD Graph come into the picture.

 

Azure Active Directory Graph

The AAD Graph Service lets you access the information in the AAD through a simple RESTful service. It uses the OData protocol to implement the RESTful service. Understanding some of the basics of RESTful services and the OData protocol is important to gain a solid understanding of how to work with AAD Graph service.

 

What is a RESTful Service?

There is plenty of information about REST on MSDN, Wikipedia and other web sites. Here is a pretty short and high level overview of RESTful services.

Representational State Transfer( REST ) is an architectural style with a set of rules that can be applied when building something( from MSDN article on REST and WCF Services ).

A RESTful service exposes data as a set of Resources on web and follows these rules:

  • Is implemented using HTTP
  • Different operations like Read, Create, and Delete etc. are supported via HTTP methods like Get, Post, Delete etc.
  • The set of resources exposed by the service have a well-defined set of URIs.
  • Data encoding is in Xml or JSON formats.
  • The API is hypertext driven i.e. the response will include all the information needed to interpret it.

 

What is OData?

OData is a web protocol for querying and updating data via RESTful services. It helps you expose data over the web as a set of resources which can be accessed and manipulated using HTTP methods.

Let’s look at some of the important rules/principles for services implemented using the OData protocol:

  • The root of the service always exposes a service document. Service document exposes the list of all resource collections available in the service.
    • For example, http://services.odata.org/OData/OData.svc is the service document for a sample OData service. As you can see, Products, Services and Suppliers are the resource collections exposed by this service.
  • It defines a set of conventions to define the URIs for the resources. For example, the URI for a resource set exposed by the service has the URI of the format <ServiceDocumentURI>/<ResourceSetName>. Based on this convention, you can construct the URI for “Products” Resource Set which would be http://services.odata.org/OData/OData.svc/Products.
  • You can use HTTP operations like GET, POST, Delete etc. to support retrieving, creating, updating data exposed by the service. For example, doing a HTTP Get on the following URL: http://services.odata.org/OData/OData.svc/Products(1) will retrieve the content of the resource in the Products Resource set with Key value ‘1’.
  • Defines a set of query options that can be represented as HTTP Request parameters and are used to perform advanced query operations like Filter, Top etc. Here is an example of a query that filters the Categories Resource set for Resources whose name equals ‘Beverages’: http://services.odata.org/OData/OData.svc/Categories(1)?$filter=Name eq 'Beverages' .

 

So we have described that a RESTful service built using OData protocol can be accessed and manipulated using HTTP operations. We also mentioned that AAD Graph is one such service. In the next blog post, let’s do a deep dive into various operations you can do against the AAD Graph service using HTTP and also look at the responses we get back.

 

Windows Azure Graph Store – An Insanely Simple Way to Store Relationships Between Things

$
0
0

The popularity of the internet has shown us that there are lots of things in the world. Websites, people, pictures, movies – the list goes on. While the internet is notoriously ‘unstructured’, many of these things are identifiable by a simple addressing schema – a URL. The rise of social networking sites is but one example of the value of storing the relationships between things and while these sites use exciting technology to manage these relationships at internet scale, the stores are invariably application-specific.

In a previous blog post we introduced the Windows Azure Graph Store or WAGS as a general purpose store, offered as a managed service and we described the REST interface which serves as the API to the service. This post described the how of WAGS but not the why. In this follow up post, we will attempt to describe why a service like this is important for solving some of the challenges faced by application developers in the cloud.

If you’ve read the previous post you will know that WAGS stores tuples of data. Tuples are a great way of representing relationships between things that can be identified by strings. In the internet of things, all things are identified by URLs and given that URLs are just strings then a tuple in WAGS can represent a relationship between anything in the internet of things.

What can I do with this? Well imagine that I want to write an application that for a set of people tracks the things (other people, photos, interesting websites, etc.) that they’re interested in (sound familiar?). The computer scientists amongst us will recognize that the data structure required to store this kind of information is known as a Graph. A graph is simply a set of nodes or data entities with edges being the relationships between nodes. This kind of data structure is extremely flexible and is helpful for a wide variety of applications.

OrgShare.net – An ‘Interests’ Application

Let’s dive into our ‘interests’ sample application, OrgShare.net. We can see that people, pictures, websites, etc. can be stored as nodes and the interests between them can be stored as edges. I can use WAGS to store the nodes and edges of my graph (see the Quick Start Guide for details on the POST operation to store nodes and edges) and that is sufficient to provide a simple data model for the application. Once the interest relationships have been stored, the application can traverse from a person to the websites that they are interested in and in turn all of the people that are also interested in those same websites.

We can now enhance the data model for orgshare.net by adding attributes to the interests relationships. This style of application may have many different scenarios that demand attributing relationships, but the most obvious is the ability to store if a person ‘Likes’ or ‘Dislikes’ a website. Other attributes may include when the relationship is effective from or to or a rating system. Given that WAGS allows us to store any set of attributes on any node or edge makes enhancing the data model and adding these features very simple. WAGS actually allows me to store any attribute at all on the relationship both of primitive and complex (including collections) types. I may choose to standardize the set of attributes that I store for a given class of relationship or I may apply arbitrary and varying attributes sets. The store won’t know beforehand what the schema of the relationships I want to store is, but it is able to accept these payloads.

WAGS provides the ability to store fully featured relationships between entities located anywhere on the internet, I now want to store a relationship to a relationship. An example of this may be a marriage between two people (the first relationship) and a third person may wish to store that they ‘Like’ the marriage (a relationship to a relationship!). Given that the marriage relationship has a URL that uniquely identifies it and relationships can be constructed between two URLs, we now have the ability to store these types of relationships as well.

Breaking Out of the Data Silos

While there are clearly internet applications that embrace storing their data as graphs, these stores are usually very application specific. Often, they require all possible nodes to reside in the same storage and even if they do provide extensibility for third parties to add their own data, it is usually about furthering the main application and not the vendor of the third party app.

The first constraint requiring all nodes to exist in the same store is particularly restrictive. Given the disparate nature of the internet, entities exist in many, many stores and domains with no converging schema or shared set of attributes (and neither there should be!). Applications that are driven by the relationships between things typically do not own all or even any of these things, they just want to store the relationships. Stores or domains that own things are known as silos. Silos exist for a variety of reasons; data ownership & governance, application performance, a lack of ability to store things in a shareable space… Silos of data will continue to exist for a long time yet and so the ability to ‘break out’ of the silo is an important one if the data is to be leveraged more broadly. Given that the one feature common to all things is a URL that uniquely identifies the thing, the ability provided by WAGS to store a relationship between two URLs allows us to ‘break out’.

Another characteristic of breaking out of data silos is the ability to extend existing entities. An example of this may be the ability to store a payroll number for an employee in the corporate directory. If the employee has a URL that uniquely identifies her in the corporate directory, then instead of taking the traditional approach of paying your directory vendor to extend the schema or pushing central IT to add the extension into the directory, an application developer can simply store the data in a graph store and hoist the relationship between that data and the directory entity. There’s no requirement to synchronize the directory with the graph store and the employee entity is seamlessly extended.

Semi-Structured Data

As we previously mentioned, the relationships in a graph store are semi-structured– they have structure, it is just not well known in advance or consistent. This has traditionally been a problem both for the store holding the data and clients discovering the structure of the data.

Relational stores such as SQL always leverage a well known schema to provide performant query and update capabilities as well as efficient layout of their data storage. The switch to cloud-based storage services means that data layout is less important (to the application writer at least) and the simple keying of graph nodes or edges means that query plans do not require sophisticated analysis by a query engine.

The ability for client applications to consume semi-structured data is greatly enhanced by serialization formats employed by the service. JSON (JavaScript Object Notation) is a very simple and easily discoverable format that is based on an implicit understanding that the client may not have any prior knowledge about the payload that it is receiving. This enables a client application to marshal unknown data structures into its own address space and reflect over them. The client still needs to be able to understand the semantics of any given payload, but at least it can do this without deserialization issues.

What’s Next?

In my next post, I will discuss how we use WAGS to extend entities within Azure Active Directory (AAD) and the Windows Azure Enterprise Graph.

Please note that at this stage WAGS is only available in public preview mode. This means that although we are supporting it as a real service, we are not enforcing any SLAs.

As always we encourage you to give the service a try. Give us your feedback on how simple you find it to use and how well it fits your needs.

By way of a sample app that leverages WAGS, checkout http://www.orgshare.net/premier-lumber.com (sign in as joe@premier-lumber.com, gustavo@premier-lumber.com or ivo@premier-lumber.com all passwords: P@ssword). See what the simplicity of storing relationships between things can do!

Additionally, WAGS has a simple web interface for browsing and managing graphs. Go to https://graphstore.windows.net/default.aspx and start entering queries and issuing updates as described in the Quick Start Guide into the app. Sign on using your AAD/Office 365 credentials to start managing the permissions of your graphs (if you’re a company administrator).

 

 


Extending the Windows Azure Graph Using the Windows Azure Graph Store

$
0
0

In our last blog post (http://blogs.msdn.com/b/aadgraphteam/archive/2013/06/08/windows-azure-graph-store-an-insanely-simple-way-to-store-relationships-between-things.aspx) we introduced the Windows Azure Graph Store (WAGS) and talked about its general usefulness as a managed storage service for graph data. One of the scenarios that we briefly spoke about was the ability to extend entities in pre-existing stores using information held in WAGS. In this post, we will drill into that scenario and talk about how entities can be extended with data held in WAGS or simply storing a relationship to another entity. We will also discuss how the Windows Azure Enterprise Graph includes a feature that allows the newly extended attributes to be discovered and published.

What’s the Windows Azure Enterprise Graph again?

John Shewchuk, in his ‘Reimagining Active Directory for the Social Enterprise’ blog post, http://blogs.msdn.com/b/windowsazure/archive/2012/05/23/reimagining-active-directory-for-the-social-enterprise-part-2.aspx, talked about how the use of simple REST interfaces returning JSON objects is enabling connection of entities, starting with Azure Active Directory (AAD). We refer to this capability as the Windows Azure Enterprise Graph. The Azure Graph truly is a graphical representation of entities from many online products providing the ability to ‘break out’ of the traditional product data silos.

By graphical we don’t mean pretty pictures (although you can create some visually stunning projections of the data), but rather graphical data structures consisting of nodes (entities) and edges (relationships between entities). We can already observe that the AAD data model is a graph; users are members of groups, which in turn include other users, groups, contacts as members, thus the graph is built out.

Extending the Graph

There are two main scenarios for extending existing entities in the graph:

  1. An existing entity requires augmentation with additional custom attributes, and
  2. Relationships between two (or more) entities in different silos need to be materialized.

The first scenario occurs typically when entities are stored in a service provided by an ISV/CSV that you have no ability to physically extend in the native store. An example of this is the native store of Azure Active Directory. This may also occur when an entity’s storage is controlled by a central IT organization and a requirement exists to augment the entity without central IT’s involvement.

The second scenario is driven by a desire to fully leverage the data contained within separate silos. For example, consider the relationship between a user’s profile held in AAD and the work items assigned to that user in Team Foundation Service (TFS). TFS, as an application, is not a user profile repository but is enhanced by having access to some of the user profile information (the user’s name for a start!). One possible solution is to have TFS replicate the user profile, either by having the user manually enter the information and not worry about the situation of multiple unsynchronized user profile stores or automatically synchronize the information from the AAD store into its own (and deal with the subsequent synchronization issues). This approach, however, only enables traversal of the relationship from user profile to work items, but not the other way. A better alternative is to leave a single copy of the entity data in their rightful store (user profile information in AAD, work items in TFS) and simply store the relationships between the entities that enable all of TFS’s rich experiences but now also extend the user profile in AAD by making related entities accessible and discoverable.

Enter the Windows Azure Graph Store

Our previous blog post introduced the Windows Azure Graph Store (WAGS) as an online storage service for storing graphical data structures. You will recall that graphs are constructed by a series of nodes and edges connecting the nodes. Looking at the two scenarios above, it seems that all that is needed to implement the storage for these scenarios are nodes and edges, so WAGS seems like the perfect place to do that.

A common example of the first extension scenario is to store a payroll number for a user. Windows Server Active Directory is commonly extended like this via custom schema, but since Azure Active Directory is a multi-tenanted store it cannot be natively extended on a tenant by tenant basis. However, if the company that requires this extension stores a tuple for each employee in WAGS using the URL of the user profile (eg. https://graph.windows.net/contoso.com/users/joe@contoso.com) as _Item1 and the payroll number as _Item2, then the user profile is effectively extended.

Now that we have our user profile extension data stored in WAGS we need a mechanism to make this extension discoverable. The discoverability of OData data services (such as AAD’s GraphAPI) is via the service’s $metadata document. Calling this service (https://graph.windows.net/contoso.com/$metadata) will return an EDMX (XML) document describing all of the entity sets exposed by the service and all of the type information associated with each entity set.

In our example of extending a user profile with a payroll number, ideally we would see information in the $metadata document indicating that the type returned by the users entity set (Microsoft.WindowsAzure.ActiveDirectory.User) has been extended to include the new attribute. The Windows Azure Graph service supports such a mechanism and so we simply need to create one additional tuple in WAGS to publish the extension. Each AAD tenant has a special WAGS graph called ‘graphextension’ that includes all of the published extensions. Details of the tuple are: 

_Item1 : name of the entity set being extended (eg. users)

_Item2 : name of the extension attribute (eg. PayrollNumber)

OwningTenant : name of the company ‘owning’ the extension. This can be either empty or the entity’s tenant to represent the graph owner (eg. contoso.com) or can be the name of an ISV/CSV that is providing the extension

ValueFormat : this is the value that will be used to construct the URL that identifies the extension data. This format can be either a fixed URL that will be constant for all extended entities or can include replacement tokens surrounded by braces {} that make the URL dynamic per entity. Following is the list of supported replacement tokens:
 

Token

Description

graphurl

The standard base part of all Windows Azure Graph addresses - https://graph.windows.net/{tenant}

graphstore

The base URI for the Graph Store (including tenant) – https://graphstore.windows.net/{tenant}

graphstorebase

The base URI for the Graph Store (excluding tenant) – https://graphstore.windows.net

tenant

The tenant name as extracted from the current request

id

The identifier of the extended entity

itemuri

The full URI of the entity being extended

urlencode:

URL encode everything after the colon (can nest other substitution values)

other

Any other value contained within braces is assumed to be the name of an attribute on the extended entity. The value of this attribute is substituted into the value.

 Going back to our example, we can POST the following JSON object to the contoso.com graphextension graph; https://graphstore.windows.net/contoso.com/graphextension

{
"_Item1" : "users",
"_Item2" : "PayrollNumber",
"OwningTenant" : "contoso.com",
"ValueFormat" : "{graphstore}/payrollnumbers/{id}"
}

We can now request the $metadata document again and this time we can see that the User type is extended with our new attribute:

<EntityType Name="User" BaseType="Microsoft.WindowsAzure.ActiveDirectory.DirectoryObject">
...
<Property Name="contoso.com/payrollnumber" Type="Graph.Extensions.Service.ExtensionAttribute" />
...
</EntityType>

This takes care of the discoverability, but what does this Graph.Extensions.Service.ExtensionAttribute type contain? If you look earlier in the $metadata document you will see this type described:

<ComplexType Name="ExtensionAttribute">
<Property Name="Url" Type="Edm.String" />
</ComplexType>

It has just one property; Url. Remember when we published the extension property by writing a tuple to WAGS that had a ValueFormat attribute? Well, this attribute is expanded by the graph.windows.net service to return a URL that identifies the extension data. So now I can issue a GET request for the extension value:

https://graph.windows.net/contoso.com/users/joe@contoso.com/contoso.com/PayrollNumber

which returns: 

{
"Url": https://graphstore.windows.net/contoso.com/payrollnumbers/joe@contoso.com
}

Following this URL gives us the extension data tuple – the employee’s payroll number: 

{
"_Item1": "https://graph.windows.net/contoso.com/users/joe@contoso.com",
"_Item2": "1234567"
}

Note that extending the Directory in this way also enables us to find users using their payroll number:

https://graphstore.windows.net/contoso.com/payrollnumbers/1234567

returns the tuple above which identifies the user in AAD.

The second extension scenario can be achieved via similar means. The difference is whether or not the relationship between the various entities can be derived from attribute values contained on the outgoing entity. The extension relationship in our original AAD User <-> TFS Work Items is an example where the userPrincipalName attribute on the AAD User type can be used to link directly to TFS Work Items currently assigned to that user. If the relationship is not self-evident and requires a conflation process to determine and store the relationship, the identifying URLs can be stored as a tuple in WAGS and then the relationship would need to indirect through WAGS to obtain the other side and then follow that link.

An example of a conflated relationship that we recently went through was connecting AAD User profiles with their LinkedIn profiles. By observing the LinkedIn public profile pages we quickly determined that email addresses did not connect the two domains (most people use their personal email address in their LinkedIn profile and the AAD user profile contains a work email address) but we were able to connect other attributes such as name, employer, title, etc. to materialize the relationship with a high degree of accuracy. Performing this conflation is a computationally expensive operation so we wouldn’t want to execute it every time an application wanted to traverse the relationship. Thus, we stored the identifying URLs of both sides as a tuple in WAGS. Eg: 

{
"_Item1": "https://graph.windows.net/contoso.com/users/joe@contoso.com",
"_Item2": http://www.linkedin.com/in/joesmith55
}

Then we published the extension attribute into contoso.com’s tenancy: 

{
"_Item1" : "users",
"_Item2" : "LinkedInProfile",
"OwningTenant" : "contoso.com",
"ValueFormat" : "{graphstore}/LinkedIn/{urlencode:{itemuri}}"
}

And so now when we query for the extended attribute:

https://graph.windows.net/contoso.com/users/joe@contoso.com/contoso.com/LinkedInProfile

we get the following response: 

{
"Url": https://graphstore.windows.net/contoso.com/linkedin/https%3A%2F%2Fgraph.windows.net%2Fcontoso.com%2Fusers%2Fjoe%40contoso.com
}

which will provide us with the URL to Joe’s LinkedIn public profile page.

What’s Next?

In our next post we’ll go through the development of a sample application, OrgShare.net that exclusively uses the Windows Azure Graph Store and Azure Active Directory as its storage layer. We will see how to interact with WAGS using both the raw REST calls as well as using the various OData client libraries that are available.

As always, we welcome any feedback and suggestions for what you would like us to talk about more.

 

Handling Errors from the Azure Active Directory Graph Service

$
0
0

All client applications should implement some level of error handling logic to react as gracefully as possible to various conditions and provide the best experience possible to their customers.  This post covers a variety of error conditions that can be returned by the Azure Active Directory Graph Service and explains how each of them can be handled.  Note that this document does not cover error handling of the Windows Azure Access Control Service (ACS) or other related services.

Errors can be categorized into a few types:

- Client errors such as providing invalid values when creating an object.

- Server errors such as a transient directory failure.

-Network/protocol errors such as a host name resolution failure.

HTTP Client Errors

Client errors fall into the range of 4xx HTTP status codes.  Examples of such errors are attempting to access a non-existent resource, attempting to create an object without a required property value, attempting to update a read-only property, not including the required authorization token, etc.  Such requests should not be retried without first fixing the underlying issue.

HTTP Server Errors

Server errors fall into the range of 5xx HTTP status codes.  Some of these errors are transient and can be resolved upon retrying, and some cannot be.

Network/Protocol Errors

A variety of network-related errors can occur while sending a request or receiving a response.  Examples are host name resolution errors, the connection being closed prematurely, SSL negotiation errors, etc.  Most of these errors will not resolve themselves without the underlying issue being resolved; however, some errors such as host-name resolution failures or timeouts might be resolved upon retry.

Service Error Codes

Whenever an HTTP error response is returned by the service, it will typically be in a form like the following:

 

HTTP/1.1 400 Bad Request
Content-Type: application/json;odata=minimalmetadata;charset=utf-8
 request-id: ddca4a7e-02b1-4899-ace1-19860901f2fc
Date: Tue, 02 Jul 2013 01:48:19 GMT


{
"odata.error" : {
"code" : "Request_BadRequest",
"message" : {
"lang" : "en",
"value" : "A value is required for property 'mailNickname' of resource 'Group'."
},
"values" : null
}
}

 

There are a few notable items in the above response body:

- code: This can be read and processed similar to that of an exception, where the client application reacts differently depending on the code.

- message: This is a language/message tuple that represents an error message that can be read by a user.

- values: This is a collection of name/value pairs that can be used to provide more context about the nature of the failure – this will be discussed in a subsequent update to this article.

 NOTE: Proxy/gateway services can be involved as a request is routed from your client to the directory service, meaning that some HTTP responses might not contain the response body indicated above.  In such cases, ensure your code responds as best as possible based on the HTTP status code alone.

The following table contains the set of error codes, along with the associated meaning of each error condition.

HTTP Status Code

Error Code

Details

Retryable

400

Request_BadRequest

A generic request failure has occurred. This can indicate that an invalid property value was included during resource creation, or that an unsupported query argument was specified, etc.  Correct the request inputs and try again.

No

400

Request_UnsupportedQuery

A generic error indicating that an unsupported GET request has been performed.  Correct the GET request inputs and try again.

No

400

Directory_ResultSizeLimitExceeded

The request cannot be fulfilled because there are too many results associated with it. This is a rare condition that will not affect the majority of clients.  This condition will not succeed upon retry, as it pertains to the number of values that would be present in the response.

No

401

Authentication_MissingOrMalformed

The access token, specified as the Authorization header value, is either missing or malformed.  It must be included to execute the request.  The WWW-Authenticate response header will provide more details that can be used to obtain a valid token.  Retry the request after correcting the access token.

No

401

Authorization_IdentityNotFound

The principal contained in the access token could not be found in the directory.  The principal may have been deleted from the directory after the access token was obtained.

No

401

Authorization_IdentityDisabled

The principal contained in the access token is present in the directory, but is disabled.  The principal’s account in the directory should first be enabled for authorization to succeed.

No

401

 Authentication_ExpiredToken

The token’s valid lifetime has been exceeded.  Obtain the request token again and retry the request.

No

403

Authorization_RequestDenied

Indicates that the request has been denied due to insufficient privileges. For example, a non-administrative principal might not have permissions to delete a given resource.

No

403

Authentication_Unauthorized

A general request authorization failure indicating invalid or unsupported claims in the token.  Obtain the request token again and retry the request.

No

403

Directory_QuotaExceeded

A directory quota has been exceeded. This can be caused due an excessive number of objects being present in the tenant, or having been created on-behalf of a specific principal.  This can also occur if the number of values on a particular object having been exceeded.  Increase the maximum allowed quota count for the tenant or principal, or reduce the number of values included in the create/update request.

No

404

Request_ResourceNotFound

The resource indicated by the URI in the request URI or body does not exist.

No

500

Service_InternalServerError

A general error indicating that an unexpected internal service problem has occurred.   Depending on the nature of the problem and the request, a retry may succeed.

Yes

502

<ALL>

A server acting as a gateway/proxy encountered an error from another server during request processing.  Retry the request after waiting a short time.

Yes

503

Request_ThrottledTemporarily

The request rate has exceeded the allowable limit.  Wait and retry your request again later – note that you will typically have to wait longer for such request throttling conditions versus other 503 service-unavailable errors.

 Yes

503

<ALL>

A general service-unavailable error.  This is typically transient and should be resolvable after waiting a short time and retrying.

Yes

 

Retry Logic

The following C# code snippet represents a possible request invocation and retry-handling approach. It uses a simple try/catch block that inspects the request exception to determine whether to retry or not.  This will not handle all exception scenarios, but will handle most that are thrown by the .NET WebClient and WCF Data Services clients.

 

///<summary>/// Invokes the specified web-method delegate and returns the result, including retrying using a simple /// exponential-backoff approach as appropriate.///</summary>///<typeparam name="T">The type of object that the delegate returns.</typeparam>///<param name="retryableOperation">The web-method delegate.</param>///<returns>The result of the method.</returns>privatestaticT InvokeWebOperationWithRetry<T>(Func<T> retryableOperation)
{// Baseline delay of 1 secondint baselineDelayMillis = 1000;constint MaxAttempts = 4;Random random = newRandom();int attempt = 0;while (++attempt <= MaxAttempts)
    {try
        {return retryableOperation();
        }catch (InvalidOperationException invalidOperationException)
        {if (attempt == MaxAttempts || !IsRetryableError(invalidOperationException))
            {throw;
            }int delayMillis =
                baselineDelayMillis + random.Next((int)(baselineDelayMillis * 0.5), baselineDelayMillis);Thread.Sleep(delayMillis);// Increment base-delay time
            baselineDelayMillis *= 2;
        }
    }// The logic above dictates that this exception will never be thrown.thrownewInvalidOperationException("This exception statement should never be thrown.");
}///<summary>/// Indicates whether the request that resulted in the specified exception is retryable or not.///</summary>///<param name="exception">The exception.</param>///<returns>///<see langword="true"/> if the exception is retryable, and <see langword="false"/> otherwise.///</returns>privatestaticbool IsRetryableError(Exception exception)
{// TODO: Log request-id response header value here, to improve debug-ability in case// a class of server errors occur that are difficult to resolve.Nullable<HttpStatusCode> httpStatusCode = null;DataServiceRequestException requestException = exception asDataServiceRequestException;if (requestException != null)
    {OperationResponse opResponse = requestException.Response.FirstOrDefault();
        httpStatusCode = opResponse != null
            ? (HttpStatusCode)opResponse.StatusCode
            : (HttpStatusCode)requestException.Response.BatchStatusCode;
    }DataServiceClientException clientException = exception asDataServiceClientException;if (!httpStatusCode.HasValue && clientException != null)
    {
        httpStatusCode = (HttpStatusCode)clientException.StatusCode;
    }DataServiceQueryException queryException = exception asDataServiceQueryException;if (!httpStatusCode.HasValue && queryException != null)
    {
        httpStatusCode = (HttpStatusCode)queryException.Response.StatusCode;
    }if (!httpStatusCode.HasValue)
    {WebException webException = exception asWebException;if (webException != null ||
            (webException = exception.InnerException asWebException) != null)
        {HttpWebResponse httpWebResponse = webException.Response asHttpWebResponse;if (httpWebResponse == null)
            {// Example of a possible network-related retry condition; the set of network conditions// to retry on and the sleep duration associated with each can differ depending on the client.return webException.Status == WebExceptionStatus.NameResolutionFailure;
            }

            httpStatusCode = httpWebResponse.StatusCode;
        }
    }

    return httpStatusCode.HasValue&& (httpStatusCode == HttpStatusCode.InternalServerError
            || httpStatusCode == HttpStatusCode.BadGateway
            || httpStatusCode == HttpStatusCode.ServiceUnavailable);
}

 

Questions and feedback are welcome as always.

 

Handling Errors from Windows Azure Active Directory Graph Service

$
0
0

All client applications should implement some level of error handling logic to react as gracefully as possible to various conditions and provide the best experience possible to their customers.  This post covers a variety of error conditions that can be returned by the Azure Active Directory Graph Service and explains how each of them can be handled.  Note that this document does not cover error handling of the Windows Azure Access Control Service (ACS) or other related services.

Errors can be categorized into a few types:

- Client errors such as providing invalid values when creating an object.

- Server errors such as a transient directory failure.

-Network/protocol errors such as a host name resolution failure.

HTTP Client Errors

Client errors fall into the range of 4xx HTTP status codes.  Examples of such errors are attempting to access a non-existent resource, attempting to create an object without a required property value, attempting to update a read-only property, not including the required authorization token, etc.  Such requests should not be retried without first fixing the underlying issue.

HTTP Server Errors

Server errors fall into the range of 5xx HTTP status codes.  Some of these errors are transient and can be resolved upon retrying, and some cannot be.

Network/Protocol Errors

A variety of network-related errors can occur while sending a request or receiving a response.  Examples are host name resolution errors, the connection being closed prematurely, SSL negotiation errors, etc.  Most of these errors will not resolve themselves without the underlying issue being resolved; however, some errors such as host-name resolution failures or timeouts might be resolved upon retry.

Service Error Codes

Whenever an HTTP error response is returned by the service, it will typically be in a form like the following:

 

HTTP/1.1 400 Bad Request
Content-Type: application/json;odata=minimalmetadata;charset=utf-8
 request-id: ddca4a7e-02b1-4899-ace1-19860901f2fc
Date: Tue, 02 Jul 2013 01:48:19 GMT


{
"odata.error" : {
"code" : "Request_BadRequest",
"message" : {
"lang" : "en",
"value" : "A value is required for property 'mailNickname' of resource 'Group'."
},
"values" : null
}
}

 

There are a few notable items in the above response body:

- code: This can be read and processed similar to that of an exception, where the client application reacts differently depending on the code.

- message: This is a language/message tuple that represents an error message that can be read by a user.

- values: This is a collection of name/value pairs that can be used to provide more context about the nature of the failure – this will be discussed in a subsequent update to this article.

 NOTE: Proxy/gateway services can be involved as a request is routed from your client to the directory service, meaning that some HTTP responses might not contain the response body indicated above.  In such cases, ensure your code responds as best as possible based on the HTTP status code alone.

The following table contains the set of error codes, along with the associated meaning of each error condition.

HTTP Status Code

Error Code

Details

Retryable

400

Request_BadRequest

A generic request failure has occurred. This can indicate that an invalid property value was included during resource creation, or that an unsupported query argument was specified, etc.  Correct the request inputs and try again.

No

400

Request_UnsupportedQuery

A generic error indicating that an unsupported GET request has been performed.  Correct the GET request inputs and try again.

No

400

Directory_ResultSizeLimitExceeded

The request cannot be fulfilled because there are too many results associated with it. This is a rare condition that will not affect the majority of clients.  This condition will not succeed upon retry, as it pertains to the number of values that would be present in the response.

No

401

Authentication_MissingOrMalformed

The access token, specified as the Authorization header value, is either missing or malformed.  It must be included to execute the request.  The WWW-Authenticate response header will provide more details that can be used to obtain a valid token.  Retry the request after correcting the access token.

No

401

Authorization_IdentityNotFound

The principal contained in the access token could not be found in the directory.  The principal may have been deleted from the directory after the access token was obtained.

No

401

Authorization_IdentityDisabled

The principal contained in the access token is present in the directory, but is disabled.  The principal’s account in the directory should first be enabled for authorization to succeed.

No

401

 Authentication_ExpiredToken

The token’s valid lifetime has been exceeded.  Obtain the request token again and retry the request.

No

403

Authorization_RequestDenied

Indicates that the request has been denied due to insufficient privileges. For example, a non-administrative principal might not have permissions to delete a given resource.

No

403

Authentication_Unauthorized

A general request authorization failure indicating invalid or unsupported claims in the token.  Obtain the request token again and retry the request.

No

403

Directory_QuotaExceeded

A directory quota has been exceeded. This can be caused due an excessive number of objects being present in the tenant, or having been created on-behalf of a specific principal.  This can also occur if the number of values on a particular object having been exceeded.  Increase the maximum allowed quota count for the tenant or principal, or reduce the number of values included in the create/update request.

No

404

Request_ResourceNotFound

The resource indicated by the URI in the request URI or body does not exist.

No

500

Service_InternalServerError

A general error indicating that an unexpected internal service problem has occurred.   Depending on the nature of the problem and the request, a retry may succeed.

Yes

502

<ALL>

A server acting as a gateway/proxy encountered an error from another server during request processing.  Retry the request after waiting a short time.

Yes

503

Request_ThrottledTemporarily

The request rate has exceeded the allowable limit.  Wait and retry your request again later – note that you will typically have to wait longer for such request throttling conditions versus other 503 service-unavailable errors.

 Yes

503

<ALL>

A general service-unavailable error.  This is typically transient and should be resolvable after waiting a short time and retrying.

Yes

 

Retry Logic

The following C# code snippet represents a possible request invocation and retry-handling approach. It uses a simple try/catch block that inspects the request exception to determine whether to retry or not.  This will not handle all exception scenarios, but will handle most that are thrown by the .NET WebClient and WCF Data Services clients.

 

///<summary>/// Invokes the specified web-method delegate and returns the result, including retrying using a simple /// exponential-backoff approach as appropriate.///</summary>///<typeparam name="T">The type of object that the delegate returns.</typeparam>///<param name="retryableOperation">The web-method delegate.</param>///<returns>The result of the method.</returns>privatestaticT InvokeWebOperationWithRetry<T>(Func<T> retryableOperation)
{// Baseline delay of 1 secondint baselineDelayMillis = 1000;constint MaxAttempts = 4;Random random = newRandom();int attempt = 0;while (++attempt <= MaxAttempts)
    {try
        {return retryableOperation();
        }catch (InvalidOperationException invalidOperationException)
        {if (attempt == MaxAttempts || !IsRetryableError(invalidOperationException))
            {throw;
            }int delayMillis =
                baselineDelayMillis + random.Next((int)(baselineDelayMillis * 0.5), baselineDelayMillis);Thread.Sleep(delayMillis);// Increment base-delay time
            baselineDelayMillis *= 2;
        }
    }// The logic above dictates that this exception will never be thrown.thrownewInvalidOperationException("This exception statement should never be thrown.");
}///<summary>/// Indicates whether the request that resulted in the specified exception is retryable or not.///</summary>///<param name="exception">The exception.</param>///<returns>///<see langword="true"/> if the exception is retryable, and <see langword="false"/> otherwise.///</returns>privatestaticbool IsRetryableError(Exception exception)
{// TODO: Log request-id response header value here, to improve debug-ability in case// a class of server errors occur that are difficult to resolve.Nullable<HttpStatusCode> httpStatusCode = null;DataServiceRequestException requestException = exception asDataServiceRequestException;if (requestException != null)
    {OperationResponse opResponse = requestException.Response.FirstOrDefault();
        httpStatusCode = opResponse != null
            ? (HttpStatusCode)opResponse.StatusCode
            : (HttpStatusCode)requestException.Response.BatchStatusCode;
    }DataServiceClientException clientException = exception asDataServiceClientException;if (!httpStatusCode.HasValue && clientException != null)
    {
        httpStatusCode = (HttpStatusCode)clientException.StatusCode;
    }DataServiceQueryException queryException = exception asDataServiceQueryException;if (!httpStatusCode.HasValue && queryException != null)
    {
        httpStatusCode = (HttpStatusCode)queryException.Response.StatusCode;
    }if (!httpStatusCode.HasValue)
    {WebException webException = exception asWebException;if (webException != null ||
            (webException = exception.InnerException asWebException) != null)
        {HttpWebResponse httpWebResponse = webException.Response asHttpWebResponse;if (httpWebResponse == null)
            {// Example of a possible network-related retry condition; the set of network conditions// to retry on and the sleep duration associated with each can differ depending on the client.return webException.Status == WebExceptionStatus.NameResolutionFailure;
            }

            httpStatusCode = httpWebResponse.StatusCode;
        }
    }

    return httpStatusCode.HasValue&& (httpStatusCode == HttpStatusCode.InternalServerError
            || httpStatusCode == HttpStatusCode.BadGateway
            || httpStatusCode == HttpStatusCode.ServiceUnavailable);
}

 

Questions and feedback are welcome as always.

 

Enhancing Graph API queries with additional Odata supported queries

$
0
0

I’m excited to share information about additional Odata support and improved paging capability, which will enable more flexible and efficient Graph API queries. We’ve enabled the following in the Graph API:

1. Support for sorting users using $orderby.  The default sort order for listing users is by userPrincipalName – we can now also sort by displayName.  Here are some examples:

   https://graph.windows.net/contoso.com/users?$orderby=displayName?api-version=2013-04-05

  https://graph.windows.net/ contoso.com/users?$orderby=displayName&$top=50?api-version=2013-04-05

Note: $orderby can’t be combined with filtering ($filter) today. Our future plans include adding support to sort by other user properties, sorting other object types such as Groups, Contacts, and enabling combinations of Odata queries.

2. Support for $expand, to allow for more efficient queries by returning an object and its linked objects, in one query.  For example, if your application needs to get a group and its group membership, without $expand, you would need to make two separate queries: one to get the group object, and a second to get the group’s membership.  For example:

https://graph.windows.net/contoso.com/groups/1747ad35-dd4c-4115-8604-09b54f89277d?api-version=2013-04-05

https://graph.windows.net/contoso.com/groups/1747ad35-dd4c-4115-8604-09b54f89277d/members?api-version=2013-04-05

These 2 queries can now be efficiently combined into one query by using $expand:

https://graph.windows.net/contoso.com/groups/1747ad35-dd4c-4115-8604-09b54f89277d?$expand=members&api-version=2013-04-05    

This query will return the list of the group’s member objects and the group object, in one return payload.  Note: The current limit of returned objects is 20 – if there were 25 members of a group, only the first 20 members would be
returned.  This limit will be reviewed for a future update..

Another example of using $expand, is to get a user and the user’s direct reports – this also previously required two queries, and can now be replaced by one:

https://graph.windows.net/contoso.com/users/derek@contoso.com?$expand=directReports&api-version=2013-04-05

This will return a list of user and contact objects that are direct reports to the user, as well as the user object. 

This last example shows how we can get a user and the user’s manager with the following query:

https://graph.windows.net/contoso.com/users/adam@contoso.com?$expand=manager&api-version=2013-04-05

 

3. Support for paging backwards using a new query parameter “previous-page=true”

Until recently, only paging forward was supported – this can be limiting, especially when you’re designing an application that needs to efficiently go forward and backward between paged results.  We now allow you to page backward.  Here’s an example:

The following request is for getting the first 5 user objects by specifying the $top=5 option.

https://graph.windows.net/contoso.com/users?$top=5&api-version=2013-04-05

The first 5 user objects are returned (default sort order for users is by user principal name), plus a skip token value (odata.nextLink) – the skip token contains the value that marks starting point of getting the
next page of results.

"odata.nextLink":
"directoryObjects/$/Microsoft.WindowsAzure.ActiveDirectory.User?$skiptoken=X'44537074020001000000213A41646D696E34404772617068446972312E6F6E6D6963726F736F66742E636F6D29557365725F31353365353064662D653930382D343836652D383133622D306335646566313666323334B900000000000000000000'"

To get the next page of 5 users, we include this skip token value in the next query:

https://graph.windows.net/contoso.com/users?$top=5&api-version=2013-04-05&$skiptoken=X'4453707402.....0000'

The result of this query will return the next 5 user objects, plus another skip token shown below. Note:  when no more skip tokens are returned, this indicates there are no more objects to be returned from the query.

 [1]  "odata.nextLink":  "directoryObjects/$/Microsoft.WindowsAzure.ActiveDirectory.User?$skiptoken=X'4453707…..00000'"

But, now instead of accessing the next 5 users, the application needs to retrieve the previous page of results – the application would execute a new query with the skip token from [1] and include the previous-page=true
query argument, as follows:

https://graph.windows.net/contoso.com/users?$top=5&api-version=2013-04-05&$skiptoken=X'4453707.....00000'&previous-page=true

Now the returned result will be the previous paged result of 5 user objects, plus another skiptoken.

Therefore, after each query, the application now has two options: 1. Page forward using the skiptoken value, or 2. page backward by using the skiptoken value and the previous-page=true query argument – this now gives the application a flexible way to process and navigate through large datasets. 

Please try it out for yourself.

A simple way to try out and play with these new capabilities, is by using the GraphExplorer at http://GraphExplorer.cloudapp.net, using either your own Azure AD/Office 365 tenant (click on SignIn), or by using the Demo company (click on “Use Demo Company”).

These updates are available immediately for the Graph API. We will continue to introduce new capabilities to enable applications to offer additional features, and we’ll be posting updates here shortly.  Please send to us any questions, comments or other feature requests.

Thank you, Ed

 


 

Announcing the new version of the Graph API: api-version=2013-11-08

$
0
0

We're excited to announce the next version of the Graph API, api-version=2013-11-08 is now available. There are several updates that can be useful for your applications, and enable some new scenarios.

The major updates in this release include: 

  • Support for provisioning Federated users
  • User License assignment
  • Support for adding multiple members to a Group
  • Managing a Group's Owner
  • Sorting Groups by their Display Name property
  • Additional Differential Query features

1. Provisioning Federated Users, by exposing a new user property: immutableId

We now expose a new user property called “immutableId” through the Graph API. What is the significance of this property?  It is used to associate the on-premises user object account to the cloud user hosted in Azure Active Directory – for example, if you have an on-premise directory, and the user object from that directory has an identifier GUID with a value of “9c5923e9-de52-33ea-88de-7ebc8633b9cc", then the Azure AD User object can store this value in its immutableId property (stored as a string). The immutableId is used to link the on-premises account identity with the Azure AD account identity.

Note: customers using the Microsoft DirSync tool, will automatically have sync’ed Azure AD Users’ immutableId property populated – DirSync writes this value as the base64 encoded value of the on-premises object Guid.  Application should store the value that meets the requirements of their federated identity service provider

Here’s an example GET call, requesting only a user’s immutableId property: 

GET https://graphwindows.net/contoso.com/users/Adam@contoso.com/immutableId?api-version=2013-11-08

RESPONSE (in JSON):   {   “value”:”6SNZnFLe6jOI3n68hjO5zA==”   }

If you are creating an Azure AD User, and are using a federated domain for the User’s userPrincipalName property, then you must specify a value for the user’s immutableId property–For example, this is the POST operation to create a new user, assigning @Fabrikam.com to the user’s userPrincipalName, which is configured as a federated domain.

Creating a New User using a
  federated  domain “Fabrikam.com”

POST

https://graph.windows.net/Contoso.com/users?api-version=2013-11-08

HEADERS:

Content-Type: application/json

Authorization: Bearer 
  eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1T….

BODY


 {    
    "accountEnabled":true,

    "userPrincipalName":Derek@Fabrikam.com,

    "displayName":"Derek ", 

     "mailNickname":"Derek",

     "passwordProfile":{ "password":"placeHold3r$", "forceChangePasswordNextLogin":false},

     “immutableId”: “6SNZnFLe6jOI3n68hjO5zA==”

 }

 HTTP RESPONSE: 201 Created

 NOTE: in the above example, a password is required as part of the POST user creation call, but is not used because the User is using a Federated identity.

In this new API version, we have also exposed a new user property, userType.  This is a string value that can be used to allow tenants to classify the types of users within their organization.  For example, regular employees can be
identified by userType =  “Member”,  and guest accounts associated with vendors, consultants or temporary workers, could be have userType = “Guest” .

 2. Support for User Licensing

This action was first available under a preview version (api-version=2013-04-10-preview) and is now officially supported in the “2013-11-08” release.  This method allows assignment and removal of a subscription for any provisioned user account. For example, here’s an initial license assignment of the Enterprise Office Sku, which contains SharePoint Online, Lync Online and the Exchange Online service plans.

POST 

https://graph.windows.net/contoso.com/users/adam@contoso.com/assignLicense?api-version=2013-11-08

HEADERS

Content-Type: application/json

Authorization: Bearer 
  eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1T….

BODY


 {
  "addLicenses":[{"disabledPlans":[ ],"skuId":"6fd2c87f-b296-42f0-b197-1e91e994b900"}],
  "removeLicenses":[ ]

 }

 HTTP RESPONSE:    200 OK

 

Here’s an example of updating a User’s license by disabling specific plans.  In this example, there are 2 disabledPlans (SharePointOnline and LyncOnline"), leaving only the Exchange Service Plan.

Select User License Assignment

POST

https://graph.windows.net/contoso.com/users/adam@contoso.com/assignLicense?api-version=2013-11-08

HEADERS

Content-Type: application/json

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1T….

BODY



  "addLicenses":[  { "disabledPlans":  [”5dbe027f-2339-4123-9542-606e4d348a72”,
                                                            “0feaeb32-d00e-4d66-bd5a-43b5b83db82c” ], 

                             "skuId":"6fd2c87f-b296-42f0-b197-1e91e994b900"
                             }  

                          ],
   "removeLicenses":[ ]

 }

HTTP RESPONSE:   200 OK

 

Finally, here’s how to remove the license from the user.

Remove User License Preview

POST 

https://graph.windows.net/contoso.com/users/adam@contoso.com/assignLicense?api-version=2013-11-08

HEADERS

Content-Type: application/json

Authorization: Bearer
  eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1T….

BODY


 {

  "addLicenses":[ ],

  "removeLicenses":["6fd2c87f-b296-42f0-b197-1e91e994b900"]

 }

HTTP RESPONSE:   200 OK

 

How do you get the subscription Sku Id and the plan ids?  These can be read from the tenant object, by calling GET https://graph.windows.net/contoso.com/subscribedSkus
which will return the subscriptions available for the tenant – this includes the SkuId and servicePlans Id’s under the Sku. Availability of subscriptions can be calculated from the “consumedUnits” property and values
from “prepaidUnits” complex property, which includes counts of units that are “enabled”, “suspended” and in “warning” states.  You can view this using the https://GraphExplorer.cloudapp.net demo – select
“Use Demo Company”, and execute this query: https://graph.windows.net/GraphDir1.OnMicrosoft.com/subscribedSkus?api-version=2013-11-08

 3. Adding Group Members operation can now support multiple member objects in the same request.  

We have always allowed adding a single object to a group, but we now support multiple objects being added to a group in one call.  The example below adds two new users to a group.  NOTE: The maximum allowed number of links to add in a single POST/PATCH request should be kept to 25 or below.  Attempts to add a higher number of links can result in a failure response.

Update Group or Role membership

PATCH

https://Graph.windows.net/contoso.com/groups/02a8a087-a371-43f9-94df-cf0f654de307?api-version=2013-11-08

HEADERS

Content-Type: application/json

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1T….

BODY


 {

   "members@odata.bind":

   [  

          "/contoso.com/users/09f8c01f-53cd-466f-9d2e-b7fb6a77b119",       
          "/contoso.com/users/2ac58744-e872-49fa-bfb2-d0ff8c0d5750"

   ]

 }

HTTP RESPONSE:   204 No Content

 Note: Removing multiple members is not supported at this time.

 

4. Managing Group Owners  

How do you find a Group’s owner?  Easy - the following will return the Group's owner: GET https://Graph.windows.net/contoso.com/groups/02a8a087-a371-43f9-94df-cf0f654de307/owners?api-version=2013-11-08

The following will return the group object, and the owner objects in one call: https://Graph.windows.net/contoso.com/groups/02a8a087-a371-43f9-94df-cf0f654de307?$expand=owners?api-version=2013-11-08

And to assign an owner to a Group, the following POST operation can be executed:

Update a Group’s Owner

POST

https://Graph.windows.net/contoso.com/groups/02a8a087-a371-43f9-94df-cf0f654de307/$links/owners?api-version=2013-11-08

HEADERS

Content-Type: application/json

Authorization: Bearer
  eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1T….

BODY


{

   "url":  "https://graph.windows.net/contoso.com/users/88478020-a5b2-4176-b720-838af8dee190"

}

RESPONSE: 204 No Content

 And with other links, you can also find out what objects are owned for a User: 

GET https://graph.windows.net/contoso.comt/users/adam@contoso.com/ownedObjects&api-version=2013-11-08

Or retrieve the user and the user's owned objects in one call: GET https://graph.windows.net/contoso.comt/users/adam@contoso.com?$expand=ownedObjects&api-version=2013-11-08

5. Sorting lists of Groups by display name

We now allow your app to specify sorting by the Group’s display name property. For example: GET https://graph.windows.net/contoso.com/groups?$orderby=displayName?api-version=2013-11-8.  And in case you missed it in my previous blog post, you can specify sort order for user lists, by either the displayName or userPrincipalName property. 

6. Additional Differential Query features

Differential Queries can now return only updated properties and links – this allows faster processing and reduced payloads for Differential Query calls. This option is enabled by setting the header ocp-aad-dq-include-only-changed-properties to true as shown in this example.

 

GET

https://graph.windows.net/contoso.com/users?api-version=2013-11-08&deltaLink= furK18V1T….

HEADERS

ocp-aad-dq-include-only-changed-properties : true

Content-Type: application/json

Authorization: Bearer   eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5nl0aEV1T….

Response: 200 OK

For example if only the “displayName” property of user has changed.  The returned object would be similar to this:

{     

          "displayName" : "AnUpdatedDisplayName",

         "objectId" :  "c1bf5c59-7974-4aff-a59a-f8dfe9809b18",

         "objectType" :  "User",

          "odata.type" :  "Microsoft.WindowsAzure.ActiveDirectory.User"

},

Differential Sync Support to Sync from “now” - a special header can be specified, requesting to only get an up-to-date deltaToken, this token can be used in subsequent queries, which will
return only changes from “now”.  Here’s the example call:

GET

https://graph.windows.net/contoso.com/users?api-version=2013-11-08&deltaLink= smLYT8V1T…

HEADERS

ocp-aad-dq-include-only-delta-token: true

Content-Type: application/json

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5nl0aEV1T….

Response: 200 OK

The response will contain the deltaLink, but will not have the changed object(s), similar to this:

{   …  "aad.deltaLink":https://graph.windows.net/contoso.com/users?deltaLink=MRa43......   }

We’ll dive into Differential Query calls in much more detail in the upcoming posts.

Give Use Feedback

We will be updating our sample applications to show some of the above mentioned features in the new version: http://msdn.microsoft.com/en-us/library/hh974459.aspx and in an upcoming post, we will include code snippets showing how to use these new
capabilities.

Please let us know if the new features are useful, and let us know what other Api's will be useful for your scenarios.  Leave a reply or question on this Post, or send email:  RESTAAD@microsoft.com

Thanks, Edward

Azure Active Directory Team

Microsoft Corp

Viewing all 46 articles
Browse latest View live