Managing Azure B2C users with Microsoft Graph API

What is Microsoft Graph?

Microsoft Graph is an API that is built on top of Office365. Microsoft Graph gives you a single REST API to connect with O365 products such as Azure AD, Azure AD B2C, Outlook, Onedrive…etc.

Today, I’m gonna show you how you can use Microsoft Graph to manage Azure B2C users.

Requirements

Before starting, You should have a working B2C tenant. If you don’t have one, please go through the below documentation from Microsoft and create a B2C tenant.

https://docs.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant

To follow up with this article, you should have some knowledge in C# and .NET Core Applications also.

Azure B2C Configuration

Step 1 — Create an Application in Azure B2C

Head over to your B2C tenant. First, your B2C tenant has a default tenant name. Copy and paste it somewhere. in my case, it is
servelabs.onmicrosoft.com

Click on the Applications tab. And then click the Add button. I’m gonna fill the form like below. Feel free to use your values.

Creating an application for Graph API

Here, you can see that I have entered https://localhost:5001 as the Reply URL. We don’t need this for our work. But Can’t create the application without it. So After creating the application, you can simply turn off the Web App / Web API option. Once you are done filling the data, click Create to create the application

Once the application is created, navigate into the application. There is a value that we gonna need later. The Client ID of the application. Copy the value and paste it.

Now go back and select App registrations (Preview) tab from the left menu. Navigate into the application that you created.

Now, you should be here.

Step 2 — Create a client secret

Click on the Certificates & secrets tab. You have to create a client secret key for this app.

Click New client secret

Click on the New client secret button to create the key. One the key is created, please copy the key and paste it somewhere.

Step 3 — Give necessary permissions to the application

Great. Now click on API permissions on the left menu. We have to give some permissions to the app. Now click on Add a permission button.

This will appear as a drawer

In the drawer, select Microsoft Graph. It will give you the below window.

Permission type selection window

There are two types of permissions. Delegated and application permission. We have to give both types of permissions here. I will list down all the permissions with their type. Go ahead and give the permissions.

All permissions

You can see the type of permission is appear here. This is a bit boring task. But you have to do that. 😉

Once you are done adding permissions, you will get a permission list like this. Some permission might have their status as Not granted

To fix this, click on the Grant admin consents for the servelabs button. this last word will be different in your B2C tenants. You should get a list like below.

In the application list, you would have noticed that there is another application named b2c-extensions-app. Do not modify. Used by AADB2C for storing user data. We need value from this application. The Application (client) ID. Copy the value and paste it somewhere.

Now we should have four values copied. Tenant name, Application ID and the client secret of the application that we created, and Application (client) ID of the b2c-extensions-app.

Great. Now we are done with Azure B2C configuration. Let’s move into the .NET project.

.NET Core project

Step 1 — Create a .NET Core API project

Open Visual Studio and create a .NET Core API project

Select or Search ASP.NET Core Web Application
Choose API option

This will give you a brand-new ASP.NET Core API project with one controller and a model.

Step 2 — Add variables to both appsettings.json and appsettings.Development.json

Open appsettings.Development.json. We need to add the copied values from B2C. Do it as follows.

appsettins.json

Do the same for appsettings.Development.json as well.

Step 3 — Add a Controller and necessary Models

First, let’s create a controller class named UserController. When you creating it, select the API Controller option. Now you should have a brand-new controller with you.

Let’s create the models too. Create a folder called Models in your project. And create a model called B2CUser.

B2CUser model

And create another model named B2CUserSettings. This model is used to map and get the values from appsettings.json to the file that we gonna performer the B2C operations. So make sure to name properties the same as in appsetting.json

B2CUserSettings

Since we are using a model/class to get the values from appsettings.json, We need to let the app know that the values from appsettings.json should be mapped to this model when the application starts. Heres how we do it. Go to the startup.cs class and find the ConfigureServices method. And the following code there.

services.Configure<B2CUserSettings>(this.Configuration.GetSection("B2C"));

I have configured CORS also. So here is my ConfigureServices method.

