Introducing the OpenIddict client

When I unveiled the OpenIddict 3.0 roadmap three years ago, I mentioned that having an OpenIddict client would be a very nice addition but that implementing it as part of 3.0 wasn't realistic. Today, I'm very happy to announce that the OpenIddict client will ship as part of OpenIddict's next major version.

Why a new client?

A few client libraries already exist for .NET, including:

So why do we need another one? Am I reinventing the wheel by introducing another OIDC client stack?

Well, that's definitely a legit question. First, I have to say that these implementations work just fine and that I've used them happily other the years. Actually, I even contributed to some of these implementations multiple times so I have a fairly good experience working with them.

In a nutshell, here's what motivated me:

  • While all these libraries offer very specialized implementations, none of them offers a unified experience that allowing sharing a common code base usable on, say, ASP.NET Core and Blazor WASM applications: what you learned about the ASP.NET Core OIDC handler is not applicable to Blazor WASM, that uses a completely different OIDC client stack under the hood.

  • The OIDC integration for Blazor WASM uses oidc-client-js, that was archived in June 2021 and is no longer supported. While the Blazor OIDC wrapper itself is supported by Microsoft, the library it uses under the hood is not and won't receive any bug or security fix, which is a bit surprising to me, considering its author is still sponsored by Microsoft.

  • While it's supported by Microsoft, the Blazor OIDC wrapper doesn't actually get much love and suffers from annoying design issues that affect OpenIddict users that the team is unwilling to fix.

  • The OAuth 2.0 base handler for ASP.NET Core offers a straightforward API for creating derived OAuth 2.0 clients that we successfully used in the aspnet-contrib social providers. Unfortunately, its simplicity comes at a cost: it's not composable. Concretely, it means that every time we need to customize something (e.g adding a parameter to the token request), we end up duplicating more code than what we should (e.g to add a token request parameter, you also need to take care of sending the HTTP request and handling the response, which is something the OAuth 2.0 base handler does for you if you don't override the OAuthHandler<T>.ExchangeCodeAsync(OAuthCodeExchangeContext context) method).

  • There's also another downside with the OAuth 2.0 base handler: it doesn't support OpenID Connect. Yet, we received contributions to add OAuth 2.0 providers that also support OpenID Connect: while ASP.NET Core has a dedicated OIDC handler that is more appropriate for these cases, many people like the simpler registration story the OAuth 2.0 base handler and the aspnet-contrib providers offer. Retrospectively, we shouldn't have accepted these contributions as these providers don't benefit from the additional security checks a real OpenID Connect implementation requires (e.g nonce, at_hash or c_hash validation, etc.)

  • The OAuth 2.0 base handler doesn't support the OAuth 2.0 Authorization Server Metadata specification that backported the OpenID Connect server discovery feature to the OAuth 2.0 world and that allows finding the location of the authorization and token endpoints dynamically, without having to hardcode them. While the adoption is slow, I'd expect more and more services to support it in the next few years.

Read more

ASP.NET Core 6 and authentication servers: the real bait and switch is not the one you think

Earlier this month, Barry Dorrans – the Security PM for .NET – announced that the ASP.NET Core templates would be updated to target Duende IdentityServer5 as part of the .NET 6 effort and that the ASP.NET team was considering creating a development-only tool for testing OpenID Connect integration in .NET 7.

By looking at the reactions under the blog post, on GitHub or on Reddit, the least we can say is that people are always quite passionate when it comes to how Microsoft treats these topics. Some like that decision while a few others feel "betrayed" and consider this move a "bait and switch":

Despite this move, my personal opinion hasn't changed: I was very happy when Microsoft announced they would use IdentityServer4 in 2018 and the commitment to use the new dual-licensed version of IdentityServer in .NET 6 (instead of writing their own OIDC server from scratch) is, in my opinion, a step in the right direction in changing their relationship with the .NET community: Microsoft can't provide everything out-of-the-box and promoting third-party projects is a great approach.

The fact Microsoft keeps sponsoring the IdentityServer project – for $12,000/year at the time of writing – is also an excellent signal sent to the community. Even if you've never liked Microsoft's love/hate relationship with OSS, it would be very hard to deny that the company has positively changed in this regard.

Read more

OpenIddict 3.0 general availability

One year and a half after releasing the first OpenIddict 3.0 alpha bits, I'm very pleased to announce that 3.0 is now generally available!

This release is unquestionably OpenIddict's most exciting milestone, as both the server and validation stacks were rewritten from scratch to adopt a whole new event-oriented model, which should give OpenIddict 3.0 much more flexibility and extensibility than the aspnet-contrib OpenID Connect server and validation middleware it replaces.

If you're interested in reading more about the changes introduced in this release, I wrote a few blog posts describing the most important ones:

