Bit of Technology

  • Archive
  • About Me
    • Advertise
    • Disclaimer
  • Speaking
  • Contact

Integrate Azure AD B2C with ASP.NET MVC Web App – Part 3

August 31, 2016 By Taiseer Joudeh 24 Comments

Be Sociable, Share!

  • Tweet
  • Email
  • WhatsApp

This is the third part of the tutorial which will cover Using Azure AD B2C tenant with ASP.NET Web API 2 and various front-end clients.

  • Azure Active Directory B2C Overview and Policies Management – (Part 1)
  • Secure ASP.NET Web API 2 using Azure AD B2C – (Part 2)
  • Integrate Azure Active Directory B2C with ASP.NET MVC Web App (This Post)
  • Secure Desktop Application using Microsoft Authentication Library (MSAL) and Azure Active Directory B2C (Part 4)

The source code for this tutorial is available on GitHub.

The MVC Web App has been published on Azure App Services, so feel free to try it out using the Base URL (https://aadb2cmvcapp.azurewebsites.net/)

I promise you that I won’t share your information with anyone, feel free to try the experience 🙂

Integrate Azure AD B2C with ASP.NET MVC Web App

In the previous post, we have configured our Web API to rely on our Azure AD B2C IdP to secure it so only calls which contain a token issued by our IdP will be accepted by our Web API.

In this post we will build our first front-end application (ASP.NET MVC 5 Web App) which will consume the API endpoints by sending a valid token obtained from the Azure AD b2C tenant, as well it will allow anonymous users to create profiles, and sign in against the Azure B2C tenant. The MVC Web app itself will be protected as well by the same Azure AD B2C tenant as we will share the same tenant Id between the Web API and MVC Web app.

So let’s start building the MVC Web App.

Step 1: Creating the MVC Web App Project

Let’s add a new ASP.NET Web application named “AADB2C.WebClientMvc” to the solution named “WebApiAzureAcitveDirectoryB2C.sln”, then add new MVC ASP.NET Web application, the selected template for the project will be “MVC”, and do not forget to change the “Authentication Mode” to “No Authentication” check the image below:

Azure B2C Web Mvc Template

Once the project has been created, click on it’s properties and set “SSL Enabled” to “True”, copy the “SSL URL” value and right lick on project, select “Properties”, then select the “Web” tab from the left side and paste the “SSL URL” value in the “Project Url” text field and click “Save”. We need to allow https scheme locally once we debug the application. Check the image below:

MvcWebSSLEnable

Step 2: Install the needed NuGet Packages to Configure the MVC App

We need to add bunch of NuGet packages, so Open NuGet Package Manager Console and install the below packages:

1
2
3
4
Install-Package Microsoft.Owin.Security.OpenIdConnect -Version 3.0.1
Install-Package Microsoft.Owin.Security.Cookies -Version 3.0.1
Install-Package Microsoft.Owin.Host.SystemWeb -Version 3.0.1
Update-package Microsoft.IdentityModel.Protocol.Extensions

The package “Microsoft.Owin.Security.OpenIdConnect” contains the middleware used to protect web apps with OpenId Connect, this package contains the logic for the heavy lifting happens when our MVC App will talk with Azure B2C tenant to request tokens and validate them.

The package “Microsoft.IdentityModel.Protocol.Extension” contains classes which represent OpenID Connect constants and messages, lastly the package “Microsoft.Owin.Security.Cookies” will be used to create a cookie based session after obtaining a valid token from our Azure AD B2C tenant. This cookie will be sent from the browser to the server with each subsequent request and get validate by the cookie middleware.

Step 3: Configure Web App to use Azure AD B2C tenant IDs and Policies

Now we need to modify the web.config for our MVC App  by adding the below keys, so open Web.config and add the below AppSettings keys:

1
2
3
4
5
6
7
8
    <add key="ida:Tenant" value="BitofTechDemo.onmicrosoft.com" />
    <add key="ida:ClientId" value="bc348057-3c44-42fc-b4df-7ef14b926b78" />
    <add key="ida:AadInstance" value="https://login.microsoftonline.com/{0}/v2.0/.well-known/openid-configuration?p={1}" />
    <add key="ida:SignUpPolicyId" value="B2C_1_Signup" />
    <add key="ida:SignInPolicyId" value="B2C_1_Signin" />
    <add key="ida:UserProfilePolicyId" value="B2C_1_Editprofile" />
    <add key="ida:RedirectUri" value="https://localhost:44315/" />
    <add key="api:OrdersApiUrl" value="https://localhost:44339/" />

The usage for the each setting has been outlined in the previous post, the only 2 new settings keys are: “ida:RedirectUri” which will be used to set the OpenID connect “redirect_uri” property The value of this URI should be registered in Azure AD B2C tenant (we will do this next), this redirect URI will be used by the OpenID Connect middleware to return token responses or failures after authentication process, as well after the sign out process. The second setting key “api:OrdersApiUrl” will be used as a base URI for our Web API.

Now let’s register the new Redirect URI in Azure B2C tenant, to do so login to Azure Portal and navigate to the App “Bit of Tech Demo App” we already registered in the previous post, then add the value “https://localhost:44315/” in the Reply URL settings as the image below, note that I already published the MVC web App to Azure App Services to the URL (https://aadb2cmvcapp.azurewebsites.net/) so I’ve included this URL too.

B2C Mvc Reply URL

Step 4: Add Owin “Startup” Class

The default MVC template comes without a “Startup” class, but we need to configure our OWIN OpenID Connect middleware at the start of our Web App, so add a new class named “Startup” and paste the code below, there is a lot of code here so jump to the next paragraph as I will do my best to explain what we have included in this class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public class Startup
    {
        // App config settings
        private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
        private static string aadInstance = ConfigurationManager.AppSettings["ida:AadInstance"];
        private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
        private static string redirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];
 
        // B2C policy identifiers
        public static string SignUpPolicyId = ConfigurationManager.AppSettings["ida:SignUpPolicyId"];
        public static string SignInPolicyId = ConfigurationManager.AppSettings["ida:SignInPolicyId"];
        public static string ProfilePolicyId = ConfigurationManager.AppSettings["ida:UserProfilePolicyId"];
 
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
        }
 
        public void ConfigureAuth(IAppBuilder app)
        {
            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
 
            app.UseCookieAuthentication(new CookieAuthenticationOptions() );
 
            // Configure OpenID Connect middleware for each policy
            app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignUpPolicyId));
            app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(ProfilePolicyId));
            app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignInPolicyId));
        }
 
        // Used for avoiding yellow-screen-of-death
        private Task AuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        {
            notification.HandleResponse();
            if (notification.Exception.Message == "access_denied")
            {
                notification.Response.Redirect("/");
            }
            else
            {
                notification.Response.Redirect("/Home/Error?message=" + notification.Exception.Message);
            }
 
            return Task.FromResult(0);
        }
 
        private OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(string policy)
        {
            return new OpenIdConnectAuthenticationOptions
            {
                // For each policy, give OWIN the policy-specific metadata address, and
                // set the authentication type to the id of the policy
                MetadataAddress = String.Format(aadInstance, tenant, policy),
                AuthenticationType = policy,
              
                // These are standard OpenID Connect parameters, with values pulled from web.config
                ClientId = clientId,
                RedirectUri = redirectUri,
                PostLogoutRedirectUri = redirectUri,
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    AuthenticationFailed = AuthenticationFailed
                },
                Scope = "openid",
                ResponseType = "id_token",
 
                // This piece is optional - it is used for displaying the user's name in the navigation bar.
                TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name",
                    SaveSigninToken = true //important to save the token in boostrapcontext
                }
            };
        }
    }

