In today's post of ASP.NET Core 2.0 Authentication series, I am going to discuss about Azure Active Directory B2C, a service provided by Microsoft Azure for identity access and management.
In previous post, I talked about Azure Active Directory and how useful it is in corporate scenarios, however B2C is a better option for external user access and identity management, with powerful features such as support for various identity providers, policies and many more.
I will first briefly discuss about Azure AD B2C, what is it and how it is different from standard Azure AD. Then I am going to show you how to setup a tenant on Azure Portal, how to configure an identity provider, GitHub in this scenario, and how to setup policies. In the last part, I will show you how to configure your application to authenticate users using Azure AD B2C and how to handle failure events.
Source Code
Code outlined in this article can be found on my GitHub repository.
What is Azure Active Directory B2C
Azure Active Directory B2C is a cloud identity service, powered by Microsoft Azure, and build on top of Azure Active Directory. The fact that's build on top of Azure AD means that it share its traits, being a highly secure, scalable and reliable cloud identity platform.
Azure AD B2C is pretty much regular Azure Active Directory but compared to latter, which it is intended to be used in corporate scenarios, this is build to be used with external users, something that makes it a very interesting option when it comes to choose a third party provider for identity management.
Like Azure Active Directory, it is using the Open ID Connect protocol, though it is not limited in that option only, it supports other open standards as well, but it also supports many other features. Such feature is the ability to configure third party providers to be used as well, including Facebook, Twitter, Google, GitHub and many other among them.
Another key feature is the policy configuration. A policy is a configuration of a feature of the Active Directory that we want to use. Such feature might be the sign-in or sign-up feature. With this in mind, a sign-in policy contains all the configuration that is used during sign-in, same goes for sign-up and other policies. You can configure things like claims to be included in the token or which providers are supported as well in a given policy, extended token configuration, multifactor authentication support, even extended page customization.
Be advised that each Active Directory feature requires a policy to be set, else it will not work. I will setup some policies later in this post to demonstrate this.
How is it different from standard Azure Active Directory
Aside from the technical details I mentioned earlier, the main difference between these two is the use case, I mean where should one use each of these options? When is it appropriate to choose one over the other?
Azure Active Directory is intended for corporate scenarios to provide a SSO service for organizations and a central place to manage users within the organization. It's better suited on internal applications, such as integrating with Office 365 or other custom internal-used applications.
Azure Active Directory B2C though fits better on public facing applications which deal with external users. This service is actually tailored for this purpose, with build-in features I mentioned earlier (social accounts, support on various open standards, etc.)
Azure Portal
To follow along, you need to have a Microsoft Azure subscription, in order to setup your Azure Active Directory tenant.If you don’t have, you can use the free trial which provides €170 credit to spend for 30 days and 12-month free access to certain Azure services. I am going to use the free version of Azure AD anyways. I will create a new directory from scratch in following example.
Tenant setup
In your Microsoft Azure portal dashboard, look for the B2C resource and create a new Azure AD B2C tenant. I named my new directory TWC AD B2C Demo and the domain name for that directory is twcadb2cdemo. It automatically takes the postfix of onmicrosoft.com. It will take up to a minute for the new tenant to be created.
As soon as it is ready, switch to that directory.
For better accessibility, I suggest to pin the Azure AD B2C resource on your dashboard.
Application setup
Next step is to create a registered application for this service, so it will be able to allow the application to authenticate its users. Click on the Applications option on the left hand side, under Manage.
Then click on the + Add button to create your application. I named my application TWC Demo App and it is a web app, so chooseYes from the option below. Regarding the reply URLs, let's for the moment add the root URL and I will come back later to change it. I will later add new reply URLs for each policy.
Policies setup
When looking on the Azure AD B2C resource panel, you might notice the policies on the left hand side. These are the policies we're going to configure for the demo application. Namely, we'll configure the Sign-up, Sign-in, Sign-up-in and Profile editing policies.
Sign up
First, create the Sign-up policy, and as the policy name suggests, this one is for configuring user's sign-up.
Click on the Sign-up policies menu option on the left, then click on the + Add button. I will name this policy Demo_sign_up and Azure will add the prefix B2C_1, so the full name will be B2C_1_Demo_sign_up, which is going to be used later for the reply URL, as you'll to see in a bit.
I now need to select the identity providers which are going to be used by the policy. At the moment, I don't have any other provider created, so the only option will be Email signup, so choose that one for now. I will revisit this policy later to add a GitHub identity provider.
Next up is the Sign-up attributes. This is a list of attributes that we'd like to be collected during sign up. This is optional of course, but in this case I will select some attributes that I want the user to fill-in during his/her sign up, which will be the Display name, Email Address, Given Name and Surname.
Finally, I would like to setup the application claims. By this option, we configure which claims we'd like the id token to include after a successful authentication. These claims are going to be visible in the Profile page that I am going to create in my simple web application, which is just listing all claims for an authenticated user.
I choose the same attributes and also the Identity Provider and User is new claims.
Policy is ready, click create to proceed. The unique name of the policy is B2C_1_Demo_sign_up.
Let's quickly add this to the application's reply URLs. Go to the application created earlier and will add this as a reply URL. The full URL is https://localhosts:44379/signin/B2C_1_Demo_sign_up
.
Click save to update the application configuration.
Following the same procedure, I am going to create the rest of the policies.
Sign in
For the sign-in policy, click on the Sign-in policies option and create a new with the name Demo_sign_in. Again, select the email provider and the same as sign-up policy for application claims (Display name, Email Address, Given Name, Identity Provider, Surname).
The policy unique name is going to be B2C_1_Demo_sign_in. In the same fashion, add this reply URL to the application's reply URLs.
Sign up in
The sign-up and sign-in policies are already defined. What is this sign-up-in policy about? Well, this can be explained better with code, but to put it simply, when you define the authentication schemes for the application, each policy will define its authentication scheme for the challenge. If I define sign-in as default challenge, what happens when I want to sign-up? The challenge will be incorrect, same the other way around. The system does not know what to pick. If you tried to access a protected resource as an anonymous user, you will receive an error (assuming that you haven't setup a default challenge scheme due to the reason explained earlier).
Using this policy as default challenge scheme, I let the system redirect me to a screen where I can choose to either sign in or sign up, if not already registered. So, rule of thumb is to use this policy as the default challenge scheme, while for sign-in or sign-up I have defined their own dedicated schemes.
I will name this policy Demo_sign_up_in, choosing the default email identity provider, the same sign-up attributes and application claims I chose on the sign-up policy.
Let's add the B2C_1_Demo_sign_up_in policy to the list of the reply URLs.
Profile
Last policy I am about to configure is the profile editing policy. This policy allows a user to edit his/her profile, of course based on the allowed attributes defined in configuration. I will name this policy Demo_profile. The identity provider remains the same and the application claims will be again the same as previously defined for this policy (Display Name, Email Address, Given Name, Identity Provider, Surname).
I want also to select the profile attributes that the user is allowed to modify during profile edit. I will allow Display Name, Given Name and Surname for editing.
And finally, add this policy, B2C_1_Demo_profile, to the list of reply URLs.
Great! With this last modification all the reply URLs are ready and we're almost finished with configuration in Azure B2C. The only thing left is to setup a third party identity provider, and for this one I have chosen GitHub.
Identity provider setup
Let's first create a new OAuth application on GitHub. Go tohttps://github.com/settings/developers, and create a New OAuth App.
The details to fill are trivial except of the last one, which is the Authorization callback URL. This is super important! As you see below, the callback URL is not the application's URL, rather uses a predefined URL that includes the Azure AD B2C tenant directory. It has the form:https://login.microsoftonline.com/te/<TENANT_DIRECTORY>/oauth2/authresp
That's it, return back to the Azure portal and register this identity provider. Under Applications, choose Identity providers option. Click the + Add button to create a new Identity provider.
The provider's name will be TWC Demo GitHub and choose GitHub (Preview) from the list of social identity providers.
After selecting GitHub, you'll be prompted to enter the Client Id and Client Secret, which can be found in the OAuth App, created earlier on GitHub.
Finally, hit the Create button and the Identity Provider is ready to use. We need to visit each of the policies and include this provider to the list of available identity providers, so visit sign-in, sign-up, sign-up-in and profile policies, edit them and add the TWC Demo GitHub provider.
After including TWC Demo GitHub to all of our listed policies, the configuration part is finished, and it's code time!
Application
In this demo, I will create a new ASP.NET Core 2.0 MVC application and I will start configuring the code required to secure protected resources listed in this app.
In order to get the complete code for this post, please visit my GitHub repository.
I have already build the tenant and I will use configuration from that tenant in my application.
I will start by registering the authentication middleware in my application and then register each policy using the OIDC protocol. In order to keep the user signed-in, I have to issue a locally authenticated cookie. Azure AD B2C work is to only authenticate the user, while app's responsibility is to keep him/her signed-in.
For each policy, I will create an endpoint which only job is to send a challenge over to Azure AD B2C. I have to create also a sign-out endpoint, which should destroy the local cookie and close the session in Azure AD as well.
This application is going to have a single protected resource, which is going to be the /profile
page, where all claims for the authenticated user are listed in screen.
Setup
First things first, remember I've setup 4 policies already, so I will list them in static class in order to use their names when needed.
Also this application needs to run using HTTPS, so I have configured it to use HTTPS under port 44379
. In ASP.NET Core 2.1 you won't need this kind of configuration, it is provided by default.
Let's now configure the policies by starting with the sign-up policy.
Sign-up
In ConfigureServices
I will first start by calling the AddAuthentication
method and then the AddOpenIdConnect
to setup the sign-up policy. Azure AD B2C supports the OIDC protocol, thus the call to this method. Finally, a call to the AddCookie
method is required in order to setup a local authentication cookie.
For the DefaultSignInScheme
and DefaultAuthenticationScheme
, I choose cookie authentication, as I want to use a cookie for signing in users and authenticating incoming requests. See my previous post on local logins for more information.
Let's look at the Open Id Connect options:
- MetadataAddress. This is the URL of the policy (in this case for the sign-up policy). If you go back at the Azure portal and visit one of the policies, you will see that it comes with a URL on top. This URL is the same across all policies, the only difference is on the query string
p
value, which takes the name of the policy, in this case B2C_1_Demo_sign_up.
- ClientId. This is the application Id that is generated for the application defined as TWC Demo App.
- ResponseType. This defines the response that the application awaits, in this case I want an OIDC identity token back.
- CallbackPath. This option defines where the Azure AD B2C should redirect the user after authentication. That path needs to be available in the application's reply URL list. This path is handled by the OIDC authentication handler.
- SignedOutCallbackPath. This option tells the Azure AD B2C to redirect the user to that address after signing out. This path is handled by the OIDC authentication handler.
- SignedOutRedirectUri. This option tells the system to redirect the user to option defined at the end of the sign-up process. In this case, it will redirect the user to the application's home page.
- TokenValidationParameters.NameClaimType. This option sets the name claim type to "name" claim, which is the DisplayName claim in this case.
Once again, the AddOpenIdConnect
call takes care of the authentication part. The call to the AddCookie
will take care of the user sign-in for the application.
Sign-up policy is now configured, I will create a controller which contains a SignUp
action for the user to call. Notice that I don't need to do anything else rather return a Challenge. The RedirectUri
is there to protect from endless redirections. I also provide the authentication scheme name, which is the same as the policy name, as second argument.
Sign-up is finished, three more to go.
Sign-in
This is a different policy, so I have to chain another call to AddOpenIdConnect
method and provide the same options, with the only difference being the policy name. So, I have extracted a method for setting up Open Id Connect options which only varies the policy name.
And now I chain the calls.
Now I need to create a sign-in endpoint, which sends a challenge back to Azure AD B2C. In AuthController
, I've added a SignIn
action. It follows the same pattern as the previously defined SignUp
action.
Sign-up-in
In HomeController
, I have added a protected resource, which is the Profile
action. It just lists all claims for an authenticated user. In order to access /profile
, one must be authenticated.
What happens if I try to access this protected resource while in anonymous mode? If I do that I will receive an exception, like the following:
This happens because I haven't set up yet the DefaultChallengeScheme
, so the system doesn't know what challenge scheme to pick up. This is where the sign-up-in policy comes into play. I will setup this policy as the default challenge scheme and register an OIDC handler for this as well.
The code in ConfigureServices
now looks like this:
I don't need to set anything else, if I try to access /profile
now, I will be redirected to a login screen, which contains also a link to sign-up.
Profile editing is the last piece in the puzzle.
Profile
Using the profile editing policy, the authenticated user will be able to edit his/her profile. You might be wondering though, do I need an Edit screen of some sort? The answer is no! Azure AD B2C already provides an edit screen with the attributes you selected, listed and ready to be edited. But in case you don't like the default layout, you are free to create a custom edit screen and use this instead. For the purposes of this post, we'll stick with the defaults.
By now, you should already know what's the next step, and you guessed correctly, we'll need to chain another call to the AddOpenIdConnect
method, to register the OIDC handler for the profile editing policy.
And now, I need to add an endpoint which returns a challenge for the profile policy scheme. I create a new action called Profile
, in AuthController
. This action returns a Challenge
response and the RedirectUri
property ensures that the user will be either redirected back from where he was in the application or if there is no returnUrl
, back to home page.
Now the user needs a link to this action in order to kick-off profile editing. In the Profile.cshtml
view, I've added a link to this action.
I am almost ready, but the application misses a vital feature, which is the ability to sign out. Let's work on that next.
Sign Out
In previous posts I have mentioned that when you define a sign out action, it should be a POST action rather GET. Reason behind this, is that Chrome pre-fetches GET requests to speed up browsing. This would result in a premature sign out for the user, with you not even noticing! I usually recommend to have a sign out screen or dialog, where the user is prompted to respond if he/she is truly willing to sign out, and a POST action, where the actual sign out happens. Let's take a look in the POST sign out action.
As usual, I first sign out from the local cookie authentication session and as a next step I sign out from the Azure AD B2C Open Id Connect authentication session. It is important to do both, you don't want to leave the OIDC session open.
Also, notice something unusual, I get the value of the "tfp
" claim, which is an authentication scheme, and I sign out using that scheme. Why do that when I can just call the HttpContext.SignOutAsync()
method? The problem is that this method needs to receive an authentication scheme to sign out from. But in our case here, I have more than one policies, I have a sign-up, a sign-in, a profile policy and so on and so forth. Azure AD B2C makes my life easier, by including a claim in the authentication token that contains the name of my policy. By getting this policy name, I can be sure that I sign out from the correct policy. This is the reason I used the same policy name as the authentication scheme for each policy that I registered in the ConfigureServices
method.
Failure events
Let's go back in the profile editing policy.
There is a slight problem with configuration for this one. If the user navigates to the profile editing page and decides to cancel the action, an exception will be thrown, like the following:
Don't worry, there is not something wrong with our code. The problem is that there is no handler for the cancellation scenario and cancelling this action results in a remote failure. So, you can handle this by registering for the OnRemoteFailure
event and redirect back to your application. Let's see that in code:
So, I create a new OpenIdConnectEvents
object and setup a listener on the OnRemoteFailure
event. When that event occurs, I redirect the user back to the home page, handling the response manually. I could do other things like log it or whatnot, but this is a simple demo and I intend to keep it that way.
In fact, I would like to handle this event for the other policies as well, so I will create a local variable events
, which is an instance of OpenIdConnectEvents
, handling the OnRemoteFailure
event. Then, I assign this to the Events
property of options parameter, in AddOpenIdConnect
method.
Putting all these together...
Testing sign-in with GitHub social identity provider
Testing sign-in-out and profile editing with GitHub social identity provider
Summary
This was a a post with lots of information about Azure AD B2C on how to simply configure your tenant, setup a web application and manage identity for that app.
By the end of this post you learned about the differences of Azure AD and Azure AD B2C and in which scenarios each is appropriate to use. I also went through the Azure Portal and showed you how to create a new Azure AD B2C resource, register your application, setup policies and even third party social identity providers. The key thing to remember is to list each policy that you have in the reply URLs for the registered application, else nothing will work.
After exploring Azure Portal and successfully configuring the Azure AD Tenant, I moved to Visual Studio and created a new ASP.NET Core 2.0 MVC web application. I configured each policy separately and incrementally shaped my code. I started first by defining the sign-up policy and created a controller which could issue a challenge against Azure AD B2C for a user to sign-up. I continued with rest of the policies, sign-in, sign-up-in and profile editing. The catch with the sign-up-in is to use this policy as the default challenge scheme in order to properly challenge anonymous users. Finally, I had to handle some OpenIdConnectProtocol
exceptions which generated by cancelling actions on profile, sign-up and similar policies, so for that reason, I showed you how to handle gracefully such errors via the OnRemoteFailure
event. You should also be careful in the sign out action, be sure to sign out the local authentication cookie and also the Open Id Connect session.
If you liked this blog, please like and share! For more, follow me on Twitter.
This post is part of the ASP.NET Core 2.0 Authentication series.
- ASP.NET Core 2.0 Cookie Authentication - Local logins
- ASP.NET Core 2.0 Authentication with local logins - Implementing claims transformation
- ASP.NET Core 2.0 Authentication with local logins - Responding to backend changes
- ASP.NET Core 2.0 Authentication with local logins - Implementing custom authorization policies
- ASP.NET Core 2.1 Authentication with social logins
- ASP.NET Core 2.0 Authentication with social logins - Implementing a profile store
- ASP.NET Core 2.0 Authentication with Azure Active Directory
- ASP.NET Core 2.0 Authentication with Azure Active Directory B2C
- ASP.NET Core 2.0 Authentication, IdentityServer4 and Angular
Comments powered by Disqus.