Using Microsoft Graph API to interact with Azure AD
Solution ·In my last article, I showed how to authenticate on Azure AD using a user name / password without using the native web flow.
The underlying scenario was to migrate an application using an LDAP server by leveraging an Azure AD tenant.
The logical continuation of that scenario is to use the Microsoft Graph API to interact with the tenant the same way we would use LDAP queries to interact with the LDAP server.
Microsoft Graph API is a generalization of the Azure AD Graph API and should be used instead. It consists of simple REST queries which are all documented.
In this scenario, I’ll consider three simple interactions:
- Testing if a user exists
- Returning the groups a user belong to
- Creating a new user
But first we need to setup the Azure AD tenant.
Azure AD setup
We’re going to rely on the last article to do the heavy lifting.
We are going to create a new application (here we’re going to use the name “QueryingApp”) of type Web App / API (although native should probably work).
The important part is to grab its Application-ID & also to give it enough permission to create users.
In this scenario, the application is going to authenticate itself (as opposed to a user) so we need to define a secret.
We’ll need to add a key, save & copy the key value.
Authenticating with ADAL
This sample is in C# / .NET but since the Active Directory Authentication Library (ADAL) is available on multiple platform (e.g. Java), this should be easy to port.
We need to install the NuGet package Microsoft.IdentityModel.Clients.ActiveDirectory in our project.
private static async Task<string> AppAuthenticationAsync()
{
// Constants
var tenant = "LdapVplDemo.onmicrosoft.com";
var resource = "https://graph.microsoft.com/";
var clientID = "9a9f5e70-5501-4e9c-bd00-d4114ebeb419";
var secret = "Ou+KN1DYv8337hG8o8+qRZ1EPqBMWwER/zvgqvmEe74=";
// Ceremony
var authority = $"https://login.microsoftonline.com/{tenant}";
var authContext = new AuthenticationContext(authority);
var credentials = new ClientCredential(clientID, secret);
var authResult = await authContext.AcquireTokenAsync(resource, credentials);
return authResult.AccessToken;
}
Here the clientID is the application ID of the application we created at secret is the secret key we created for it.
Here we return the access token as we’re going to use them.
Authenticating with HTTP POST
If we do not want to integrate with the ADAL, here’s the bare bone HTTP post version:
private static async Task<string> HttpAppAuthenticationAsync()
{
// Constants
var tenant = "LdapVplDemo.onmicrosoft.com";
var clientID = "9a9f5e70-5501-4e9c-bd00-d4114ebeb419";
var resource = "https://graph.microsoft.com/";
var secret = "Ou+KN1DYv8337hG8o8+qRZ1EPqBMWwER/zvgqvmEe74=";
using (var webClient = new WebClient())
{
var requestParameters = new NameValueCollection();
requestParameters.Add("resource", resource);
requestParameters.Add("client_id", clientID);
requestParameters.Add("grant_type", "client_credentials");
requestParameters.Add("client_secret", secret);
var url = $"https://login.microsoftonline.com/{tenant}/oauth2/token";
var responsebytes = await webClient.UploadValuesTaskAsync(url, "POST", requestParameters);
var responsebody = Encoding.UTF8.GetString(responsebytes);
var obj = JsonConvert.DeserializeObject<JObject>(responsebody);
var token = obj["access_token"].Value<string>();
return token;
}
}
Here I use the popular Newtonsoft Json Nuget Package to handle JSON.
By no mean is the code here a master piece of robustness and style. It is meant to be straightforward and easy to understand. By all means, improve it %500 before calling it production ready!
Testing if a user exists
Here we’re going to use Get a User of Microsoft Graph API.
There is actually a NuGet package for Microsoft Graph API and, in general SDKs (at the time of this writing) for 9 platforms.
private static async Task<bool> DoesUserExistsAsync(HttpClient client, string user)
{
try
{
var payload = await client.GetStringAsync($"https://graph.microsoft.com/v1.0/users/{user}");
return true;
}
catch (HttpRequestException)
{
return false;
}
}
Again, the code is minimalist here. The HTTP GET actually returns user information that could be used.
Returning the groups a user belong to
Here we’re going to use the memberof method of Microsoft Graph API.
private static async Task<string[]> GetUserGroupsAsync(HttpClient client, string user)
{
var payload = await client.GetStringAsync(
$"https://graph.microsoft.com/v1.0/users/{user}/memberOf");
var obj = JsonConvert.DeserializeObject<JObject>(payload);
var groupDescription = from g in obj["value"]
select g["displayName"].Value<string>();
return groupDescription.ToArray();
}
Here, we deserialize the returned payload to extract the group display names. The information returned is richer and could be used.
Creating a new user
Finally we’re going to use the Create User method of Microsoft Graph API.
This is slightly more complicated as it is an HTTP POST with a JSON payload in input.
private static async Task CreateUserAsync(HttpClient client, string user, string domain)
{
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))
{
var payload = new
{
accountEnabled = true,
displayName = user,
mailNickname = user,
userPrincipalName = $"{user}@{domain}",
passwordProfile = new
{
forceChangePasswordNextSignIn = true,
password = "tempPa$$w0rd"
}
};
var payloadText = JsonConvert.SerializeObject(payload);
writer.Write(payloadText);
writer.Flush();
stream.Flush();
stream.Position = 0;
using (var content = new StreamContent(stream))
{
content.Headers.Add("Content-Type", "application/json");
var response = await client.PostAsync("https://graph.microsoft.com/v1.0/users/", content);
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException(response.ReasonPhrase);
}
}
}
}
Calling Code
The calling code looks like this:
private static async Task Test()
{
//var token = await AppAuthenticationAsync();
var token = await HttpAppAuthenticationAsync();
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var user = "test@LdapVplDemo.onmicrosoft.com";
var userExist = await DoesUserExistsAsync(client, user);
Console.WriteLine($"Does user exists? {userExist}");
if (userExist)
{
var groups = await GetUserGroupsAsync(client, user);
foreach (var g in groups)
{
Console.WriteLine($"Group: {g}");
}
await CreateUserAsync(client, "newuser", "LdapVplDemo.onmicrosoft.com");
}
}
}
Summary
We can see that using the Microsoft Graph API, most if not all LDAP query can easily be converted.
Microsoft Graph API is aligned with OData v3 which makes it a great REST API where filtering queries are standardized.
19 responses