What we have implemented here is the following:

  • From line 4-12 we have read the app settings for the keys we have included in MVC App web.config where they represent Azure AD B2C tenant and policy names, note that policy names access modifiers are set to public as it will be referenced in another class.
  • Inside the method “ConfigureAuth” we have done different things as the following:
    • Line  app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType) will configure the OWIN security pipeline and inform the OpenID connect middleware that the default authentication type we will use is”Cookies”, and this means that the “Claims” encoded in the token we will receive from Azure AD B2C tenant will be stored in a Cookie (Session for the authenticated user).
    • Line  app.UseCookieAuthentication(new CookieAuthenticationOptions()); will register a cookie authentication middleware instance with default options, this means that Authentication type here is equivalent to the same authentication type we set in the previous step. it will be “Cookies” too.
    • Lines  app.UseOpenIdConnectAuthentication are used to configure the OWIN security pipeline to use the authentication provider (Azure AD B2C) per policy, in our case, there will be 3 different policies we already defined.
  • The method  CreateOptionsFromPolicy will take the Policy name as input parameter and will return an object of type “OpenIdConnectAuthenticationOptions”, This object is responsible for controlling the OpenID Connect middleware. The properties we used to configure the instance of “OpenIdConnectAuthenticationOptions” as the below:
    • The MetadataAddress property will accept the address of the discovery document endpoint for our Azure AD B2C tenant per policy, so for example, the discovery endpoint for policy “B2C_1_Signup” will be “https://login.microsoftonline.com/BitofTechDemo.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1_Signup”. This discovery document will be used to get information from Azure AD B2C on how to generate authentication requests and validated incoming token responses.
    • The  AuthenticationType property will inform the middleware that authentication operation used is the policies we already defined, so for example if you defined a forth policy and you didn’t register it with the OpenID connect middleware, the tokens issues by this policy will be rejected.
    • The  ClientId property will tell Azure AD B2C which ID to use to match the requests originating from the Web App. This will represent the Azure AD B2C tenant we defined earlier in the previous posts.
    • The  RedirectUri property will inform the Azure AD B2C where your app wants the requested token response to be returned to, the value of this URL should be registered previously in the “ReplyURLs” values in Azure AD B2C App we defined earlier.
    • The  PostLogoutRedirectUri property will inform Azure AD B2C where to redirect the browser after a sign out operation completed successfully.
    • The  Scope property will be used to inform our Azure AD B2C tenant that our web app needs to use “OpenId Connect” protocol for authentication.
    • The  ResponseType property will indicate what our Web App needs from Azure AD B2C tenant after this authentication process, in our case, we only need an  id_token
    • The  TokenValidationParameters is used to store the information needed to validate the tokens, we only need to change 2 settings here, the  NameClaimType and the  SaveSigninToken . Setting the “NameClaimType” value to “name” will allow us to read the display name of the user by calling  User.Identity.Name , and setting the “SaveSigninToken” to “true” will allow us to save the token we received from the authentication process in the claims created (Inside the session cookie), this will be useful to retrieve the token from the claims when we want to call the Web API. Keep in mind that the cookie size will get larger as we are storing the token inside it.
    • Lastly, the property  Notifications will allow us to inject our custom code during certain phases of the authentication process, the phase we are interested in here is the  AuthenticationFailed phase, in this phase we want to redirect the user to the root directory of the Web App in case he/she clicked cancel on the sign on or sign in forms, and we need to redirect to the error view if we received any other exception during the authentication process.

