OpenID Connect Interactive authentication with Authorization Code Flow (OIDC Part 3)

Share on facebook
Share on google
Share on twitter
Share on linkedin

In part 2 we created a simple OIDC setup using hard-coded client credentials for the client to obtain an access token, so it could invoke the resource API. In this post, we are gonna enable interactive login on the identity server with hard-coded test users using authorization flow. After the users have successfully logged in, the requested scopes will be provided to the client app using the callback URL. This will enable the client app to display the users’ claims on an authorized MVC view.

To enable this, we will need to do some changes to the authorization server and client app:

The OpenID connect with IdentityServer4 and Angular series

This series is learning you OpenID connect with Angular with these parts:

Authorization server – setup interactive login

The easiest way to get started with an interactive login is to download the quickstart controllers and view supporting this by running the below script (as administrator) in the command prompt inside the authorization server folder:

iex ((New-Object System.Net.WebClient).DownloadString(''))

After this the authorization server project should have a folder structure like this:

│   ├───Account
│   ├───Consent
│   ├───Diagnostics
│   ├───Grants
│   └───Home
│   ├───Account
│   ├───Consent
│   ├───Diagnostics
│   ├───Grants
│   ├───Home
│   └───Shared
        │   ├───css
        │   ├───fonts
        │   └───js

This provides you with controllers and views for managing accounts, consent and gives you a standard MVC homepage (of course with cringe-worthy bootstrap styling!).

For the login, we are going to create an authorization code grant client in the Config.cs file:

// OpenID Connect authorization flow client (MVC)
new Client
    ClientId = "mvc",
    ClientName = "MVC Client",
    AllowedGrantTypes = GrantTypes.Code,
    ClientSecrets =
        new Secret("secret".Sha256())
    RedirectUris = { "http://localhost:5002/signin-oidc" },
    PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
    AllowedScopes =

Here we are making this use authorization code flow by setting the AllowedGrantTypes to Code. Because we use authorization code flow we also need to specify a client secret, to be shared with the client app. The redirect urls are just set to identity server defaults. The allowed scopes is containing the resourceApi as well as two IdentityResources resources that are added to the Config.cs file as:

// scopes define the resources in your system
public static IEnumerable<IdentityResource> GetIdentityResources()
    return new List<IdentityResource>
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),

Where an APIResource grants access to resources on an API, Identity Resources grants access to information about the user. These Identity Resources enables scopes for getting a unique id of the users, called subject id, as well as users basic profile information such as name, address etc.

We are setting these new configurations up in the Startup.cs file that should look like this:

public class Startup
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit
    public void ConfigureServices(IServiceCollection services)


    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        if (env.IsDevelopment())



Since the last part, we have now added MVC and static files middleware as well as AddInMemoryIdentityResources for the identity resources.

Client app – Create an authorized view with user information

For the client app we are gonna create an authorized view for displaying user information contained in the id token.

First we are setting the authentication middleware up for our new authorization code flow client:

