---
title: "Authentication and Authorization Enhancements in .NET 9.0"
description: "With .NET 9.0, some interesting authentication and authorization features have been added to the platform. Let’s take an overview of them."
authors:
  - name: "Andrea Chiarelli"
    url: "https://auth0.com/blog/authors/andrea-chiarelli/"
date: "Nov 12, 2024"
category: "Developers,Tutorial,.NET"
tags: ["dotnet", "authentication", "authorization", "oidc", "par", "pushed-authorization-requests"]
url: "https://auth0.com/blog/authentication-authorization-enhancements-in-dotnet-9/"
---

# Authentication and Authorization Enhancements in .NET 9.0

.NET 9 introduces interesting new features, primarily focused on cloud-native development and performance. However, the [list of improvements](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-9/overview) 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](https://datatracker.ietf.org/doc/html/rfc9126) (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](https://auth0.com/blog/what-are-oauth-push-authorization-requests-par).

ASP.NET Core adds support for PAR to the native [OpenID Connect middleware](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnecthandler). This is a typical configuration of the middleware if you are not using the [Auth0 SDK](https://github.com/auth0/auth0-aspnetcore-authentication):

```csharp
// 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:

```csharp
// 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`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnectevents.onpushauthorization?view=aspnetcore-9.0#microsoft-aspnetcore-authentication-openidconnect-openidconnectevents-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:

```csharp
// 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](https://github.com/auth0/auth0-aspnetcore-authentication), you already have [an easier way to add the audience parameter](https://github.com/auth0/auth0-aspnetcore-authentication/blob/main/EXAMPLES.md#calling-an-api).

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

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

...
```

## Configuration Flags for Windows Authentication

When using [HTTP.sys](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/httpsys) as a web server for ASP.NET Core applications on Windows systems, you can now configure [Windows Authentication](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth) 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:

```csharp
// 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](https://www.telerik.com/blogs/getting-started-blazor-new-render-modes-net-8) have been introduced in .NET 8. You can get an idea by reading [this excerpt](https://auth0.com/blog/auth0-authentication-blazor-web-apps/#Why-Is-Blazor-Authentication-More-Complicated-Now) 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](https://auth0.com/blog/auth0-authentication-blazor-web-apps/#Syncing-the-Authentication-State). 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:

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

On the client, you will write the following:

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

Read the [documentation](https://learn.microsoft.com/en-us/aspnet/core/blazor/security/server/?view=aspnetcore-9.0&tabs=visual-studio#manage-authentication-state-in-blazor-web-apps) 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.