This was the most complicated part in configuring our Web App to use our Azure AD B2C tenant. Now the next steps should be simpler and we will modify some views and add some new actions to issue requests to our Web API and call the Azure AD B2C polices.

Step 5: Call the Azure B2C Polices

Now we need to configure out Web App to invoke the policies we created, to do so we need to add a new controller named “AccountController”, so add it and paste the code below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    public class AccountController : Controller
    {
        public void SignIn()
        {
            if (!Request.IsAuthenticated)
            {
                // To execute a policy, you simply need to trigger an OWIN challenge.
                // You can indicate which policy to use by specifying the policy id as the AuthenticationType
                HttpContext.GetOwinContext().Authentication.Challenge(
                    new AuthenticationProperties() { RedirectUri = "/" }, Startup.SignInPolicyId);
            }
        }
 
        public void SignUp()
        {
            if (!Request.IsAuthenticated)
            {
                HttpContext.GetOwinContext().Authentication.Challenge(
                    new AuthenticationProperties() { RedirectUri = "/" }, Startup.SignUpPolicyId);
            }
        }
 
        public void Profile()
        {
            if (Request.IsAuthenticated)
            {
                HttpContext.GetOwinContext().Authentication.Challenge(
                    new AuthenticationProperties() { RedirectUri = "/" }, Startup.ProfilePolicyId);
            }
        }
 
        public void SignOut()
        {
            // To sign out the user, you should issue an OpenIDConnect sign out request
            if (Request.IsAuthenticated)
            {
                IEnumerable<AuthenticationDescription> authTypes = HttpContext.GetOwinContext().Authentication.GetAuthenticationTypes();
                HttpContext.GetOwinContext().Authentication.SignOut(authTypes.Select(t => t.AuthenticationType).ToArray());
            }
        }
    }

