OpenIddict 5.0 general availability

Almost exactly a year after OpenIddict's last major release, I'm very pleased to announce that 5.0 is now generally available! 🎉

What's new?

While most of the new features were already gradually added to the 4.x branch throughout 2023, some of them required introducing breaking changes and had to wait for 5.0:

  • Relaxed redirect_uri comparison policies for native applications.
  • Per-client static token lifetimes.
  • Client assertions support for the server and validation stacks.

If you're interested in learning more about these new features, don't miss this post: Introducing native applications, per-client token lifetimes and client assertions support in OpenIddict 5.0 preview1.

Migration

While OpenIddict 5.0 comes with some breaking changes, the migration process should be fairly easy. To help users with this process, an OpenIddict 5.0 migration guide was added to the documentation.

OpenIddict 5.0 is fully compatible with ASP.NET Core 2.1 (on .NET Framework), ASP.NET Core 6.0, ASP.NET Core 7.0 and ASP.NET Core 8.0, so the migration can be done without having to upgrade to the latest .NET runtime/ASP.NET Core version.

Read more

Introducing native applications, per-client token lifetimes and client assertions support in OpenIddict 5.0 preview1

Earlier today, the first OpenIddict 5.0 preview was pushed to NuGet.org 🎉

As always, all the changes can be found on GitHub but here's a recap of the most interesting ones:

What's new?

This major release introduces breaking changes that require applying migrations to update the database schema. As such it is not recommended to use it in production or in tandem with an existing deployment using OpenIddict 4.x.

Relaxed redirect_uri comparisons for native applications

While the OAuth 2.0 and OpenID Connect specifications explicitly mandated the use of the "simple string comparison" logic – put simply, a byte-by-byte comparison – newer specifications like RFC8252 (aka OAuth 2.0 for Native Apps) relaxed this policy for mobile and desktop applications to allow using dynamic ports chosen at runtime.

While OpenIddict already allowed customizing the comparison policy by overriding OpenIddictApplicationManager.ValidateRedirectUriAsync(), built-in support for native applications was not a thing.

OpenIddict 5.0 changes that by introducing a new ApplicationType property for the application entity. This property is always set to web by default, but can be explicitly set to native to opt in the relaxed comparison policy that ignores ports for local redirect_uris:

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
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
// Note: the application must be registered as a native application to force OpenIddict
// to apply a relaxed redirect_uri validation policy that allows specifying a random port.
ApplicationType = ApplicationTypes.Native,
ClientId = "console",
ClientType = ClientTypes.Public,
ConsentType = ConsentTypes.Systematic,
DisplayName = "Console client application",
RedirectUris =
{
// Note: the port must not be explicitly specified as it is selected
// dynamically at runtime by the OpenIddict client system integration.
new Uri("http://localhost/callback/login/local")
},
Permissions =
{
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Device,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.GrantTypes.DeviceCode,
Permissions.GrantTypes.RefreshToken,
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
Permissions.Scopes.Roles,
Permissions.Prefixes.Scope + "demo_api"
},
Requirements =
{
Requirements.Features.ProofKeyForCodeExchange
}
});

To avoid ambiguities, the existing OpenIddictApplicationDescriptor.Type property (and the properties of the same name in OpenIddictEntityFrameworkApplication, OpenIddictEntityFrameworkCoreApplication and OpenIddictMongoDbApplication) have been replaced by ClientType.

Users migrating to 5.0 will be invited to update their database schema to include the new ApplicationType property and the renamed ClientType member.

Read more

Can you use the ASP.NET Core Identity API endpoints with OpenIddict?

TL;DR: yes, but please, don't do it! 🤣

If you're an avid reader of Andrew Lock's blog (certainly one of the best .NET blogs out there!), you probably figured out that the title of this blog post is very similar to his latest post, Can you use the .NET 8 Identity API endpoints with IdentityServer?, in which he describes how you could (but really shouldn't 😂) use ASP.NET Core 8's Identity API endpoints with an OAuth 2.0/OpenID Connect server stack like IdentityServer or OpenIddict.

The similarity is of course completely deliberate, as his post motivated me to write this one.

The approach described in Andrew's post – that mainly consists in using the Identity API endpoints introduced by .NET 8 to handle the user authentication part – also works with OpenIddict, so there's no point covering the same aspects twice: if you haven't read it, please read Andrew's post before reading mine.

Instead, we're going to do something even crazier ('cause why not? 😎): using OpenIddict to process token requests handled by the Identity API login endpoint... and generate token responses containing either JWT or ASP.NET Core Data Protection access tokens!

ASP.NET Core Identity's API endpoints are faux-OAuth 2.0 endpoints...

As a preamble, it's important to note that while the ASP.NET team mentioned multiple times that the Identity API endpoints are not an OAuth 2.0 implementation, it's actually a non-standard equivalent of the OAuth 2.0 resource owner password credentials grant, as I demonstrated here.

