I am not going to explain about the Azure setup for B2C creations and configurations, please find the below link for setting up the Tenant and Configurations.
For
AzureB2C Setup, please go through the link completely and try to create Active Directory and configurations like signup & sign in policy and domain setup etc.
Most of the people can directly integrate into strap class after setting up a tenant in Azure like this,
- services.AddAuthentication(options =>
- {
- options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
- })
- .AddJwtBearer(jwtOptions =>
- {
- jwtOptions.Authority = $"https://login.microsoftonline.com/tfp/{Configuration["AzureAdB2C:Domain"]} /{Configuration["AzureAdB2C:SignUpSignInPolicyId"]}/v2.0/";
- jwtOptions.Audience = Configuration["AzureAdB2C:ClientId"];
- jwtOptions.Events = new JwtBearerEvents
- {
- OnAuthenticationFailed = AuthenticationFailed
- };
- });
Let's try something different to implement in our own way.
Create a sample class with get and setter properties to set configuration, before that in appsetting.json create properties with the same names,
- "AzureAdB2C": {
- "Instance": "https://login.microsoftonline.com/tfp/",
- "ClientId": "###########################",
- "Domain": "########.onmicrosoft.com",
- "SignUpSignInPolicyId": "B2C_#_SignInUp"
- },
After creating json , create a class like this,
- public class AzureAdB2COptions
- {
- public string ClientId { get; set; }
- public string Instance { get; set; }
- public string Domain { get; set; }
- public string SignUpSignInPolicyId { get; set; }
- }
Configuration class is created, but we have built the Azure connection, so create an extension method for "AuthenticationBuilder", we will call this extenesion method in startup rather than calling "AddJwtBearer"
- public static class AzureAdServiceCollectionExtensions
- {
- public static AuthenticationBuilder AddAzureAdB2CBearer(this AuthenticationBuilder builder)
- => builder.AddAzureAdB2CBearer(_ => { });
- }
And Create private class to set configuration to connect to Azure using "AzureAdB2COptions", and inject "AzureAdB2COptions" class into ConfigureAzureoption
- private class ConfigureAzureOptions : IConfigureNamedOptions<JwtBearerOptions>
- {
-
- private readonly AzureAdB2COptions _azureOptions;
-
- public ConfigureAzureOptions(IOptions<AzureAdB2COptions> azureOptions)
- {
- _azureOptions = azureOptions.Value;
- }
-
-
- public void Configure(string name, JwtBearerOptions options)
- {
- options.Audience = _azureOptions.ClientId;
- options.Authority = $"{_azureOptions.Instance}/{_azureOptions.Domain}/{_azureOptions.SignUpSignInPolicyId}/v2.0";
- }
-
- public void Configure(JwtBearerOptions options)
- {
- Configure(Options.DefaultName, options);
- }
- }
After creating this ConfigureAzureOptions class you have to inject this class into extension class or it won't call, right? So here we go.
- public static AuthenticationBuilder AddAzureAdB2CBearer(this AuthenticationBuilder builder, Action<AzureAdB2COptions> configureOptions)
- {
-
- builder.Services.Configure(configureOptions);
-
- builder.Services.AddSingleton<IConfigureOptions<JwtBearerOptions>, ConfigureAzureOptions>();
- builder.AddJwtBearer();
- return builder;
- }
After finishing this extension method implementation we have to call this extension method, as I said, in place of "AddJwtBearer
- services.AddAuthentication(sharedOptions =>
- {
- sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
- }).AddAzureAdB2CBearer(options => Configuration.Bind("AzureAdB2C", options));
-
Also while Authorizing our request if we want to apply any filter globally we can write it like this, it works for multi-tenancy to validate id (in case they send it from API header).
- public class AuthorizationFilterApply: IAsyncAuthorizationFilter {
- public Task OnAuthorizationAsync(AuthorizationFilterContext context) {
- if (context.HttpContext.User.Claims != null) {
- var email = context.HttpContext.User.Claims.FirstOrDefault(c => c.Type == "emails").Value;
- } else {
- context.HttpContext.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
- context.Result = new JsonResult("Unauthorized");
- return Task.FromResult < AuthorizationFilterContext > (context);
- }
- return Task.CompletedTask;
- }
- }
Once you have written Filter you have injected in startup class or else it won't execute.
- services.AddMvc(config =>
- {
- config.Filters.Add<AuthorizationFilterApply>();
- });
To apply Authorization we have specified the attribute on all our controller methods as below,
- [Authorize]
- [ApiVersion("1.0")]
- [Produces("application/json")]
- [Route("api/v{api-version:apiVersion}/[controller]")]
- public class SampleController : Controller
- {
- }
We have completed B2C implementation nice, but here we have to test the API and we have to send a token in every request. But we are not sending a token in each request, and I will tell you how to do it.
Here Swagger will come into play, it will help us to set authorization globally rather than sending each request.
To Integrate Swagger into the Web API, please install the below package from package manager console
- PM > install-package Swashbuckle.AspNetCore
and initialize:
- public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApiVersionDescriptionProvider provider)
-
- app.UseSwagger();
Once you install this package here is a small piece of code to authorization integration.
- services.AddSwaggerGen(options => {
- options.AddSecurityDefinition("Bearer", new ApiKeyScheme {
- In = "header",
- Description = "JWT Authorization header using the Bearer scheme.Example: \\\"Authorization: Bearer {token}\\\"\" <h4 style='color:#49cc90'>You can get your token from <a href='azureloginurl' target='_blank'>here</a></h4>",
- Name = "Authorization",
- Type = "apiKey"
- });
- options.AddSecurityRequirement(new Dictionary < string, IEnumerable < string >> {
- {
- "Bearer",
- Enumerable.Empty < string > ()
- },
- });
- });
After your run the application , it will look like this with open locks (Not Authorized).
So it will enable the Authorize button on the left side corner, and when you click it will display a popup like this,
If you have a token, enter the token and click authorize, and it will authorize all your API's at a single go.
If you don't have a token please click the "here" link and it will take you through the generating token.
If you can compare the first image and last one, you will be amazed (just kidding). In the first image all locks are open which means it is not authorized; and in this image the locks are locked, which means all API's are authorized, that's it :)
This is how we will integrate Azure B2C in Swagger for globally authorizing all our API's instead of sending each and every request.
I hope you like this post, please comment in case you're looking for anything more.