What we have implemented here is simple, and it is the same for actions  SignIn ,  SignUp , and  Profile , what we have done is a call to the  Challenge method and specify the related Policy name for each action.

The “Challenge” method in the OWIN pipeline accepts an instance of the object AuthenticationProperties()  which is used to set the settings of the action we want to do (Sign in, Sign up, Edit Profile). We only set the “RedirectUri” here to the root path of our Web App, taking into consideration that this “RedirectUri” has nothing to do with the “RedirectUri” we have defined in Azure AD B2C. This can be a different URI where you want the browser to redirect the user only after a successful operation takes place.

Regarding the  SignOut action, we need to Signout the user from different places, one by removing the app local session we created using the “Cookies” authentication and the other one by informing the OpenID connect middleware to send a Sign out request message to our Azure AD B2C tenant so the user is signed out from there too, that’s why we are retrieving all the Auth types available for our Web App and then we pass those different authentication types to the the “SignOut” method.

Now let’s add a partial view which renders the links to call those actions, so add a new partial view named “_LoginPartial.cshtml” under the “Shared” folder and paste the code below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@if (Request.IsAuthenticated)
{
    <text>
        <ul class="nav navbar-nav navbar-right">
            <li>
                <a id="profile-link">@User.Identity.Name</a>
                <div id="profile-options" class="nav navbar-nav navbar-right">
                    <ul class="profile-links">
                        <li class="profile-link">
                            @Html.ActionLink("Edit Profile", "Profile", "Account")
                        </li>
                    </ul>
                </div>
            </li>
            <li>
                @Html.ActionLink("Sign out", "SignOut", "Account")
            </li>
        </ul>
    </text>
}
else
{
    <ul class="nav navbar-nav navbar-right">
        <li>@Html.ActionLink("Sign up", "SignUp", "Account", routeValues: null, htmlAttributes: new { id = "signUpLink" })</li>
        <li>@Html.ActionLink("Sign in", "SignIn", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
    </ul>
}

Notice that part of partial view will be rendered only if the user is authenticated and notice how we are displaying the user “Display Name” from the claim named “name” by only calling  @User.Identity.Name

Now we need to reference this partial view in the “_Layout.cshtml” view, we need just to replace the last Div in the body section with the below section:

1
2
3
4
5
6
7
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@Html.ActionLink("Home", "Index", "Home")</li>
<li>@Html.ActionLink("Orders List", "Index", "Orders")</li>
</ul>
@Html.Partial("_LoginPartial")
</div>

Step 6: Call the Web API from the MVC App

Now we want to add actions to start invoking the protected API we’ve created by passing the token obtained from Azure AD B2C tenant in the “Authorization” header for each protected request. We will add support for creating a new order and listing all the orders related to the authenticated user. If you recall from the previous post, we will depend on the claim named “objectidentifer” to read the User ID value encoded in the token as a claim.

To do so we will add a new controller named “OrdersController” under folder “Controllers” and will add 2 actions methods named “Index” and “Create”, add the file and paste the code below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
    [Authorize]
    public class OrdersController : Controller
    {
        private static string serviceUrl = ConfigurationManager.AppSettings["api:OrdersApiUrl"];
 
        // GET: Orders
        public async Task<ActionResult> Index()
        {
            try
            {
 
                var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as System.IdentityModel.Tokens.BootstrapContext;
 
                HttpClient client = new HttpClient();
 
                client.BaseAddress = new Uri(serviceUrl);
 
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bootstrapContext.Token);
 
                HttpResponseMessage response = await client.GetAsync("api/orders");
 
                if (response.IsSuccessStatusCode)
                {
 
                    var orders = await response.Content.ReadAsAsync<List<OrderModel>>();
 
                    return View(orders);
                }
                else
                {
                    // If the call failed with access denied, show the user an error indicating they might need to sign-in again.
                    if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                    {
                        return new RedirectResult("/Error?message=Error: " + response.ReasonPhrase + " You might need to sign in again.");
                    }
                }
 
                return new RedirectResult("/Error?message=An Error Occurred Reading Orders List: " + response.StatusCode);
            }
            catch (Exception ex)
            {
                return new RedirectResult("/Error?message=An Error Occurred Reading Orders List: " + ex.Message);
            }
        }
 
        public ActionResult Create()
        {
            return View();
        }
 
        [HttpPost]
        public async Task<ActionResult> Create([Bind(Include = "ShipperName,ShipperCity")]OrderModel order)
        {
 
            try
            {
                var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as System.IdentityModel.Tokens.BootstrapContext;
 
                HttpClient client = new HttpClient();
 
                client.BaseAddress = new Uri(serviceUrl);
 
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bootstrapContext.Token);
 
                HttpResponseMessage response = await client.PostAsJsonAsync("api/orders", order);
 
                if (response.IsSuccessStatusCode)
                {
                    return RedirectToAction("Index");
                }
                else
                {
                    // If the call failed with access denied, show the user an error indicating they might need to sign-in again.
                    if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                    {
                        return new RedirectResult("/Error?message=Error: " + response.ReasonPhrase + " You might need to sign in again.");
                    }
                }
 
                return new RedirectResult("/Error?message=An Error Occurred Creating Order: " + response.StatusCode);
            }
            catch (Exception ex)
            {
                return new RedirectResult("/Error?message=An Error Occurred Creating Order: " + ex.Message);
            }
 
        }
 
    }
 
    public class OrderModel
    {
        public string OrderID { get; set; }
        [Display(Name = "Shipper")]
        public string ShipperName { get; set; }
        [Display(Name = "Shipper City")]
        public string ShipperCity { get; set; }
        public DateTimeOffset TS { get; set; }
    }