public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddCors(options =>
{
options.AddPolicy(
"CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
services.AddScoped<IUserManager, UserManager>();
services.Configure<B2CUserSettings>(this.Configuration.GetSection("B2C"));
}

view raw
ConfigureServices.cs
hosted with ❤ by GitHub

One thing that you should notice is when you add that code, You should get an error. That is because you haven’t imported the Models folder. So either you can type

using B2CUserManagement.Models;

on the top. Or simply hover over the error. And it will give you a bulb icon. Press that. You will see an option to import the relevant folder/folders

Since I configured CORS, I have added the following line to the Configure method as well. You should add that too.

app.UseCors("CorsPolicy");

Great. Now we are good to go with the controller and the models. Of course, we need to add a method to the controller. But we will do that after creating the user management class.

Step 4 — Add Helper classes

Talking about helpers, We need a way to create the password for the user. So for that, I’m gonna create a public class. PasswordHelper.cs

In this class, I wanna method to generate a random and a good password. Here is the method.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace B2CUserManagement.Helpers
{
public static class PasswordHelper
{
public static string GenerateNewPassword(int lowercase, int uppercase, int numerics)
{
string lowers = "abcdefghijklmnopqrstuvwxyz";
string uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string number = "0123456789";
Random random = new Random();
string generated = "!";
for (int i = 1; i <= lowercase; i++)
generated = generated.Insert(
random.Next(generated.Length),
lowers[random.Next(lowers.Length 1)].ToString()
);
for (int i = 1; i <= uppercase; i++)
generated = generated.Insert(
random.Next(generated.Length),
uppers[random.Next(uppers.Length 1)].ToString()
);
for (int i = 1; i <= numerics; i++)
generated = generated.Insert(
random.Next(generated.Length),
number[random.Next(number.Length 1)].ToString()
);
return generated.Replace("!", string.Empty);
}
}
}

view raw
PasswordHelper.cs
hosted with ❤ by GitHub

Step 5 — Create an interface for the user manager class.

We still haven’t created the user manager class. The first step for that is creating the interface. We are not going to call the main class directly. We gonna do that through the interface. So, create a new folder called Interfaces. and add an interface. I’m gonna name that interface as IUserManager

using B2CUserManagement.Models;
using Microsoft.Graph;
using System.Threading.Tasks;
namespace B2CUserManagement.Interfaces
{
public interface IUserManager
{
Task CreateUser(B2CUser user);
Task<IGraphServiceUsersCollectionPage> GetUserByEmail(string email);
Task<bool> DeleteUser(string email);
}
}

view raw
IUserManager.cs
hosted with ❤ by GitHub

In interfaces, we are not implementing anything. It only has the method names.

Step 6 — Create a user manager class.

Here we are in the main class. I’m not gonna create a folder for that. But if you want, You can create a folder.

Before creating this class, I’m gonna install a nugget package called Microsoft.Graph.Auth. Otherwise, I’m gonna get an error. So open the package manager console and the package by executing following command

Install-Package Microsoft.Graph.Auth -Version 1.0.0-preview.4

Here is my UserManager class

using B2CUserManagement.Interfaces;
using B2CUserManagement.Models;
using Microsoft.Extensions.Options;
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace B2CUserManagement
{
public class UserManager : IUserManager
{
private readonly GraphServiceClient graphClient;
private readonly B2CUserSettings userSettings;
//public UserManagement() { }
public UserManager(IOptions<B2CUserSettings> userSettings)
{
// The client_id, client_secret, and tenant are pulled in from the appsettings.json from coach API
this.userSettings = userSettings.Value;
// Initialize the client credential auth provider
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(this.userSettings.clientId)
.WithTenantId(this.userSettings.tenant)
.WithClientSecret(this.userSettings.clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
// Set up the Microsoft Graph service client with client credentials
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
this.graphClient = graphClient;
}
public async Task CreateUser(B2CUser user)
{
try
{
// Create user
var result = await this.graphClient.Users
.Request()
.AddAsync(new User
{
GivenName = user.FirstName,
Surname = user.LastName,
DisplayName = user.FirstName + " " + user.LastName,
Identities = new List<ObjectIdentity>
{
new ObjectIdentity()
{
SignInType = "emailAddress",
Issuer = this.userSettings.tenant,
IssuerAssignedId = user.Email
}
},
PasswordProfile = new PasswordProfile()
{
Password = Helpers.PasswordHelper.GenerateNewPassword(4, 8, 4)
},
PasswordPolicies = "DisablePasswordExpiration",
});
}
catch (ServiceException ex)
{
if (ex.StatusCode == System.Net.HttpStatusCode.BadRequest)
{
}
}
catch (Exception ex)
{
}
}
public async Task<IGraphServiceUsersCollectionPage> GetUserByEmail(string email)
{
try
{
// Get user by sign-in name
var result = await this.graphClient.Users
.Request()
.Filter($"identities/any(c:c/issuerAssignedId eq '{email}' and c/issuer eq '{this.userSettings.tenant}')")
.Select(e => new
{
e.DisplayName,
e.Id,
e.Identities
})
.GetAsync();
if (result != null)
{
return result;
}
return null;
}
catch (Exception ex)
{
return null;
}
}
public async void DeleteUser(string email)
{
var user = await this.GetUserByEmail(email);
var userId = user.CurrentPage[0].Id;
try
{
// Delete user by object ID
await this.graphClient.Users[userId]
.Request()
.DeleteAsync();
}
catch (Exception ex)
{
}
}
}
}

view raw
UserManager.cs
hosted with ❤ by GitHub

As I told you before, We are not gonna call the UserManager class directly. We gonna use the IUserManager interface for that. You can see that I have implemented the interface in this class. So in Startup.cs we have to define this relationship.

services.AddScoped<IUserManager, UserManager>();

Add the above code in the ConfigureServices method.

Step 7 — Add the controller method

Let’s create the controller methods now. The important thing is we have defined the IUserManager interface. So we gonna inject that into the constructor of the controller.

Here is my code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using B2CUserManagement.Interfaces;
using B2CUserManagement.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace B2CUserManagement.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
private IUserManager UserManager { get; set; }
public UserController(IUserManager userManager)
{
this.UserManager = userManager;
}
[HttpGet]
public async Task<ActionResult> Get([FromQuery]string email)
{
try
{
var user = await this.UserManager.GetUserByEmail(email);
if (user == null)
{
return this.NotFound("User do not exist");
}
return this.Ok(user.CurrentPage[0].DisplayName);
}
catch (Exception ex)
{
return this.BadRequest("Could not get the user");
}
}
[HttpPost]
public async Task<ActionResult<B2CUser>> AddUsers([FromBody] B2CUser user)
{
try
{
await this.UserManager.CreateUser(user);
return this.Ok("User Added");
}
catch (Exception ex)
{
return this.BadRequest("Could not add user");
}
}
[HttpDelete("{email}")]
public async Task<ActionResult> Delete(string email)
{
try
{
var result = await this.UserManager.DeleteUser(email);
if (result)
{
return this.Ok("User deleted");
}
return this.BadRequest("Couldn't delete the user");
}
catch (Exception ex)
{
return this.BadRequest("Could not delete User");
}
}
}
}

view raw
UserController.cs
hosted with ❤ by GitHub

Step 8 — Add swagger to the project

It’s better to use swagger in your project always. Swagger is a middleware that gives a UI for the controller method. Follow this documentation to add swagger to your project.

https://docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-3.1&tabs=visual-studio

One additional thing that you can do is change the launchSettings.json as follows. So each time you start the project, swagger UI will be loaded. This is so much easier.

change “launchUrl” value to “swagger” in both places

Finally, run the project. You will get the swagger UI.

Swagger UI

It’s time to test the application

First, let’s add a user. Click on the POST method. Fill the data and execute the method. I’m gonna use following details

{
  "firstName": "Ayesh",
  "lastName": "Nipun",
  "email": "nipun.yesh@gmail.com"
}

Now check the response.

Response

Let’s check the B2C users now.

User is added to the B2C

As you can see, the user is added to the B2C tenant. Same as above, you can test both GetUserByEmail and DeleteUser methods.

Hope you learned something from this article. Here is the complete code in Github. Your comments and thoughts are always welcome.

https://github.com/ayeshN/B2CUserManagement

References – https://docs.microsoft.com/en-us/azure/active-directory-b2c/manage-user-accounts-graph-api

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s