Replacing IdentityModel with MSAL's support for generic OIDC-compliant authorities

Learn how to replace IdentityModel with MSAL.NET for OAuth 2.0 and OpenID Connect flows using support for generic OIDC-compliant authorities.

When it comes to implementing OAuth 2.0 and OpenID Connect authentication flows in .NET applications, there are often two ways to do it. The first, which I would never recommend, is to do it manually by making all the necessary HTTP calls. The second is to use a third-party library that implements these flows in a secure and performant manner. Among the most popular third-party libraries is IdentityModel, created by Dominick Baier and Brock Allen, who are also the creators of IdentityServer, now a paid product of their company, Duende Software.

But recently, it has become possible to use MSAL.NET, and you may prefer to use a Microsoft library instead of a third-party library like IdentityModel, despite its popularity and reputation. In this article, I will explain the changes that have been made to MSAL.NET and how to use it instead of IdentityModel.

But wait, isn’t MSAL.NET only for accessing Microsoft 365 and Azure resources?

Well, it used to be true until the beginning of 2023. Since the addition of support for any OIDC-compliant authority, such as Auth0, Okta, IdentityServer, Keycloak, etc., MSAL.NET has become a viable alternative to IdentityModel for using OAuth 2.0 and OpenID Connect flows. Furthermore, MSAL.NET has more robust error handling and better logging compared to IdentityModel, in my professional experience. It also offers several additional performance features, such as token persistence, which can be used to implement a distributed cache mechanism.

MSAL.NET is now a generic OAuth 2.0 and OIDC client.

So, how do I use MSAL.NET to replace IdentityModel?

Let’s take the Client Credentials Flow as an example, which involves exchanging a client ID and client secret for an access token. This flow is particularly popular for Machine-to-Machine (M2M) communication between backend services and other daemons, where no end-user is involved.

First, let’s see how to implement it with IdentityModel. We will use the OIDC demo authority provided by Duende Software, which offers the following client registration:

  • Client ID: m2m
  • Client secret: secret
  • Allowed scopes: api
  • OIDC authority: https://demo.duendesoftware.com
  • Token endpoint: https://demo.duendesoftware.com/connect/token

The C# code required to obtain an access token with IdentityModel is as follows:

// This is a code sample, please follow Microsoft's guidelines for using HttpClient:
// https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines
var httpClient = new HttpClient();

using var accessTokenRequest = new ClientCredentialsTokenRequest
{
    Address = "https://demo.duendesoftware.com/connect/token",
    Scope = "api",
    ClientId = "m2m",
    ClientSecret = "secret",
    ClientCredentialStyle = ClientCredentialStyle.AuthorizationHeader
};

var tokenResponse = await httpClient.RequestClientCredentialsTokenAsync(accessTokenRequest);
Console.WriteLine(tokenResponse.AccessToken);

I am not a big fan of the approach used by IdentityModel because, in addition to being a wrapper around HttpRequestMessage, I find the error handling a bit ambiguous. C# is a language that relies heavily on exceptions for error handling, but the TokenResponse class returned by RequestClientCredentialsTokenAsync swallows all exceptions other than OperationCanceledException and exposes transformed values that can be confusing, such as: Exception, IsError, ErrorType, ErrorMessage, HttpErrorReason, and Error.

From personal experience, when something goes wrong in this authentication flow, it becomes very difficult to react correctly because there are so many properties to check depending on the situation. That’s why I prefer MSAL.NET, which exposes more explicit and easier-to-handle exceptions.

Here is how to implement the same flow with MSAL.NET:

var app = ConfidentialClientApplicationBuilder.Create("m2m")
    .WithOidcAuthority("https://demo.duendesoftware.com")
    .WithClientSecret("secret")
    .Build();

var authResult = await app.AcquireTokenForClient(["api"]).ExecuteAsync();
Console.WriteLine(authResult.AccessToken);

The big news that has gone relatively unnoticed is the addition of the WithOidcAuthority method, which allows specifying a generic OIDC authority. MSAL.NET handles retrieving the OIDC metadata by appending /.well-known/openid-configuration to the authority URL. This means you no longer need to specify the token endpoint URL.

Note that it is preferable to reuse instances of IConfidentialClientApplication and IPublicClientApplication as much as possible, as they offer built-in token cache management, whereas you would need to implement your own cache with IdentityModel.

Licensed under CC BY 4.0
Ko-fi donations Buy me a coffee