What we have implemented here is the following:

  • We have added an  [Authorize] attribute on the controller so any unauthenticated (anonymous) request (Session cookie doesn’t exist) to any of the actions in this controller will result into a redirect to the Sign in policy we have configured.
  • Notice how we are reading the  BootstrapContext from the current “ClaimsPrincipal” object, this context will contain a property named “Token” which we will send in the “Authorization” header for the Web API. Note that if you forgot to set the property “SaveSigninToken” of the “TokenValidationParameters” to “true” then this will return “null”.
  • We are using HTTP Client to craft the requests and call the Web API endpoints we defined earlier. There is no need to pay attention to the User ID property in the MVC App as this property is encoded in the token itself, and the Web API will take the responsibility to decode it and store it in the Azure table storage along with order information.

Step 7: Add views for the Orders Controller

I will not dive into details here, as you know we need to add 2 views to support rendering the list of orders and creating a new order, for sake of completeness I will paste the cshtml for each view, so open a new folder named “Orders” under “Views” folder, then add 2 new views named “Index.cshtml” and “Create.cshtml” and paste the code as the below:

Index.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@model IEnumerable<AADB2C.WebClientMvc.Controllers.OrderModel>
@{
    ViewBag.Title = "Orders";
}
<h2>Orders</h2>
<br />
<p>
    @Html.ActionLink("Create New", "Create")
</p>
 
<table class="table table-bordered table-striped table-hover table-condensed" style="table-layout: auto">
    <thead>
        <tr>
            <td>Order Id</td>
            <td>Shipper</td>
            <td>Shipper City</td>
            <td>Date</td>
        </tr>
    </thead>
    @foreach (var item in Model)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.OrderID)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ShipperName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ShipperCity)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.TS)
            </td>
        </tr>
    }
</table>

Create.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@model AADB2C.WebClientMvc.Controllers.OrderModel
@{
    ViewBag.Title = "New Order";
}
<h2>Create Order</h2>
@using (Html.BeginForm())
{
    <div class="form-horizontal">
        <hr />
 
        <div class="form-group">
            @Html.LabelFor(model => model.ShipperName, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.ShipperName, new { htmlAttributes = new { @class = "form-control" } })
            </div>
        </div>
 
        <div class="form-group">
            @Html.LabelFor(model => model.ShipperCity, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.ShipperCity, new { htmlAttributes = new { @class = "form-control" } })
            </div>
        </div>
 
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save Order" class="btn btn-default" />
            </div>
        </div>
    </div>
 
    <div>
        @Html.ActionLink("Back to Orders", "Index")
    </div>
}