services.AddAuthentication(options =>
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
    .AddOpenIdConnect("oidc", options =>
        options.SignInScheme = "Cookies";

        options.Authority = "http://localhost:5000";
        options.RequireHttpsMetadata = false;
        options.ResponseType = "code";
        options.ClientId = "mvc";
        options.ClientSecret = "secret";
        options.SaveTokens = true;

Here we are disabling the Jwt Claim type mapping before we call the middlewareJwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); Otherwise the claim names would have long URI names instead of how they are in the JWT payload. For ease, we are using cookies here that will be send to the app client server on every request. It is specified that the client should use authorization flow with: options.ResponseType = “code” “ We are connecting to the identity server client by specifying the corresponding client id and client secret. options.SaveTokens = true is saving the tokens in the authentication cookie. That way the tokens can easily be access from the HTTPContext.

The IdentityController from last part is to be rewritten a little to return the claims from basic authentication to view by encapsulating the logic in a private method:

private async Task<HttpResponseMessage> GetUserClaimsFromApiWithClientCredentials()
    // discover endpoints from metadata
    var oidcDiscoveryResult = await DiscoveryClient.GetAsync("http://localhost:5000");
    if (oidcDiscoveryResult.IsError)
        throw new HttpRequestException(oidcDiscoveryResult.Error);

    // request token
    var tokenClient = new TokenClient(oidcDiscoveryResult.TokenEndpoint, "clientApp", "secret");
    var tokenResponse = await tokenClient.RequestClientCredentialsAsync("resourceApi");

    if (tokenResponse.IsError)
        throw new HttpRequestException(tokenResponse.Error);


    // call API
    var client = new HttpClient();

    var response = await client.GetAsync("http://localhost:5001/api/identity");
    return response;

The Index action is now an authorized endpoint only accessible if the authentication middleware accepts the request (valid authentication of user).

public async Task<IActionResult> Index()
    var userClaimsVM = new UserClaimsVM();
    var userClaimsWithClientCredentials = await GetUserClaimsFromApiWithClientCredentials();
    userClaimsVM.UserClaimsWithClientCredentials = userClaimsWithClientCredentials.IsSuccessStatusCode ? await userClaimsWithClientCredentials.Content.ReadAsStringAsync() : userClaimsWithClientCredentials.StatusCode.ToString();

    return View(userClaimsVM);

This is simply getting the user claims from the resourceApi using access token obtained with clientCredential authentication. The data is passed to the view using a simple view model containing a property for user claims with client credentials.

Also, we create a logout action for logging out the user by clearing the authentication cookie and sign out of OpenID connect session:

public async Task Logout()
    await HttpContext.SignOutAsync("Cookies");
    await HttpContext.SignOutAsync("oidc");
public async Task Logout()
    await HttpContext.SignOutAsync("Cookies");
    await HttpContext.SignOutAsync("oidc");


Finally we create a simple view for showing the client credentials from the resource API alongside with client credentials obtained using the new authorization code client:

@model ClientApp.Models.UserClaimsVM;
    ViewData["Title"] = "Index";



User claims:
    @foreach (var claim in User.Claims)

<form asp-controller="Identity" asp-action="Logout" method="post">
    <button type="submit">Logout</button>

Notice that in the bottom of the view we also add a button for logging out the user with the logout action.

Running it all

We can now run the whole application by right-clicking the solution in the Solution Explore->Properties and select Start for the three projects as:

We should now be able to go to “http://localhost:5002/identity” and be prompted for login, which we do with one of the test users. Hereafter we will be redirected to the identity view looking like:

Great job following along this far! Next time we will set up a hybrid flow client that can call the resource API.

Do you want to become an Angular architect? Check out Angular Architect Accelerator.

Related Posts and Comments

How to Handle Errors in a Reactive Angular App

In this post, we will cover how to handle errors in a reactive Angular app. To provide a good user experience you should always let your users know what state your application is in. That includes showing a loading spinner when it’s loading and showing error messages if there are any errors. It is a

Read More »

How to Set Up Git Hooks in an Nx Repo

Git hooks can be used to automate tasks in your development workflow. The earlier a bug is discovered, the cheaper it is to fix (and the less impact it has). Therefore it can be helpful to run tasks such as linting, formatting, and tests when you are e.g. committing and pushing your code, so any

Read More »

The Stages of an Angular Architecture with Nx

Long gone are the times when the frontend was just a dumb static website. Frontend apps have gotten increasingly complex since the rise of single-page application frameworks like Angular. It comes with the price of increased complexity and the ever-changing frontend landscape requires you to have an architecture that allows you to scale and adapt

Read More »

The Best Way to Use Signals in Angular Apps

Since Angular 16, Angular now has experimental support for signals and there is a lot of confusion in the community about whether this is going to replace RxJS or how it should be used in an app in combination with RxJS. This blog post sheds some light on what I think is the best way

Read More »

High ROI Testing with Cypress Component Testing

Testing is one of the most struggled topics in Angular development and many developers are either giving up testing altogether or applying inefficient testing practices consuming all their precious time while giving few results in return. This blog post will change all this as we will cover how I overcame these struggles the hard way

Read More »