In OpenIddict 3.0 RTM, the Data Protection formatter used to create and read tokens serialized using ASP.NET Core Data Protection was fixed to use the same serialization versions as OpenIddict 2.x so that tokens created with OpenIddict 2.x can be read and used with OpenIddict 3.0. This change will impact users of the beta and RC versions that opted in for the Data Protection format (typically, OrchardCore users). Fortunately, this only requires retrieving new refresh tokens by starting a new authorization flow.

As announced in Introducing OpenIddict 3.0 beta1, the OpenIddict 2.x and aspnet-contrib OpenID Connect server/validation middleware are now considered legacy/obsolete packages and won't receive bug fixes or security updates.

Whether you're still on ASP.NET 4.x (via OWIN/Katana), ASP.NET Core 2.1 on .NET Framework or .NET Core, ASP.NET Core 3.1 or are already using ASP.NET Core 5.0, migrating to OpenIddict 3.0 should never be a blocking factor, thanks to its broader-than-ever platforms support!

If you need additional time to migrate to OpenIddict 3.0 and are interesting in chatting about your options, feel free to ping me at contact@kevinchalet.com.

I'd like to thank Sébastien Ros, mridentity, Andrew, gustavdw, Gillardo, Dovydas Navickas and Christian Schmitt for sponsoring me on GitHub, which allows me to dedicate more time to OpenIddict! You guys have no idea how much I appreciate that.

Merry Christmas everyone!

Introducing OpenIddict 3.0's first release candidate version

What's new?

All the changes introduced in this release can be found on GitHub.

While this release almost exclusively focused on fixing bugs and eliminating pain points, an important change affected our localization story: starting with OpenIddict 3.0 RC1, the error descriptions returned by OpenIddict itself will no longer be translated.

Thanks to an external report, I discovered that the localization support introduced in OpenIddict 3.0 beta3 was causing issues when returned as part of the standard WWW-Authenticate response header. Worse, it was violating the OAuth 2.0 core specification, that requires that all error_descriptions (not just the ones returned in WWW-Authenticate) only include US-ASCII characters: even diacritics like é (that are common in French) are not allowed. In practice, that means we can sadly only return English error descriptions and that's why localization support had to be removed in this release.

That said, I still believe having localized content is extremely useful and makes a library like OpenIddict more inclusive and developer-friendly. As such, we'll opt for a different approach in the next RC: the OpenIddict server and validation stacks will be updated to always include a unique error_uri pointing to openiddict.com, which will allow documenting every returned error over time.

What's next?

OpenIddict 3.0 RTM is expected to ship mid-December. Before that, a second release candidate version should be released early December.

OpenIddict 2.x and the aspnet-contrib OAuth 2.0/OpenID Connect server/extensions will no longer be supported and won't receive free security updates/bug fixes as soon as 3.0 is officially released (only users benefiting from a support contract will still receive updates). As such, it is very important for users of these packages to start evaluating OpenIddict 3.0 RC1 as soon as possible, so that potential bugs affecting their scenarios can be fixed in the RTM version.

OpenIddict 3.0 beta6 is out

What's new?

All the changes introduced in this release can be found on GitHub, but here's a recap of the most important ones:

OpenIddict now supports response type permissions

In previous versions, the response_type values an application was allowed to use were always inferred from the grant types. For instance, if a client was granted the authorization_code grant type permission, it was automatically allowed to use response_type=code (that corresponds to the "pure" authorization code). If it was granted the implicit permission, it was also allowed to use response_type=id_token and depending on its type (confidential or public), response_type=id_token token and response_type=token.

While convenient, this approach lacked granularity. In OpenIddict 3.0 beta6, this mechanism is replaced by explicit response type permissions. Response type permissions are granted exactly the same way as the other permissions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
ClientId = "mvc",
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
ConsentType = ConsentTypes.Explicit,
DisplayName = "MVC client application",
DisplayNames =
{
[CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente MVC"
},
PostLogoutRedirectUris =
{
new Uri("https://localhost:44381/signout-callback-oidc")
},
RedirectUris =
{
new Uri("https://localhost:44381/signin-oidc")
},
Permissions =
{
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Logout,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.GrantTypes.RefreshToken,
Permissions.ResponseTypes.Code, // New permission
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
Permissions.Scopes.Roles,
Permissions.Prefixes.Scope + "demo_api"
},
Requirements =
{
Requirements.Features.ProofKeyForCodeExchange
}
});

Introducing response type permissions was also a good opportunity for removing the OpenIddict-specific hybrid client type, that was used by confidential clients that also wanted to retrieve an access token directly from the authorization endpoint. In beta6, the hybrid type is no longer supported and must be replaced by explicit Permissions.ResponseTypes.CodeIdTokenToken or Permissions.ResponseTypes.CodeToken permissions.

Read more