Step 8: Lastly, let’s test out the complete flow

To test this out the user will click on “Orders List” link from the top navigation menu, then he will be redirected to the Azure AD B2C tenant where s/he can enter the app local credentials, if the crednetials provided are valid then a successful authentication will take place and a token will be obtained and stored in the claims identity for the authenticated user, then the orders view are displayed the token is sent in the authorization header to get all orders for this user. It should be something as the animated image below:

Azure AD B2C animation

That’s it for now folks, I hope you find it useful 🙂 In the next post, I will cover how to integrate MSAL with Azure AD B2C and use it in a desktop application. If you find the post useful; then do not forget to share it 🙂

The Source code for this tutorial is available on GitHub.

The MVC Web App has been published on Azure App Services, so feel free to try it out using the Base URL (https://aadb2cmvcapp.azurewebsites.net/)

Follow me on Twitter @tjoudeh

Resources

  • A primer on OWIN cookie authentication middleware by Brock Allen
  • Modern Authentication with Azure Active Directory for Web Applications by Vittorio Bertocci
  • Azure AD B2C: Call a web API from a .NET web app

Be Sociable, Share!

  • Tweet
  • Email
  • WhatsApp

Related Posts

  • Secure ASP.NET Web API 2 using Azure AD B2C – Part 2
  • Azure Active Directory B2C Overview and Policies Management – Part 1
  • ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 – Part 5
  • ASP.NET Identity 2.1 Accounts Confirmation, and Password Policy Configuration – Part 2
  • ASP.NET Identity 2.1 with ASP.NET Web API 2.2 (Accounts Management) – Part 1

Filed Under: ASP.Net Web API, Azure, Azure AD B2C, CodeProject, RESTful API, Web API Security Tagged With: Azure Active Directory B2C, Azure AD B2C, Token Authentication, Tutorial, Web API 2, Web API Security

Comments

  1. Janick Ross says

    September 14, 2016 at 7:17 pm

    is it possible to customize the ui of login page with our company UI to make a complete integration of our website ?

    Reply
    • Taiseer Joudeh says

      September 14, 2016 at 7:39 pm

      Hi Janick, sure thing you can take a look at the azure documentation where they have a full walk through on how to customize the Ui https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-reference-ui-customization/?cdn=disable

      Reply
  2. Behrooz Dalvandi says

    September 22, 2016 at 6:57 pm

    Would you please do a tutorial on implementing individual user accounts with social logins enabled for .Net Core Web Api apps? Preferably using Identity Server 4?

    Thanks

    Reply
    • Taiseer Joudeh says

      October 2, 2016 at 1:54 pm

      Thanks for the suggestion, I need to look at this soon.

      Reply
      • Behrooz says

        December 7, 2016 at 8:33 am

        Hi Taiseer,
        I have actually managed to write a 4 parts tutorial on securing ,Net Core Web APIs using IdentityServer4, here is the link to the first part:
        https://www.linkedin.com/pulse/securing-net-core-web-api-identityserver4-resource-owner-dalvandi?trk=mp-author-card

        If you think it is of any value, I would appreciate if you comment on it or even better, spread the word 🙂

        Thanks.

        Reply
        • Taiseer Joudeh says

          December 15, 2016 at 5:04 pm

          Behroz, sorry for the late reply. sure thing I will check it on this weekend. thanks for sharing this 🙂

          Reply
  3. Rafael says

    October 27, 2016 at 5:59 pm

    Hi,

    Very nice tutorial. However I cannot acces the order list, I get a redirection error. Is this suppose to work at this point?
    Looking forward to next one!

    Reply
    • Taiseer Joudeh says

      October 28, 2016 at 2:33 pm

      Hello Rafael,
      I just tried it from my end using the demo app https://aadb2cmvcapp.azurewebsites.net and it works as expected, what the exact error you are facing?

      Reply
  4. Lance says

    October 28, 2016 at 7:28 pm

    Thanks for this example.
    I am using on-premises ADFS and am having trouble signing out of my web application. I tried your code but the cookies are not consistently being deleted in the SignOut method. I think this should work the same as your azure example. For sure the SignOut method is not logging me out of ADFS since I can delete all cookies in the browser manually and when I go to my site and get redirected to ADFS, I get a new cookie without logging in.

    Reply
    • Taiseer Joudeh says

      November 3, 2016 at 7:20 pm

      That is strange Lance, I’m not experienced in ADFS so I can’t comment on this.

      Reply
  5. John Bigley says

    November 10, 2016 at 8:04 pm

    I find your posts very well written and thorough. Thank you! Can you do a post on SPA UIs? There is one out there now but it leaves out too much and does not cover calls to a Web API app.

    Reply
    • Taiseer Joudeh says

      November 12, 2016 at 12:13 am

      Hi John,
      I need to check what has been changed on Azure B2C as the pace for updates on Azure is very quick, sure thing I will consider this soon, I believe things would be easier with SPAs when MS release MSAL.

      Reply
  6. Dan says

    February 21, 2017 at 5:19 pm

    Hi!

    So I followed this model and everything was working fine – I was able to login with B2C credentials and secure my web app. All was well.

    For some reason, now, I am getting an authentication failure when I login- the exception being:

    IDX10500: Signature validation failed. Unable to resolve SecurityKeyIdentifier: ‘SecurityKeyIdentifier

    So I am able to get the token back and if I subclass my own JwtSecurityTokenHandler and use that as part of the handler collection, it fails in ResolveIssuerSigningKey(), it returns null instead of a security token.

    I have no idea what could have changed.

    Any ideas?

    Reply
    • Dan says

      February 22, 2017 at 12:06 am

      So I knew after I posted I would figure it out.

      It had to do with the metadata endpoint in the token parameters. I had removed the ?p={policy} from the querystring and added it instead in the RedirectToIdentityProvider notification of the OpenIdConnectAuthenticationOptions. Well, apparently, somewhere in the vastness of the middleware. it uses that MetadataAddress string to retrieve the security keys and doesn’t respect that which is returned by RedirectToIdentityProvider.

      Reply
      • Taiseer Joudeh says

        March 5, 2017 at 2:55 am

        Thanks for sharing this update Dan, when I wrote the post, B2C was in Preview, so things might change right now. Happy to hear you find a solution for the issue.

        Reply
  7. Ambareesh Ps says

    March 8, 2017 at 9:00 pm

    Thanks. Very good article.
    I have a query. Say, I have an Web APP registered under Azure AD B2C and protected. How I can enable SSO for on-premise users whose user ID are on premise AD. Say an on premise user logged into his workstation (within on premise network) with his user ID. In a later point in time, he is trying to access the Web App which is registered under Azure AD B2C. He should not be prompted to key in the user id and Password. He should be able to access the Web APP with his already logged in ID. What are the option to achieve this single sign on.
    Thanks and Regards
    Ambareesh

    Reply
    • Taiseer Joudeh says

      March 11, 2017 at 1:06 pm

      Hello Ambareesh,
      Thanks for your comment, if I understood your question correctly, I do not believe this doable, you have a missing requirement here, so let me ask you, is there any relation between on-premise AD and Azure AD B2C? Well, I believe there is nothing, so how the identity of the on-prem user will be used in Azure B2C?
      If I didn’t get your correctly please elaborate more to check this issue.

      Reply
      • Ambareesh says

        March 11, 2017 at 3:48 pm

        Hello Taiseer,
        Thank you for your time in replying to my query. The requirement is as below.
        I have a Web APP and Web API hosted on Azure protected by Azure AD B2C. If that same application (Web APP and Web API) has to be accessed by users who are in another AD, what are the options? Is that the application has to be registered in that AD as well? Can that same application accepts token from different Identity provider’s?
        With Best Regards
        Ambareesh

        Reply
  8. Bas says

    March 19, 2017 at 11:57 pm

    When you use a custom domain and get an error about different root domains check this: http://stackoverflow.com/questions/34123640/azure-b2c-multiple-reply-url

    Just add the root level first, than the sub domains.

    Reply
  9. Arun says

    March 16, 2018 at 10:01 am

    Can you please make a video tutorials on the client-server binding using Angular/React? I’m an Angular trainer at Kamal Technologies, but I would love to bind it with Azure at the backend via ASP.NET.

    Thanks a lot in advance

    Reply
  10. Bhaskar says

    July 17, 2019 at 12:45 pm

    Hi Taiseer Joudeh,

    Could you please assist me or let me know how to implement role based authorization in Azure ADB2C application. We are struggling in the implementation of role based authorization in Azure Active Directory B2C

    Regards,
    Bhaskar

    Reply
  11. manjerekarrao says

    November 18, 2019 at 5:30 pm

    when I hit this url https://nlagb2capp.azurewebsites.net/ in the browser, its redirecting me to https://localhost:44307/signin-oidc and throws can’t reach this page error.

    //I’ve set the reply url in this order:
    https://nlagb2capp.azurewebsites.net/
    https://localhost:44307/signin-oidc

    //my launchsettings .json file contents

    “iisSettings”: {
    “windowsAuthentication”: false,
    “anonymousAuthentication”: true,
    “iisExpress”: {
    “applicationUrl”: “https://localhost:44307”,
    “sslPort”: 44307
    }
    },

    //my b2c settings
    “nlagb2c”: {
    “commandName”: “Project”,
    “launchBrowser”: true,
    “environmentVariables”: {
    “ASPNETCORE_ENVIRONMENT”: “Development”
    },
    “applicationUrl”: “https://nlagb2capp.azurewebsites.net/;http://nlagb2capp.azurewebsites.net/”
    },

    // my appsettings.json file content

    {
    “AzureAdB2C”: {
    “Instance”: “**************”,
    “ClientId”: “**************”,
    “CallbackPath”: “/signin-oidc”,
    “Domain”: “**************”,
    “SignUpSignInPolicyId”: “B2C_1_nlagsusi”,
    “ResetPasswordPolicyId”: “B2C_1_nlagpwdreset”,
    “EditProfilePolicyId”: “B2C_1_nlagprofileedit”
    },

    Reply
  12. Narendra says

    January 28, 2020 at 2:19 am

    My Request.IsAuthenticated is always false….
    Need help please.

    Reply
    • Narendra says

      March 24, 2020 at 3:43 am

      I have added Session variables in HomeController, just before signin and signout. Once I have removed the session variables, everything works perfectly fine.

      Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

About Taiseer

Husband, Father, Consultant @ MSFT, Life Time Learner... Read More…

Buy me a coffeeBuy me a coffee

Recent Posts

  • Integrate Azure AD B2C with ASP.NET MVC Web App – Part 3
  • Secure ASP.NET Web API 2 using Azure AD B2C – Part 2
  • Azure Active Directory B2C Overview and Policies Management – Part 1
  • ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 – Part 5
  • ASP.NET Identity 2.1 Roles Based Authorization with ASP.NET Web API – Part 4

Blog Archives

Recent Posts

  • Integrate Azure AD B2C with ASP.NET MVC Web App – Part 3
  • Secure ASP.NET Web API 2 using Azure AD B2C – Part 2
  • Azure Active Directory B2C Overview and Policies Management – Part 1
  • ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 – Part 5
  • ASP.NET Identity 2.1 Roles Based Authorization with ASP.NET Web API – Part 4

Tags

AJAX AngularJS API API Versioning ASP.NET Authentication Autherization Server Azure Active Directory B2C Azure AD B2C basic authentication C# CacheCow Client Side Templating Code First Dependency Injection Entity Framework ETag Foursquare API HTTP Caching HTTP Verbs IMDB API IoC Javascript jQuery JSON JSON Web Tokens JWT Model Factory Ninject OAuth OData Pagination Resources Association Resource Server REST RESTful Single Page Applications SPA Token Authentication Tutorial Web API Web API 2 Web API Security Web Service wordpress.com

Search

Copyright © 2021 · eleven40 Pro Theme on Genesis Framework · WordPress · Log in

loading Cancel
Post was not sent - check your email addresses!
Email check failed, please try again
Sorry, your blog cannot share posts by email.