Using Azure AD B2C with Angular 9

Azure Active Directory (Azure AD) B2C is a popular business-to-consumer identity management service from Microsoft that enables you to customize and control how users sign up and sign in to your application.
While there are many examples out there how to use Azure B2C with an ASP.NET Core web application, it’s hard to find examples that performs identity management with Azure AD B2C using an Angular application.

In this article we will create an Angular 9 web application that authenticate against an Azure AD B2C and calls a secured ASP.NET Core-Web-API.

Prerequisites

Configure Azure AD B2C

For our example we need to create two applications. The first one will represent our middleware (ASP.NET Core-Web-API). The second one represents our web app (Angular).

Register the middleware application

To register the middleware application, go to the Application blade within your Azure AD B2C and click on Add:

add-b2c-app

Enter a name that describes your middleware and turn the Include web app / web API switch to YES. We don’t need the Reply URL for our middleware since we will obtain the token with an angular application. However, we have to enter any valid URI here (I choosed https://localhost). Finally we need to specify the APP ID URI that we will use later to grant the web app access to the middleware:

b2c-middleware-app

Register the web application

Now we have to register the web application. Again, enter a name that describes your web application and turn the Include web app / web API switch to YES. This time we do care about the reply URL. For local testing purpose we can enter http://localhost:4200/index.html here. We don’t need to specify an App ID URI. This is how the configuration should look like:

b2c-web-app

Grant API access

Since the web application needs to communicate with the secured middleware, we will also have to grant the web application access to the API.

Go back to your Azure B2C web application registration and inside the API access blade, click on the Add button. On the new dialog select your previous created middleware application and click on the Ok button (leave the scopes as they are):

API access

Create a sign up and sign in user flow

The last thing we have to configure inside the Azure AD B2C is the user flow. Learn more about user flow types.

Open the User flows (policies) blade and click on the New user flow button. In the following view click on Sign up and sign in.

Enter susi in the Name input and select Email signup for the Identity provider. Leave Multifactor authentication disabled and check both checkboxes for the given name claim:

createsusi

Create and secure the middleware

We will scaffold our ASP.NET Core Web API middleware using the .NET Core CLI. The command we are looking for is dotnet new.

Using the following parameters, we can create the middleware with preconfigured Azure AD B2C authentication:

  • –auth IndividualB2C (Individual authentication with Azure AD B2C)
  • –aad-b2c-instance The Azure Active Directory B2C instance to connect to
  • –susi-policy-id The sign-in and sign-up policy ID for the middleware we have created earlier
  • –client-id The Application ID of the middleware
  • –domain The domain for the directory tenant
  • –name The name for the middleware

Here an example with the values for my setup:

dotnet new webapi `
--auth IndividualB2C `
--aad-b2c-instance https://aboutazure.b2clogin.com/ `
--susi-policy-id B2C_1_susi `
--client-id 1d1cfeca-1fb2-4bf8-9d6b-4d5ac15ad7b3 `
--domain aboutazure.onmicrosoft.com `
--name foo.api

Before we run the application, lets change the ValuesController.cs to return “Hello World”:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace foo.api.Controllers
{
    [Authorize]
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        // GET api/values
        [HttpGet]
        public IActionResult GetAsync() => Ok("Hello, World!");
    }
}

We can now start the application using the dotnet run command. Our site will be available at http://localhost:5000.
If we perform a GET request on https://localhost:5001/api/values, we will get a 401 UNAUTHORIZED error – as expected since we are not passing an access token:

invalid-token

Create the web application

To create the Angular web application, we can use the ng new command from the Angular CLI. The name of our app will be foo.web. I also added some additional parameters to scaffold a minimal project without any testing frameworks and without routing:

ng new foo.web --minimal=true --skipGit=true --routing=false --style=css

Now it is time to choose a library that supports OpenID Connect (OIDC) authentication. For this demo project, we will use the angular-oauth2-oidc npm module from Manfred Steyer. It is OpendID certified and has lot of handy features like automatically adding the access token to certain URLs when calling a Web API.

You can install the module using npm:

npm i angular-oauth2-oidc --save

Then we need to import the module inside the app.module.ts. I also configured the module to include the access token in the request when calling our Web API:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { OAuthModule } from 'angular-oauth2-oidc';
import { AppComponent } from './app.component';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        HttpClientModule,
        OAuthModule.forRoot({
            resourceServer:
            {
                allowedUrls: ['https://localhost:5001/api'],
                sendAccessToken: true
            }
        })
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }

To configure the library, we need to create a class that implements AuthConfig. We will do this in a new file called auth.config.ts:

import { AuthConfig } from 'angular-oauth2-oidc';

export const DiscoveryDocumentConfig = {
    url: "https://aboutazure.b2clogin.com/aboutazure.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=b2c_1_susi"
}

export const authConfig: AuthConfig = {
    redirectUri: window.location.origin + '/index.html',
    responseType: 'token id_token',
    issuer: 'https://aboutazure.b2clogin.com/b88448e7-64f3-497b-a7aa-73ae0cc6e9ce/v2.0/',
    strictDiscoveryDocumentValidation: false,
    tokenEndpoint: 'https://aboutazure.b2clogin.com/aboutazure.onmicrosoft.com/oauth2/v2.0/token?p=b2c_1_susi',
    loginUrl: 'https://aboutazure.b2clogin.com/aboutazure.onmicrosoft.com/oauth2/v2.0/token?p=b2c_1_susi',
    clientId: '0c295dee-8904-4546-92b7-dc64dd14bf58',
    scope: 'openid profile https://aboutazure.onmicrosoft.com/foo-api/user_impersonation',
    skipIssuerCheck: true,
    clearHashAfterLogin: true,
    oidc: true,
}

We can get most of the values from the discovery document which is always located under:
https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/v2.0/.well-known/openid-configuration?p={policy}.

Here is how the values are mapped:
discoverytoauth.png

The clientId is the Application Id of our foo.web Azure AD application. The scope for our API can be found under Published scopes inside the Azure AD foo.api application:
publishedscopes

Finally, to test the application, I modified the app.component.ts to list the claims for a logged in user and added a button to invoke our secured API and to Sign out.

Test the sign up and sign in

Now its time to test our application. This is how our web app looks like when we browse it:
initial.png

If we click on the Login button, we get redirected to our b2c_1_susi Azure AD B2C flow:

b2cflow.png

I already created an account. This is how the site looks like after I signed in with that account:

martin.png

We can now invoke our secured foo.api service using the API Call button. We will see the response at the end of the page:

hello.png

I published the whole source code in my GitHub repository. I also deployed both the web application and the API on Azure. You can test it live here.
Please note, that you inital have to create an account using the “Sign up now” link after you clicked on the Login button.

Update: I upgraded the source code on GitHub to .NET Core 3.1 and Angular 9

14 thoughts on “Using Azure AD B2C with Angular 9”

  1. This is a great article, thanks a lot.
    question.
    – when you call the api, I don’t see a bearer token is explicitly added to html header. and the secured api call is still successful, what is the magic?

    thanks.

  2. Great work.. How did u manage refresh token logic? Also the access_token gets logged in browser history (could be security issue)

  3. When the token will expire, how you will get new one without angular “back-end support”? According the OpenID Connect, you can’t use refresh token from client side as it should be securely kept.

  4. I usually do not comment but just had to say what a great job you’ve done with the article, very clear and thorough!
    The only changes I had to do to make it work for me was enable CORS on server for testing
    and change the port for server written here (default for me was 44304).

    Amazing time saver!!!

  5. Hy Martin, I am creating a web app, where I provided a new functionality to user for “FREE TRIAL”, in normal free trial user need to provide basic info like name, email, password. I also provided a button for “Sign up with Azure Active Directory”, to implement this I created an app on Azure which is multi-tenanted (users from any directory can access my app), it is working fine but when I submitted this App to the Microsoft they respond this:
    ——————————————————————————————————–
    Critical issues 1 failure(s)
    These issues block the publish of your solution to Microsoft marketplace. Please refer to our Microsoft Marketplace policies for more details.

    Acquisition, Pricing, and Terms
    1000.3.1 AAD Single Sign-On
    The app URL does not seem to provide a user experience of the app.
    ——————————————————————————————————–
    Can you please help me, what I did wrong? Thanks

  6. Thanks for the easy to follow article. I need to secure an Angular app, and I was wondering if there is a way to redirect unauthorized access to protected routes of my Angular app to the B2C flow’s login page? Can the DefaultOAuthInterceptor be used for this purpose, and if yes, how? Thanks in advance.

  7. Hi this tutorial is looking great but I am stuck on the dotnet component. Being new to that bit the first mistake I made was using dotnet verison 3.0… Figured out how to use 2.1.607.

    However when I run dotnet new and your values with obvious changes. I do not get a ValuesController.cs that is like yours (hello world change). When I try and get the 401 error I get a 502 and it seems to relate to certificates.

    Perhaps this is my setup problem but could you specfically state what dotnet version you are using as it would be great to eliminate the scaffolding issue. As an FYI if you run dotnet new with version 3.0 it is totally different

Comments are closed.