(if you're not convinced yet these endpoints are "heavily inspired" by OAuth 2.0, this message posted by Stephen Halter – who wrote the ASP.NET Core Identity API endpoints feature – should speak for itself 😁)

... that can be used with OpenIddict nevertheless

Why is this "OAuth 2.0/not OAuth 2.0" distinction important you may ask? Well, since the ASP.NET Core Identity API endpoints implement a clone of the OAuth 2.0 resource owner password credentials grant with only a few differences, it's going to be very easy to use OpenIddict's advanced events model to make it compatible with the non-standard protocol created by the ASP.NET team.

Create a minimal ASP.NET Core API and enable the ASP.NET Core Identity API endpoints

For that, create a new .csproj referencing the ASP.NET Core Identity UI, the OpenIddict ASP.NET Core metapackage and the EF Core packages:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.0-rc.1.23421.29" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0-rc.1.23421.29" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.0-rc.1.23421.29" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0-rc.1.23419.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0-rc.1.23419.6" />
<PackageReference Include="OpenIddict.AspNetCore" Version="4.8.0" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="4.8.0" />
</ItemGroup>

</Project>

Read more

Connecting Windows Media Center to Tvheadend with HDHRProxyIPTV: an alternative to DVBLink

This blog post was updated to use a different method for injecting the channels into the WMC database. The attached tool has been updated accordingly.

After more than 13 years of continuous use, I'm saying goodbye to my traditional Windows Media Center TV setup 😊

Like many (if not most) users, I've always been using Windows Media Center with terrestrial tuners physically attached to my HTPCs:

  • A USB dual-tuner Sony PlayTV when I started playing with WMC back in 2008 (an excellent device that's still working just fine).
  • A PCI dual-tuner Hauppauge WinTV-NOVA-TD-500.
  • A PCIe dual-tuner AVerMedia AVerTV Duo Hybrid (the hardware is good but the driver is terrible and has been responsible for a lot of blue screens...)
  • A PCIe quad-tuner Hauppauge WinTV-quadHD – great hardware and great driver! – when I eventually got tired of the crashes caused by the AVerTV Duo Hybrid.

While this setup certainly lacked the flexibility of more elaborate options based on network tuners (like the popular HDHomeRun) or virtualized tuners such as the now defunct DVBLink, it was actually trivial to set up and exceptionally stable, specially since replacing the old TV tuners by newer Hauppauge WinTV-quadHD cards (these things are not only ultra-stable but they also offer low-latency channel switching and have a fairly good reception).

So, why changing something that has been working so well? It all started with a message posted by a user of the My Digital Life forum – acer-5100 – about using Windows Media Center with H.265/HEVC channels.

As you probably already know, due to internal Windows Media Center/DirectShow limitations, only MPEG1, MPEG2 and H.264 channels are supported and it's not possible to watch or record H.265/HEVC channels, even if you install the appropriate DirectShow codecs. Since WMC was abandoned by Microsoft many years ago, it's extremely unlikely we'll ever see an update to make it compatible with HEVC channels.

To work around this limitation, acer-5100 opted for a simple but clever setup that consists in combining DVBLink (and its IPTV source plugin) with Tvheadend: by simply configuring DVBLink to use transcoded streams provided by Tvheadend rather than the original HEVC sources, Windows Media Center always gets a good old MPEG2 or H.264 video stream it can decode without any issue. Simple and clever.

Read more

Introducing system integration support for the OpenIddict client

Note: this blog post was updated to use the new record-based APIs introduced in OpenIddict 4.5.

When I unveiled the new OpenIddict client stack a year ago, I mentioned that one of the core design goals was to avoid coupling it to ASP.NET Core to eventually allow using it basically everywhere. With the release of OpenIddict 4.1, I'm making one additional step towards this goal by adding experimental support for Windows and Linux applications.

Why is a dedicated system integration package necessary?

While the OpenIddict client can already be used as-is to implement non-interactive flows like password or client credentials (thanks to its dedicated APIs in OpenIddictClientService), interactive flows like the code, hybrid or implicit flows are more complicated to implement, as they typically require launching the system browser (or using some sort of web view) to redirect the user to the authorization server and handling the authorization callback, which is generally implemented using an embedded web server or by registering a custom protocol URI scheme.

Leaving these critical parts as an exercise wouldn't offer a great experience. To avoid that, the new OpenIddict.Client.SystemIntegration package takes care of launching the user's preferred browser and handles the authorization responses returned by the identity provider to the protocol URI scheme associated with the application (or posted to the embedded web server), in a completely transparent way.

Once configured, doing a complete code flow dance should be as easy as:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
try
{
// Ask OpenIddict to initiate the authentication
// flow (typically, by starting the system browser).
var result = await _service.ChallengeInteractivelyAsync(new()
{
ProviderName = provider
});

// Wait for the user to complete the authorization process.
var response = await _service.AuthenticateInteractivelyAsync(new()
{
Nonce = result.Nonce
});

MessageBox.Show($"Welcome, {response.Principal.FindFirst(ClaimTypes.Name)!.Value}.",
"Authentication successful", MessageBoxButton.OK, MessageBoxImage.Information);
}

catch (ProtocolException exception) when (exception.Error is Errors.AccessDenied)
{
MessageBox.Show("The authorization was denied by the end user.",
"Authorization denied", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}

Read more