Sign Up
Hero

Authentication and Authorization Enhancements in .NET 9.0

With .NET 9.0, some interesting authentication and authorization features have been added to the platform. Let’s take an overview of them.

.NET 9 introduces interesting new features, primarily focused on cloud-native development and performance. However, the list of improvements in other areas of the platform is long. This article will focus on the main innovations introduced by .NET 9.0 in authentication and authorization.

Support for Pushed Authorization Requests (PAR)

One of the most interesting features brought by .NET 9.0 is support for Pushed Authorization Requests (PAR), an OAuth 2.0 and OpenID Connect extension that strengthens the security profile of these protocols. PAR allows applications to not expose the parameters of an authorization request through the browser. Instead, it enables direct communication of these parameters between the application and the authorization server. This is very important when you implement applications in a high-security context, such as in the financial, healthcare, or other regulated sectors. If you want to learn the details of how PAR works, read this article.

ASP.NET Core adds support for PAR to the native OpenID Connect middleware. This is a typical configuration of the middleware if you are not using the Auth0 SDK:

// Program.cs
...
  
builder.services.AddAuthentication(options =>
  {
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
  })
  .AddCookie()
  .AddOpenIdConnect("Auth0", options =>
  {
    options.Authority = $"https://{builder.Configuration["Auth0:Domain"]}";

    options.ClientId = builder.Configuration["Auth0:ClientId"];
    options.ClientSecret = builder.Configuration["Auth0:ClientSecret"];
    options.ResponseType = "code";
});

...

If the Identity provider you are using supports PAR, you don't need to do anything different from this. In fact, the OpenID Connect middleware transparently supports PAR by default. In other words, if the middleware detects PAR support by analyzing the standard .well-known/openid-configuration discovery document, it automatically leverages the PAR endpoint to pass the authorization request parameters. You can change the default behavior by modifying the value of the new option property PushedAuthorizationBehavior. For example, the following setting disables PAR support:

// Program.cs
...
  
  .AddOpenIdConnect("Auth0", options =>
  {
    options.Authority = $"https://{builder.Configuration["Auth0:Domain"]}";
    ...
    options.ResponseType = "code";
    
    options.PushedAuthorizationBehavior = PushedAuthorizationBehavior.Disable;
});

...

If you want to force your application to use PAR, you can set the value of PushedAuthorizationBehavior to PushedAuthorizationBehavior.Require. In this case, you get an error if the Identity provider does not support PAR.

Finally, to help customize PAR support, the OnPushAuthorization event has been added to OpenIdConnectEvents.

OAuth and OIDC Parameters Customization

Staying in the context of the OpenID Connect middleware, now you have a simplified way to add custom parameters to the authorization request. Up to .NET 8, if you wanted to add a custom parameter, such as audience, to the authorization request, you had to handle the OnRedirectToIdentityProvider event as shown in the following code snippet:

// Program.cs
...

.AddOpenIdConnect(options =>
{
  options.Events.OnRedirectToIdentityProvider = context =>
  {
    context.ProtocolMessage.SetParameter("audience", "https://myapi.com");
    return Task.CompletedTask;
  };
});
...

If you are using the Auth0 SDK for ASP.NET Core, you already have an easier way to add the audience parameter.

.NET 9 introduces the AdditionalAuthorizationParameters option to simplify this process. The previous code is equivalent to the following:

// Program.cs
...
  
  .AddOpenIdConnect("Auth0", options =>
  {
    ...
    
    options.AdditionalAuthorizationParameters.Add("audience", "https://myapi.com");
});

...

Configuration Flags for Windows Authentication

When using HTTP.sys as a web server for ASP.NET Core applications on Windows systems, you can now configure Windows Authentication by setting two new properties:

  • EnableKerberosCredentialCaching. If this boolean value is true, the Kerberos authentication credentials are cached.
  • CaptureCredentials. If this boolean value is true, the HTTP Server API captures the caller's credentials and uses them for Kerberos or Negotiate authentication.

The following code shows an example of usage of these properties:

// Program.cs
...
builder.UseHttpSys(options =>
{
    options.Authentication.Schemes = AuthenticationSchemes.Negotiate;
    options.Authentication.EnableKerberosCredentialCaching = true;
    options.Authentication.CaptureCredentials = true;
});
...

Authentication State Serialization for Blazor Web Apps

If you are a Blazor developer, you know how complicated authentication is since render modes have been introduced in .NET 8. You can get an idea by reading this excerpt from my tutorial on adding authentication to Blazor Web Apps. Basically, in an application model where components can be rendered on the server side, on the client side, and on both sides dynamically, you need to keep the user authentication state in sync between the two sides of your Blazor application. This leads to writing a fair amount of code, as you can see by taking a look here. In practice, you need to serialize the user authentication state on the server, then send it to the WebAssembly client, which in turn needs to deserialize it. In addition, both authentication states must be kept in sync in order to reflect on the client side the actual authentication state on the server.

Fortunately, .NET 9 simplifies this approach by providing two new methods:

  • AddAuthenticationStateSerialization(), which adds the services to serialize the authentication state on the server.
  • AddAuthenticationStateDeserialization(), which adds the services to deserialize the authentication state in the WebAssembly client.

Instead of creating the serializing and deserializing classes yourself, you can now simply call the two methods respectively on the server and on the client to sync the authentication state.

On the server, you will have:

// Program.cs
...
builder.Services.AddRazorComponents()
    .AddInteractiveWebAssemblyComponents()
    .AddAuthenticationStateSerialization();
...

On the client, you will write the following:

// Program.cs
...
builder.Services.AddAuthorizationCore();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddAuthenticationStateDeserialization();
...

Read the documentation to learn more.

Conclusion

Although they are not revolutionary innovations in authentication and authorization, these new features are very convenient because they aim to simplify the work of .NET developers. In particular, PAR support prevents developers from implementing the extension specifications themselves and encourages the adoption of increasingly advanced security. Also, simplifying the serialization and deserialization of the authentication state in Blazor relieves developers of the burden of implementing specific classes, resulting in potentially fewer bugs.