If you’ve spent any time with the Azure Graph API, it’s pretty sweet. Federated identity for the masses, with almost zero drama. Up until now I was mostly doing logins, queries, etc. with Azure AD, but for my latest project, I need to create both new domains and new users in those domains. I haven’t tackled creating new domains yet, because that looks like it’s going to be a royal PITA (automating powershell? ick) — but I kicked down the user path today. Went pretty well, until I got stopped cold adding a user.
Here’s some code
Adding a user with ADALv2 + the Active Directory Graph Client is pretty easy. Both are NuGet packages and simplify the process considerably. You can also post the JSON yourself, which you can find here on MSDN.
But I’m using ADGC, so here’s a quick snip of the required fields you’ll need to get a user created:
var gc = new GraphConnection(accessToken); //get this below
var pp = new PasswordProfile() //required
{
ForceChangePasswordNextLogin = true,
Password = "Watermelon1!"
};
var u = new User
{
DisplayName = displayName,
UserPrincipalName = upn,
PasswordProfile = pp,
MailNickname = displayName.Replace(" ", string.Empty),
UsageLocation = "US",
AccountEnabled = true,
ImmutableId = Guid.NewGuid().ToString()
};
try
{
var p = gc.Add(u);
Console.WriteLine("Created {0}, immutable ID: {1}", p.UserPrincipalName, p.ImmutableId);
}
catch (GraphException ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("{0}: {1}", ex.ErrorMessage, ex.ErrorResponse.Error.Message);
}
Pretty straightforward…until you get to
gc.Add(u);
chances are you’ll blow up with a 403 Forbidden. In fact, chances are high, like 100% this will happen (if it doesn’t let me know).
Graph Read/Write
For whatever reason, and I’m still trying to figure out exactly why, the ‘Read and write directory data’ permission doesn’t appear to allow adding users. I’m assuming this is because they want a user who’s in one of the principal management roles, like User Administrator (see this post for some info on that), as opposed to allowing app principals to do this. The long and short is that the Graph API wants you to go through an OAuth browser flow to delegate a token from a user with the appropriate permissions. If you’re using ADALv2, there’s no
AcquireToken
overload that’ll do this. This is fine, unless you want to automate the creation of these users.
What are you to do?
Fortunately, we can use the OAuth password grant_type to request a token with only a user’s username & password.
AccessTokens & the Graph
You’ll need a few things to get setup. I’m not going to go into much detail here, because if you’re encountering this issue chances are you’re already well setup. We need to request a token from the AAD STS, including both the user’s username/password, as well as the client ID and secret of the app you’re developing. Here’s a sample:
var reqUri = "https://login.windows.net/YOUR TENANT ID OR NAME/oauth2/token";
var postData = "resource=00000002-0000-0000-c000-000000000000&client\_id={0}&grant\_type=password&username=john%40mytenant.onmicrosoft.com&password=nicetry&scope=openid&client\_secret={1}";
var wc = new WebClient();
wc.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
var response = wc.UploadString(reqUri, "POST", string.Format(postData, AppId, EncodedKey));
var tokenData = JObject.Parse(response);
return tokenData\["access\_token"].Value();
Let’s deconstruct this request a bit, shall we?
https://login.windows.net/YOUR TENANT ID OR NAME/oauth2/token
This is where we’re posting our token request
PostData
The main chunk of our request.
resource
The resource you’re trying to access. In this case, it’s the graph, which always has this ID:00000002–0000–0000-c000–000000000000
client_id
your app’s client IDclient_secret
Important — make sure to URL encode your key before putting it heregrant_type
password
username
your UPN (e.g., blah@tenant.onmicrosoft.com)password
this should be obviousscope
Get data in the OpenID Connect format: openid
And make sure you URL encode the form/values before submitting, otherwise it’s 400s for you.
And that’s it
Provided you got a token back and didn’t have any problems with the request, you should be able to tack that access token into the header
Authorization: Bearer …access token…
or you can stuff that into
new GraphConnection(accessToken)
if you’re using the Graph Client wrapper.
Create away! You’re off.