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.
Put simply, the OpenIddict client is closely modeled after the OpenIddict server and validation stacks and reuses many of their core concepts, including their extremely powerful events model, their modular approach and the host/transport-agnostic design of the core server and validation packages.
Concretely:
The OpenIddict client will be usable with any OAuth 2.0 and OpenID Connect server (based on OpenIddict or not) and will heavily use client/server negotiation to adapt its validation routines to the targeted authorization servers to guarantee maximum compatibility while ensuring the best level of security.
The OpenIddict client currently supports ASP.NET Core 2.1 and higher and native support for ASP.NET 4.x/OWIN will be added in the next few weeks. I also started working on a Blazor WASM prototype and while there'll be limitations (e.g Blazor WASM's encryption/signing support is extremely limited), things are really promising.
The code, implicit and hybrid flows are all natively supported (except response_type=token
, which is unsafe) and OpenIddict has built-in logic to select the best flow to use (unlike the ASP.NET Core OIDC handler that uses the hardcoded response_type=id_token
). It's worth noting the OpenIddict client was designed to allow implementing additional flows (like the OpenID Connect Client-Initiated Backchannel Authentication Flow) without requiring massive design changes.
The OpenIddict client will allow supporting multiple static authorization servers (I currently don't plan on adding dynamic client registration support, but it's something that may be added in a future version).
The OpenIddict client uses a different approach for handling authorization callbacks: while the OAuth 2.0 and OIDC handlers developed by Microsoft typically handle everything for you by flowing the ClaimsPrincipal
extracted from identity tokens/userinfo responses to another authentication handler (in most cases, an instance of the cookies authentication handler), the OpenIddict client won't have this logic and will offer exactly the same approach as the OpenIddict server stack by encouraging users to be involved in the creation of the authentication cookie during the callback handling.
Concretely, the OpenIddict client will offer a pass-through mode to allow handling callbacks in a custom MVC action or minimal API handler. Here's an example of such an action:
|
|
By doing that, developers will have greater control over what's actually stored in their authentication cookies without requiring a claims mapping feature similar to what's used by the MSFT OIDC handler, that can sometimes be hard to use when you need to restore claims that are removed by the OIDC handler to help ensure the resulting authentication cookies don't hit cookie size limits.
The OpenIddict client will be fully suitable for delegation-only scenarios where authentication is not needed or where the identification of the user who authorized the client is not possible (typically, OAuth 2.0-only servers that don't have a userinfo endpoint). In this case, no information about the user will be available but the tokens will still be available to perform any API call on his behalf.
Currently, only the authorization/authentication part is fully implemented: userinfo will come in the next few weeks and logout support at a later date.
I plan on working on a prototype evaluating the OpenIddict client as a potential replacement of the OAuth 2.0 base handler developed by Microsoft for the aspnet-contrib social providers. While the OpenIddict client is certainly a bit more complex, doing so would have interesting advantages:
By being natively compatible with multiple environments (ASP.NET Core, ASP.NET 4.x, Blazor WASM), a broader audience of developers could be reached at no additional cost. This could be a nice migration option for those using the OWIN OAuth providers (by which the aspnet-contrib providers were initially inspired), that are sadly no longer under active development.
Unlike the OAuth 2.0 base handlers, the OpenIddict client natively supports the client_secret_basic
client authentication method, which would save us from having code that is needed just to attach the client credentials to the Authorization
header of token requests in all the providers that don't support client_secret_post
.
By being natively composable, the events model could be used to eliminate code is not strictly required for the social providers to work (e.g if we need to simply attach a custom parameter, we shouldn't have to also send the HTTP request and handle the response in each provider).
The story for social providers supporting both OAuth 2.0 and OpenID Connect would be much better than with the OAuth 2.0 base handler, as they would automatically benefit from all the validation checks implemented in the core OpenIddict client without requiring custom code.
As the OpenIddict client supports both static and dynamic server configuration, social providers that implement discovery could be easily updated to use dynamic configuration, which is not possible with the OAuth 2.0 base handler (that requires hardcoded endpoints).
This will certainly be points I'll discuss with Martin Costello - who co-owns the aspnet-contrib OAuth 2.0 providers with me - once I have a fully working prototype.
You can see the OpenIddict client in action in the sandbox project. If you're interesting in sharing your feedback, please don't hesitate to do so by posting a message in the dedicated GitHub discussion.
]]>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.
That said, I'd be lying if I didn't admit I was quite disappointed when reading that blog post: OpenIddict – the OpenID Connect server I maintain – wasn't even mentioned as a potential free and open source alternative that can be used on-premises or hosted in a cloud application.
This omission is not an oversight: Barry and the rest of the ASP.NET team know my work and we informally discussed potential options that could have helped secure their supply chain, should they have included something based on OpenIddict in the ASP.NET templates: even options like making OpenIddict a co-owned project were on the table.
This omission has a reason, clearly explained in the blog post: Microsoft wants you to use their cloud offers: Azure AD and Azure AD B2C. In fact, the ASP.NET team is already working on migration guides to help users move to Azure AD as part of their .NET 6 effort (fun fact: the thread has been locked so that only members of the .NET team can reply).
The crucial thing to note here is that Azure AD – or the equivalents offered by Okta, Auth0, Firebase or Amazon – are not just OpenID Connect servers like IdentityServer or OpenIddict (that typically rely on a membership stack like ASP.NET Core Identity): they also aim at replacing your users database.
Relying on a SaaS product to take care of your users database is not a bad idea in itself: most of the security aspects are managed for you by the cloud vendors, that will transparently maintain the software and hardware, keep your users database safe and try their best to protect your application against bad actors. This argument alone is often enough to justify the move to cloud solutions like Azure AD:
Microsoft already has a team and a product in that area, Azure Active Directory, which allows 500,000 objects for free. The ASP.NET team feels a managed cloud solution remains the best practical option for developers – the security is managed, you don’t store credentials locally with the risks that presents.
After all, Azure AD has a free plan with up to 500,000 users (*), so this should be a no-brainer, right? Not so fast: these services are always free for a reason: their business model relies on the fact a fraction of their customers will end up exceeding the limits of the free plan. At that point, customers basically have three options:
The thing is moving to a different vendor is never an easy task as cloud vendors typically do their best to make that a difficult process: while you're generally allowed to export non-sensitive parts of your users database (e.g to re-import it in a different service), there's a thing you often have no control over: password hashes:
While cloud-based identity offers are captive environments, things are completely different with IdentityServer and OpenIddict, that are often used in conjunction with ASP.NET Core Identity: you own the database and user passwords are stored in a format that could even be implemented in a non-.NET environment (after all, it uses PBKDF under the hood).
When using an open-source solution you can run locally, you have full control over your data. With a cloud offer, the story is completely different. Don't get lured by free plans: if you're interested in a cloud offer, make sure the pricing model of paid plans is adequate for your case before even considering it (and even then, be prepared for a potential price increase at any point).
(*) Actually, the free plan's limit is 50,000 objects. If you need additional entries, you have to ask the Azure support to extend the limit, which is something they do at their own discretion.
]]>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!
]]>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_description
s (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.
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.
]]>All the changes introduced in this release can be found on GitHub, but here's a recap of the most important ones:
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:
|
|
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.
Users who have many clients to migrate can use this script to infer the "best" response type permissions based on the already allowed grant types:
|
|
In previous versions, the hybrid flow was automatically enabled when enabling both the authorization code and implicit flows. In beta6, it now must be enabled explicitly, like the other flows:
|
|
In 3.0 beta6, refresh tokens were revamped and the following changes were made:
The rolling refresh tokens mechanism now allows for a small leeway to avoid revoking entire token chains when concurrent requests are received (by default, it is set to 15 seconds but can be changed using OpenIddictServerBuilder.SetRefreshTokenReuseLeeway()
).
The rolling refresh tokens mechanism no longer revokes previously issued tokens, it just marks refresh tokens as redeemed when they are used.
Rolling refresh tokens are now enabled by default to make OpenIddict compliant with the OAuth 2.0 best current practices, that require implementing either rotation or sender-constrained tokens. Developers who prefer disabling rolling refresh tokens can do so by calling OpenIddictServerBuilder.DisableRollingRefreshTokens()
.
The refresh token lifetime extension mechanism was removed: even when rolling refresh tokens are disabled, a new refresh token will now be generated and returned for each refresh token request, which guarantees that the client application always gets a refresh tokens (and subsequent access tokens) containing the latest claims.
DateTime
instead of DateTimeOffset
Due to incomplete support in the Oracle MySQL and Npgsql Entity Framework providers, the EF Core and EF 6 entities no longer use DateTimeOffset
. Developers migrating to OpenIddict 3.0 beta6 will need to add a migration to move from DateTimeOffset
to DateTime
.
Previous versions of OpenIddict 3.0 already allowed making PKCE mandatory per-client, but in beta6, there's now a global option:
|
|
jti
claimTo make tokens lighter, a jti
claim is no longer generated and persisted in the tokens generated by OpenIddict 3.0 beta6.
Special thanks to Johann Wimmer for his great contribution to the tests projects, that are now compiled with nullable reference types enabled!
]]>Even though both implement the OpenID Connect and OAuth 2.0 specifications, OpenIddict and IdentityServer4 are very different under the hood and have different approaches: IdentityServer4 was designed as a general and ready-to-use identity provider for ASP.NET Core while OpenIddict 3.0 aims at offering a fully modular OpenID Connect server and validation stack that can be used not only on ASP.NET Core 2.1 (.NET Framework and .NET Core), 3.1 and 5.0, but also in legacy ASP.NET >= 4.6.1 applications using OWIN/Katana.
Internally, the two server stacks don't have much in common: IdentityServer4 uses a traditional and linear services-based approach (with request/token validators and response generators) while OpenIddict 3.0 uses a completely different model based on events and asynchronous events handlers: each handler is responsible of a very specific task (e.g ensuring a redirect_uri
is not malformed and doesn't include a fragment) and all the handlers can be removed, reordered and even replaced by custom handlers for those who need to override the default logic used by OpenIddict 3.0.
Unlike IdentityServer4, OpenIddict doesn't ship with any membership stack integration, which is something developers must implement themselves. In the OpenIddict world, this is typically done by adding an authorization controller that serves as bridge between the user authentication part (generally implemented using ASP.NET Core Identity) and the OpenID Connect authorization part. Concretely, OpenIddict only knows about ClaimsPrincipal
instances and you're responsible of providing ClaimsPrincipal
s containing the claims you want OpenIddict to use in your tokens.
You probably guessed it by now: OpenIddict 3.0 is very different from IdentityServer4 and those hoping for a free look-alike will likely be disappointed. Fortunately, implementing an OpenID Connect server using OpenIddict is not hard, and generally consists in copying a few files from the openiddict-samples repository.
There's however something that IdentityServer4 users will likely miss: an equivalent of the popular Skoruba.IdentityServer4.Admin
project that offers a GUI to manage users, roles, clients and authorizations. While there's indeed no direct equivalent in OpenIddict itself, the OrchardCore project has a native OpenID module based on OpenIddict that includes a clients management feature. This feature is still young and quite limited (read, not as complete as Skoruba.IdentityServer4.Admin
), but it's actively developed and contributions are always welcome.
In this blog post, I'll demonstrate how this management GUI can be used with an existing OpenIddict server (typically located in another project).
So here we go:
For that, clone the https://github.com/openiddict/openiddict-samples
repository.
For this tutorial, we'll use the Velusia.Server demo, a very typical ASP.NET Core application that implements code flow support. Launch it via Visual Studio or using dotnet run
and make sure the openiddict-velusia-sample
database is correctly created by Entity Framework Core in your local (localdb)\MSSQLLocalDB
SQL Server instance.
To simplify things, we'll opt for the "complete" OrchardCore CMS approach, that offers a GUI-based setup experience. For that, reference the following package:
|
|
Then, update your Startup
class to register the OrchardCore CMS services and middleware:
|
|
At this point, you're ready to launch the application and configure OrchardCore:
Fill in the form and press "finish setup".
The database configured at this stage will only be used by OrchardCore. We'll later configure the OpenID module to use Entity Framework Core and SQL Server with the database created by the Velusia.Server
demo application.
Once set up, log in and click on "Dashboard" to access the administration interface. Then, click on "Features" under "Configuration":
In the features admin, type "OpenID" to filter the available features and enable the "OpenID management interface" feature:
At this stage, if you visit the client applications administration page, you should see an empty list:
That's expected: by default, the management interface points to the local OrchardCore database and not (yet) to openiddict-velusia-sample
.
To fix that, we'll need to reference the OpenIddict Entity Framework Core package, so that the management UI can directly communicate with the openiddict-velusia-sample
database. For that, reference the following packages:
|
|
Final part: adding an EF Core DbContext
pointing to the openiddict-velusia-sample
database and registering the OpenIddict EF Core stores using the OrchardCore APIs:
|
|
Restart the application and visit the applications administration page. If everything is correctly configured, you should see the MVC client application
entry corresponding to the Velusia.Client
demo application registered by Velusia.Server
.
From here, you can easily update the application entry by clicking on "Edit":
Voilà!
]]>All the changes introduced in this release can be found on GitHub, but here's a recap of the most important ones:
To help with housecleaning and remove authorization and token entries that are no longer valid from the database, OpenIddict now comes with a new OpenIddict.Quartz
plugin that you can enable in a few lines of code.
First, you need to register the Quartz.NET services and configure it to use DI and an in-memory store:
|
|
Then, you need to enable the Quartz/.NET generic host integration.
|
|
Finally, you'll need to register the OpenIddict job:
|
|
Once registered, the OpenIddict job will start removing invalid entries approximately 2 minutes after the application starts and will do that every hour (assuming your app is still running).
To keep track of recently expired entries, the OpenIddict job will only remove authorizations and tokens whose lifespan is of at least 14 days. This value can be changed via OpenIddictQuartzBuilder
but cannot be less than 10 minutes.
Special thanks to Quartz.NET's maintainer, Marko Lahma, for reviewing the PR that added this feature and for the great job he did with Quartz.NET 3.2 (that was released earlier today).
Thanks to fantastic contributions from the community, 10 additional languages have been added in this release:
Language | Contributor |
---|---|
Arabic | Hisham Bin Ateya |
Deutsch | Johann Wimmer |
Dutch | Maarten Balliauw |
Gujarati | Ashish Patel |
Hindi | Ashish Patel |
Italian | Massimiliano Donini |
Simplified Chinese | Chino Chang |
Spanish | Bart Calixto |
Traditional Chinese | Chino Chang |
Turkish | Serkan Zengin |
options.EnableAuthorizationEndpointCaching()
and options.EnableLogoutEndpointCaching()
have a new nameFeedback indicated that options.EnableAuthorizationEndpointCaching()
and options.EnableLogoutEndpointCaching()
's current name was not ideal. To fix that, these methods were renamed to options.EnableAuthorizationRequestCaching()
and options.EnableLogoutRequestCaching()
.
This post is also a good opportunity to thank Sébastien Ros, Andrew and Mr.i, who now sponsor me on GitHub. Thank you very much!
]]>All the changes introduced in this release can be found on GitHub, but here's a recap of the most important ones:
As announced in a previous post, OpenIddict was fully updated to use the .NET Platform localization extensions to return localized error descriptions and validation messages, which will be particularly useful in multilingual projects like Orchard Core.
At the time of writing, OpenIddict 3.0 beta3 comes with English and French resources, but additional languages will likely be added in the next versions.
To enable localization support in ASP.NET Core, simply register the request localization middleware (ideally, put it at the top of your Configure(IApplicationBuilder app)
method):
|
|
ASP.NET 4.x users can enable globalization support in web.config by setting uiCulture
and culture
to auto
. Alternatively, ASP.NET 4.x and OWIN users can use Owin.Localization, which is a backport of the ASP.NET Core middleware.
Conversely, developers who don't need localization support and prefer keeping their deployments as small as possible can remove the localized satellite assemblies by simply adding <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
in their .csproj
file.
Following a general trend across the .NET community, all the OpenIddict libraries are now compiled with nullable reference types support enabled.
Due to a bug in the Roslyn compiler, the non-generic IOpenIddictApplicationManager
, IOpenIddictAuthorizationManager
, IOpenIddictScopeManager
and IOpenIddictTokenManager
interfaces deliberately don't expose any nullable annotation. You can track the resolution of this bug here.
In previous versions of OpenIddict, sending a rolling refresh token twice always resulted in the revocation of the entire tokens chain and the revocation of the associated authorization. To make the rolling tokens option more convenient to use, the authorization associated to a refresh token is no longer revoked when it is sent multiple times.
DisableSlidingExpiration()
was renamed to DisableSlidingRefreshTokenExpiration()
To make clearer the fact the sliding expiration mechanism applies to refresh tokens, the DisableSlidingExpiration()
helper was renamed to DisableSlidingRefreshTokenExpiration()
in OpenIddict 3.0 beta3.
All the changes introduced in this release can be found on GitHub, but here's a recap of the most important ones:
System.Linq.Async
was removed from the OpenIddict.Core
dependenciesDue to a namespace conflict between Entity Framework Core and System.Linq.Async
(maintained by the RX team), we had to remove the dependency the OpenIddict.Core
package had on this package, as explained in this GitHub thread.
This is not specific to OpenIddict and other popular libraries like Stripe.NET
had to make a similar decision. As such, I strongly encourage the EF and RX teams to work together to find a better solution to this issue, that has a serious impact on the .NET ecosystem.
In OpenIddict 1.x/2.x, the 4 collections used by the MongoDB stores were automatically initialized during the first use of the stores.
In OpenIddict 3.0 beta2, this logic was removed for performance reasons and consistency with the other stores. Instead, MongoDB users are encouraged to initialize their database once using a setup script. The following C# script can be used to initialize the 4 OpenIddict collections:
|
|
With response_mode=form_post
being impacted by same-site, some applications are moving back to the query response mode (the default mode for the authorization code flow). To avoid token length issues with clients using response_mode=query
and having strict query string limits, authorization codes are now reference tokens in OpenIddict 3.0 beta2.
As part of this change, the options.UseReferenceTokens()
setting was also split into options.UseReferenceAccessTokens()
and options.UseReferenceRefreshTokens()
for more flexibility and options.UseRollingTokens()
was renamed to options.UseRollingRefreshTokens()
for clarity.
In OpenIddict 3.0 beta1, the OpenIddict server Data Protection integration allowed you to opt out of the ASP.NET Core Data Protection token format when creating new tokens thanks to the options.PreferDefaultTokenFormat()
option.
In beta2, this option was split into multiple options for more control over the token format (JWT or Data Protection) used to produce the different token types supported by OpenIddict:
options.PreferDefaultAccessTokenFormat()
.options.PreferDefaultAuthorizationCodeFormat()
.options.PreferDefaultDeviceCodeFormat()
.options.PreferDefaultRefreshTokenFormat()
.options.PreferDefaultUserCodeFormat()
.Some of the major changes introduced in this release were described in the OpenIddict 3.0 roadmap (the detailed list can be found on GitHub), but here's a recap of the most important ones:
This is the most important change of this release: ASOS – the low-level OpenID Connect server framework used in OpenIddict 1.0/2.0 – and the aspnet-contrib validation and introspection middleware were all merged into OpenIddict.
To ensure OpenIddict can be used as a replacement for ASOS (for which people usually write their own persistence layer, at least to validate things like client_id
and redirect_uri
), a new degraded mode was introduced to allow using the OpenIddict server components without all the additional logic that relies on the OpenIddict managers/stores (e.g client authentication, client validation, token storage).
For more information about this feature, read my previous post: Creating an OpenID Connect server proxy with OpenIddict 3.0's degraded mode.
In OpenIddict 1.0/2.0, the core and the EF 6/EF Core/MongoDB stores were already decoupled from ASP.NET Core. In 3.0, the server and validation features have been revamped to avoid depending on ASP.NET Core. Instead, ASP.NET Core integration is now provided by separate packages named OpenIddict.Server.AspNetCore
and OpenIddict.Validation.AspNetCore
. For convenience, a metapackage named OpenIddict.AspNetCore
can be referenced to import the OpenIddict core, server and validation packages and their ASP.NET Core integration with a single PackageReference
.
Unlike the previous versions, OpenIddict 3.0 will also support multiple ASP.NET Core versions: 2.1, 3.1 and 5.0.
Decoupling OpenIddict from ASP.NET Core was also a great opportunity to make it natively compatible with OWIN/Katana, which will allow using its server or validation features in any ASP.NET (non-core) 4.6.1 application. OWIN/Katana integration is provided by the OpenIddict.Server.Owin
and OpenIddict.Validation.Owin
packages, which follows the same pattern as the ASP.NET Core hosts.
Here's the framework/runtime combinations that will be officially supported in 3.0:
Web framework version | .NET runtime version |
---|---|
ASP.NET Core 2.1 | .NET Framework 4.6.1 |
ASP.NET Core 2.1 | .NET Framework 4.7.2 |
ASP.NET Core 2.1 | .NET Framework 4.8 |
ASP.NET Core 2.1 | .NET Core 2.1 |
ASP.NET Core 3.1 | .NET Core 3.1 |
ASP.NET Core 5.0 | .NET 5.0 |
OWIN/Katana 4.1 | .NET Framework 4.6.1 |
OWIN/Katana 4.1 | .NET Framework 4.7.2 |
OWIN/Katana 4.1 | .NET Framework 4.8 |
For more information about OWIN/Katana and ASP.NET 4.x support, read this dedicated post: Adding OpenIddict 3.0 to an OWIN application.
In OpenIddict 1.0/2.0, the ASP.NET Core Data Protection stack is always used to encrypt the authorization codes, refresh tokens and access tokens, unless you explicitly opt for JWT access tokens with options.UseJsonWebTokens()
.
In 3.0, OpenIddict will now use encrypted JWT as the default token format for all the token types. Developers who need to disable access token encryption will be able to do so using options.DisableAccessTokenEncryption()
:
|
|
Unlike previous versions, the OpenIddict 3.0 validation handler now supports JWT and introspection. Developers who use JWT access tokens in 2.0 and the JWT bearer middleware developed by Microsoft are strongly encouraged to move to the OpenIddict validation handler, that provides a simpler configuration story and includes dedicated logic to ensure tokens produced by OpenIddict 1.0/2.0 can still be used when migrating to OpenIddict 3.0.
ASP.NET Core Data Protection tokens are still supported in 3.0 and even recommended if you migrate from 1.0/2.0 (to ensure tokens produced by older versions can still be read after migrating to 3.0) or if you enable device flow support, as they provide additional protection against token leakage. To enable Data Protection support, simply call options.UseDataProtection()
in both the server and validation options:
|
|
ASP.NET Core Data Protection support is provided by the OpenIddict.Server.DataProtection
and OpenIddict.Validation.DataProtection
packages. These packages are referenced by the OpenIddict.AspNetCore
metapackage but not by OpenIddict.Owin
for naming and layering reasons.
Unlike the rest of ASP.NET Core, Data Protection is still compatible with the .NET Framework and thus can be used in ASP.NET 4.x applications. To enable ASP.NET Core Data Protection with the OWIN host, reference OpenIddict.Server.DataProtection
and OpenIddict.Validation.DataProtection
.
All the OpenIddict libraries have been updated to use System.Text.Json
(developed by Microsoft) instead of JSON.NET.
While it's a relatively new library (that still lacks features like a writable DOM), the performance boost it offers is impressive and makes System.Text.Json
a fantastic candidate for replacing JSON.NET in OpenIddict.
Here's a tiny benchmark showing the difference between deserializing a OpenIdConnectMessage
– the type used in OpenIddict 2.0 – with JSON.NET and deserializing its equivalent in 3.0 (where OpenIddictMessage
was optimized and is now internally powered by System.Text.Json
).
|
|
As you can see, the performance gain is excellent: not only it is 35% faster, but it also allocates much less memory!
|
|
Method | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|
OpenIddict20 | 4.510 μs | 0.0725 μs | 0.0642 μs | 1.00 | 0.8469 | 0.0229 | - | 6.92 KB |
OpenIddict30 | 2.948 μs | 0.0160 μs | 0.0149 μs | 0.65 | 0.2136 | - | - | 1.77 KB |
... and serialization is even more impressive!
|
|
|
|
Method | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|
OpenIddict20 | 4.209 μs | 0.0667 μs | 0.0714 μs | 1.00 | 0.9918 | 0.0076 | - | 8.11 KB |
OpenIddict30 | 1.361 μs | 0.0257 μs | 0.0275 μs | 0.32 | 0.1621 | - | - | 1.34 KB |
In most cases, this change should be completely transparent, but if you manually use JSON.NET to serialize or deserialize OpenIdConnectMessage
, OpenIdConnectRequest
or OpenIdConnectResponse
instances, consider moving to System.Text.Json
when migrating to OpenIddict 3.0, as 3.0 no longer includes a built-in JSON.NET JsonConverter
for these types.
Standardized in 2019, the device authorization grant (aka device flow) is now natively supported by OpenIddict 3.0.
Adding device flow support required making changes to the OpenIddict entities. This should be transparent for MongoDB users but Entity Framework 6 and Entity Framework Core users will have to create and apply a migration for the new schema to be reflected in the database.
In 1.0/2.0, developers who needed immediate access token revocation support had to use reference tokens. As of 3.0, this is no longer required: even self-contained JWT or Data Protection access tokens can be revoked, as OpenIddict 3.0 now always creates a database entry for all token types.
Like in previous versions, the API projects needing immediate access token revocation need to either have a direct access to OpenIddict's database or use introspection:
|
|
|
|
Reference tokens can still be used in OpenIddict 3.0 and are now stored either as JWT or Data Protection tokens in the database, but enabling them only affects how tokens are returned to the client: when reference tokens are enabled, the original token payload is added to the database entry and a reference identifier (a 256-bit random value) is returned instead of the token, which greatly reduces the size of the tokens returned to the clients in exchange for more space used in the database.
Independently of whether options.UseReferenceTokens()
is called or not, user codes used in the device flow are always user-readable reference tokens. As such, the device flow cannot be enabled when disabling token storage with options.DisableTokenStorage()
and will require custom code when using the degraded mode.
IAsyncEnumerable<T>
The application, authorization, scope and token managers (located in OpenIddict.Core
) and all the Entity Framework 6, Entity Framework 6 and MongoDB stores now natively support IAsyncEnumerable<T>
. While this is a massive binary/source breaking change, migrating to IAsyncEnumerable<T>
allows stores to implement streamed enumerations, which was not possible in OpenIddict 2.0 (that uses Task<ImmutableArray<T>>
instead of IAsyncEnumerable<T>
).
IAsyncEnumerable<T>
is fully supported even on .NET Framework >= 4.6.1, thanks to the Microsoft.Bcl.AsyncInterfaces
compatibility package that OpenIddict.Abstractions
references.
To make more obvious the fact the OpenIddict entities are designed for a specific ORM/database, the application, authorization, scope and tokens entities have all been renamed to include the name of their corresponding store (e.g OpenIddictApplication
-> OpenIddictMongoDbApplication
).
In previous versions of OpenIddict, the authorization, logout, token and userinfo endpoints were always pass-through: OpenIddict validated the requests for you, but you needed to add an MVC controller – typically named AuthorizationController
– or a custom middleware to handle them later in the ASP.NET Core pipeline.
Starting in 3.0, none of the OpenIddict endpoints is pass-through by default, which helps with debugging as OpenIddict will now throw an exception if the request is not handled using the events model and if the pass-through mode was not enabled, instead of letting ASP.NET Core return a 404 response.
To enable pass-through for a specific endpoint, use the methods provided by OpenIddictServerAspNetCoreBuilder
or OpenIddictServerOwinBuilder
:
|
|
In 3.0, integration with the ASP.NET Core status code pages middleware is now opt-in:
|
|
AuthenticationProperties.Parameters
is now supportedReturning custom parameters was already supported in 1.0/2.0, but they had to be stored as strings in AuthenticationProperties.Items
with a special suffix. This mechanism is no longer supported, but returning custom bool
, long
, string
, string[]
or JsonElement
parameters is now much simpler:
|
|
AuthenticationProperties.Parameters
was introduced in ASP.NET Core 2.1 and thus is not supported on OWIN. To return custom properties on OWIN, you must store them in AuthenticationProperties.Items
as strings and create a custom event handler to return them as part of the response. See Add additional values into Authorize endpoint response for an example.
There'll likely be a few other beta releases before RTM to ensure everything is working as intended and to add features like localization support. All the samples contained in the openiddict-samples have already been updated to target OpenIddict 3.0 beta1 and new samples will be progressively added to cover the newly supported scenarios (e.g device flow).
Considerable effort has been dedicated to making sure that all users of ASOS, the aspnet-contrib extensions or OpenIddict have a migration path to 3.0 – whether they are using the latest ASP.NET Core version and the latest .NET Core runtime, are stuck with 2.1 on .NET Framework or still use ASP.NET 4.6.1 with OWIN/Katana.
As such, I'll no longer offer free support for OpenIddict 1.0/2.0 or the aspnet-contrib packages that were merged into OpenIddict as part of this release once the 3.0 RTM packages ship (in a few months). The 2 aspnet-contrib repositories will also be archived.
The following NuGet packages will be eventually flagged as obsolete to inform users that they are no longer actively developed or supported:
Package name | Package version |
---|---|
AspNet.Security.OpenIdConnect.Extensions | All |
AspNet.Security.OpenIdConnect.Primitives | All |
AspNet.Security.OpenIdConnect.Server | All |
Owin.Security.OpenIdConnect.Extensions | All |
Owin.Security.OpenIdConnect.Server | All |
AspNet.Security.OAuth.Introspection | All |
AspNet.Security.OAuth.Validation | All |
Owin.Security.OAuth.Introspection | All |
Owin.Security.OAuth.Validation | All |
OpenIddict.* | < 3.0 |
Users who substantially contributed to OpenIddict, sponsored the project or benefit from a support contract will still get bug fixes for these packages via a private feed to give them additional time to migrate to OpenIddict 3.0.
With that in mind, I encourage everyone to start migrating to OpenIddict 3.0 beta1 and testing it in a non-production environment.
While super exciting, this release has been way more time-consuming than I had initially anticipated, and there are still many important things to do, like adding documentation and a migration guide to help developers update their applications to OpenIddict 3.0.
For that, I need your help: please consider contributing to the effort or sponsoring me, so I can spend more time working on OpenIddict. Without external help, I likely will not have enough spare time to work on things that would benefit the community, like documentation.
Contribution area |
---|
Core/server/validation components |
Documentation |
Samples |
If you're interested in getting dedicated support or want to discuss sponsoring options, don't hesitate to reach me at contact@kevinchalet.com.
]]>Up-to-date samples for ASP.NET 4.x and OWIN can be found in the openiddict/openiddict-samples repository:
Last year, I announced that OpenIddict would become compatible with OWIN/Katana – and thus usable in any ASP.NET 4.x non-Core application – as part of the 3.0 release. While using OpenIddict in an OWIN application shouldn't be much different than using it in an ASP.NET Core application, there's actually a part than will slightly differ between the two platforms: dependency injection.
Globally introduced in ASP.NET Core 1.0, dependency injection support was not a thing in OWIN. Yet, OpenIddict itself depends on dependency injection for all its features: core, server and token validation. To work around the lack of official DI support, OpenIddict 3.0 will support two approaches: one working with the Autofac DI container and one without. This blog post briefly describes how to do that.
To keep things simple, we'll create a tiny self-hosted .NET 4.7.2 OWIN application, but of course, these 2 approaches also work in any ASP.NET application, no matter what's the host used: HttpListener
, System.Web
or even third-party options like Nowin
.
For that, create a new application with the following .csproj
, that contains the basic things we'll need: the Microsoft DI primitives used by OpenIddict, the OWIN self-host and hosting abstractions and the OpenIddict OWIN metapackage (that itself references the core, server and validation packages, plus the OWIN integration assembly):
|
|
Then, add the entry point...
|
|
... and the Startup
class:
|
|
At this point, no matter the option you'll opt for, you'll need to instantiate a ServiceCollection
and register the OpenIddict server and validation services (exactly like how you'd do in a ASP.NET Core application):
|
|
If you read my previous post, you probably figured out that I used OpenIddict 3.0's degraded mode in this snippet to implement the OAuth 2.0 password flow, which allows using OpenIddict's server feature without any backing database.
If you try to run the project as-is, all you'll get is a 404 error, as there's still a thing we need to do: integrating OpenIddict into OWIN's request processing pipeline.
Unlike most other DI containers, Autofac has an excellent OWIN integration story that keeps things very clean and well-integrated. To use Autofac with OWIN, reference the following packages:
|
|
Then, update your Configuration
method to create the IContainer
, import the ServiceCollection
and register the Autofac OWIN middleware:
|
|
This is the beauty of this option: nothing else is required.
Without Autofac, things will get a bit more complicated, as you'll need to manage per-request scopes yourself. While we'll use the Microsoft DI container to achieve that, this approach is also doable with any other DI container that provides an IServiceProvider
implementation.
For that, we'll have to add a middleware creating a service scope per request and injecting it in the OWIN environment with the System.IServiceProvider
key (the value that the OpenIddict server and validation OWIN integrations expect). We'll also need to call app.UseOpenIddictServer()
and app.UseOpenIddictValidation()
, that register the OpenIddict OWIN authentication handlers.
|
|
To ensure things are working properly, you can use Postman and send a token request:
If your application uses ASP.NET Web API, a couple additional things will need to be configured:
Microsoft.AspNet.WebApi.WebHost
package. Instead, use the Microsoft.AspNet.WebApi.Owin
package and call app.UseWebApi()
at the end of your Startup.Configuration(IAppBuilder app)
method:
|
|
When using Autofac and its ASP.NET Web API integration, you'll also need to call app.UseAutofacWebApi(configuration)
before app.UseWebApi(configuration)
. For more information, read the Autofac documentation.
HostAuthenticationFilter
can be registered globally to always call the OpenIddict validation handler:
|
|
HostAuthenticationFilter
can be applied per-action or per-controller using HostAuthenticationAttribute
:
|
|
As part of this task, a new feature was added to OpenIddict: the degraded mode (also known as the ASOS-like or bare mode). Put simply, this mode allows using OpenIddict's server without any backing database. Once enabled, all the features that rely on the OpenIddict application, authorization, scope and token managers (contained in the OpenIddict.Core
package) are automatically disabled, which includes things like client_id
/client_secret
or redirect_uri
validation, reference tokens and token revocation support. In other words, this mode allows switching from an "all you can eat" offer to a "pay-to-play" approach.
A thread, posted on one of the aspnet-contrib repositories gave me a perfect opportunity to showcase this particular feature. The question asked by the commenters was simple: how can I use an external authentication provider like Steam (that implements the legacy OpenID 2.0 protocol) with my own API endpoints?
Steam doesn't issue any access token you could directly use with your API endpoints. Actually, access tokens are not even a thing in OpenID 2.0, which is a pure authentication protocol that doesn't offer any authorization capability (unlike OAuth 1.0/2.0 or OpenID Connect).
So, how do we solve this problem? The most common approach typically consists in creating your own authorization server between your frontend application and the remote authentication provider (here, Steam). This way, when the application needs to authenticate a user, the user is redirected to the authorization server, that delegates the actual authentication part to another party. Once authenticated by that party, the user is redirected back to the main authorization server, that issues an access token to the client application.
This a super common scenario, that can be implemented using standard protocols like OpenID Connect and well-known implementations like OpenIddict or IdentityServer. However, these options are sometimes considered "overkill" for such simple scenarios. After all, why would you need a fully-fledged OIDC server – with login, registration or consent views – when all you want is to delegate the actual authentication to another server in a totally transparent and almost invisible way?
Rolling your own protocol is tempting... but a very bad idea, as you can't benefit from all the security measures offered by standard flows like OAuth 2.0/OpenID Connect's authorization code flow, whose threat model is clearly identified for many years now. As you may have guessed by now, this is precisely where OpenIddict 3.0's degraded mode can come in handy.
First, we'll start by creating an ASP.NET Core 3.1 API application and by adding the aspnet-contrib Steam provider and an instance of the cookies authentication handler (that will be used to store the user identity retrieved from Steam).
For that, add the following dependency in your .csproj:
|
|
Then, update ConfigureServices
to register the Steam and cookies authentication handlers:
|
|
You'll also need to update Configure
to call app.UseAuthentication()
:
|
|
Now, we'll need to add the OpenID Connect server part. For that, add the following packages:
|
|
Next, tweak ConfigureServices
to register the OpenIddict ASP.NET Core server and validation services, with only the options we need: the authorization code flow allowed, the authorization and token endpoints active and the degraded mode enabled:
|
|
At this point, trying to launch the application will result in an exception being thrown:
InvalidOperationException: No custom authorization request validation handler was found. When enabling the degraded mode, a custom
IOpenIddictServerHandler<ValidateAuthorizationRequestContext>
must be implemented to validate authorization requests (e.g to ensure the client_id and redirect_uri are valid).
This is expected: when using the degraded mode, you must add custom code to validate things that are normally validated for you by OpenIddict, which includes the client_id
or redirect_uri
, that must be checked to ensure users are not redirected to unsafe/unknown addresses.
To fix that error, we'll need to register a handler for the ValidateAuthorizationRequest
event. Since we enabled the token endpoint, we'll also need one to validate token requests.
There are multiple ways to create and register event handlers in OpenIddict: you can create a dedicated class implementing the generic IOpenIddictServerHandler<TContext>
interface – which allows using dependency injection – or you can use inline event handlers.
To keep things simple, we'll use inline event handlers (directly defined in ConfigureServices
) and static hard-coded checks:
|
|
Final and most interesting part: gluing everything together, so that OpenIddict can redirect users to Steam and generate an authorization response containing an authorization code that the client application will be able to use to redeem an access token. To implement that, we need to use the HandleAuthorizationRequest
event:
|
|
Adding a handler for HandleTokenRequestContext
is not necessary: in this case, OpenIddict will automatically reuse the user identity extracted from the authorization code to produce an access token returned as part of the token response.
To test our minimalist OpenID Connect proxy, we'll now create a separate .NET Core 3.1 console referencing the IdentityModel.OidcClient
package:
|
|
There are typically 2 ways to handle authorization responses in desktop/mobile applications (i.e applications that don't run inside a browser):
Running a local HTTP server: this works well for desktop applications, but might be hard to implement in certain enterprise environments with strict firewall configurations.
Registering an application-specific URI scheme (e.g: myapp://
): this is the best approach... and pretty much the only option on most mobile operating systems, where the first option is not always possible, for security reasons.
Since the first option is easier to implement, it's the one we will choose for this demo client:
|
|
For that, start the two applications (server and client). Once the client is started, press a key to start the authentication process. When doing so, the default browser will be launched and you'll be redirected to the authorization server. If you're not already logged in, you'll be immediately invited to authenticate using your Steam account:
After logging in, an authorization response will be returned to the client console, that will automatically send a token request to finish the process:
And voilà, you're now ready to create your first APIs! To accept the JWT bearer tokens issued by OpenIddict, don't forget to decorate your controllers/actions with:
|
|
To make migration a bit less painful, Microsoft published a "transport security best practices" paper that list a few solutions that help avoid handshake errors related to the use of legacy TLS versions that are no longer considered safe.
One of the proposed solutions is to update your project to target .NET Framework 4.7: in this case, you'll have nothing else to do, as .NET 4.7 applications automatically default to whatever the operating system they run on offers and considers safe (which currently includes TLS 1.2 and will later include TLS 1.3).
Unfortunately, such an option requires re-compiling the application, which is not always feasible. Thankfully, you can also force an existing application to use the system default TLS versions without having to re-compile it (assuming it doesn't explicitly set the SSL/TLS versions it prefers via ServicePointManager
).
The best practices paper lists a few options, but my favourite one is the one that consists in simply updating the configuration file associated with the application executable, as it's easy to do and doesn't impact anything else on the machine.
For that, locate the configuration file associated to the executable of the application you want to add TLS 1.2 support to: it's always named [name of the executable].exe.config
. If there's no such file, create one. Once located or created, update its content to enable the compatibility switch required to support TLS 1.2:
|
|
If you're still using Windows 7, you'll also have to tweak the registry to enable TLS 1.2 support, as indicated on https://docs.microsoft.com/en-us/windows-server/security/tls/tls-registry-settings.
A few days ago, a vulnerability impacting application permissions was publicly reported by nurhat on GitHub (many thanks to him!).
In a nutshell, scope permissions were not correctly enforced for public clients using the password flow and custom grant types (confidential clients or clients using the code or client credentials flows were not impacted).
A fix was immediately added to the nightly builds and is present in the RTM release.
OpenIddict now comes with built-in entity caching to avoid having to send multiple requests to retrieve the same entities. Concretely, if your AuthorizationController
uses APIs like OpenIddictApplicationManager.FindByClientIdAsync(request.ClientId)
, the corresponding application will be directly retrieved from the cache and the resulting operation will be extremely cheap.
To ensure this feature works with non-thread-safe stores and stores that rely on context-affinity (like Entity Framework 6.x or Entity Framework Core), these built-in caches are scoped so that cached entities are not reused across requests.
While definitely not recommended, this feature can be disabled via the OpenIddict core options:
|
|
Based on feedback, the event model used by the server and validation handlers was slightly reworked so that it's now more explicit whether next handlers are allowed to be invoked by OpenIddict or not.
Concretely, both IOpenIddictServerEventHandler.HandleAsync()
and IOpenIddictValidationEventHandler.HandleAsync()
now return an enum value indicating whether the other handlers can be invoked. Here's an example of the new syntax:
|
|
|
|
|
|
OpenIddict supports both ASP.NET Core 1.x and 2.x so if you're still on the former version, no need to hurry: both versions basically offer the same feature set, with only a few API differences. For clarity, the OpenIddict packages use the 1.x
pattern for the ASP.NET Core 1.x-compatible version and 2.x
for ASP.NET Core 2.x.
Here's the complete list of packages published as part of this release:
ASP.NET Core version | Package name | Package version | Package description |
---|---|---|---|
1.x | OpenIddict | 1.0.0 | References the OpenIddict abstractions, core, server and validation packages. |
1.x | OpenIddict.Abstractions | 1.0.0 | Contains the common managers/stores interfaces used by OpenIddict. |
1.x | OpenIddict.Core | 1.0.0 | Contains the default managers implementations used by OpenIddict. |
1.x | OpenIddict.EntityFramework | 1.0.0 | Contains the Entity Framework 6.x stores (only compatible with .NET Framework 4.5.1). |
1.x | OpenIddict.EntityFramework.Models | 1.0.0 | Contains the Entity Framework 6.x models. |
1.x | OpenIddict.EntityFrameworkCore | 1.0.0 | Contains the Entity Framework Core stores. |
1.x | OpenIddict.EntityFrameworkCore.Models | 1.0.0 | Contains the Entity Framework Core models. |
1.x | OpenIddict.MongoDb | 1.0.0 | Contains the MongoDB 2.7.0 stores. |
1.x | OpenIddict.MongoDb.Models | 1.0.0 | Contains the MongoDB 2.7.0 models. |
1.x | OpenIddict.Mvc | 1.0.0 | Contains the OpenIddict/ASP.NET Core MVC integration components. |
1.x | OpenIddict.Server | 1.0.0 | Contains the OpenIddict server services. |
1.x | OpenIddict.Validation | 1.0.0 | Contains the OpenIddict validation services. |
2.x | OpenIddict | 2.0.0 | References the OpenIddict abstractions, core, server and validation packages. |
2.x | OpenIddict.Abstractions | 2.0.0 | Contains the common managers/stores interfaces used by OpenIddict. |
2.x | OpenIddict.Core | 2.0.0 | Contains the default managers implementations used by OpenIddict. |
2.x | OpenIddict.EntityFramework | 2.0.0 | Contains the Entity Framework 6.x stores (only compatible with .NET Framework 4.6.1). |
2.x | OpenIddict.EntityFramework.Models | 2.0.0 | Contains the Entity Framework 6.x models. |
2.x | OpenIddict.EntityFrameworkCore | 2.0.0 | Contains the Entity Framework Core stores. |
2.x | OpenIddict.EntityFrameworkCore.Models | 2.0.0 | Contains the Entity Framework Core models. |
2.x | OpenIddict.MongoDb | 2.0.0 | Contains the MongoDB 2.7.0 stores. |
2.x | OpenIddict.MongoDb.Models | 2.0.0 | Contains the MongoDB 2.7.0 models. |
2.x | OpenIddict.Mvc | 2.0.0 | Contains the OpenIddict/ASP.NET Core MVC integration components. |
2.x | OpenIddict.Server | 2.0.0 | Contains the OpenIddict server services. |
2.x | OpenIddict.Validation | 2.0.0 | Contains the OpenIddict validation services. |
Both OpenIddict 1.0 and 2.0 will be supported for as long as the ASP.NET Core version they are written for gets updates from Microsoft. You can find their support policy on Microsoft.com.
While I'll probably mostly focus on improving OrchardCore's OpenID module (which is based on OpenIddict) during the next few weeks, OpenIddict itself will also get updates, including NHibernate 5 stores (that will likely be OpenIddict 2.0-only as NHibernate doesn't offer a netstandard1.x
TFM that would be required to work with .NET Core 1.x).
Depending on the demand, stores for RavenDB or other databases might also be part of the next update. Don't hesitate to contact me if you'd like to see a particular database supported in the next version.
]]>As an avid WMC user, I have built 3 HTPC machines and recorded thousands of TV programs (movies, series, documentaries, etc.). As such, when Microsoft announced in May 2015 that Windows Media Center would no longer be developed (almost 6 year after disbanding the development team!) – and thus would not be part of Windows 10 – I refused to migrate to Windows 10, despite Microsoft's offers encouraging users to upgrade their OS for free.
The fact Windows Media Center was not available on Windows 10 – even as a paid feature like it was on Windows 8.x – was a major blocker for many users. To work around this limitation, enthusiasts decided to create an unofficial port of Windows Media Center for Windows 10.
While unofficial, the port worked just fine. But over time, Microsoft started to update some of the system components Windows Media Center relied on, causing annoying bugs. For instance, the introduction of breaking changes in Windows 10 1803 made watching a .wtv
file (WMC's TV file format) recorded on Windows 7 completely impossible.
While investigating, I discovered that the issue was caused by a change in MSVidCtl.dll
, the system-wide DLL containing the DirectShow components needed by WMC for all its TV-related features. After replacing the faulty DLL by an older version, WMC was able to play old recordings like a charm.
This phenomenom, that occurs every time API or functional changes are introduced in a DLL a program depends on, has a name: DLL Hell.
The thing is, replacing a system-owned DLL is far from ideal: while it solves the initial problem quite easily, it's not future-proof as any major Windows update will end up overwriting the replaced DLL. The same thing will happen if the user (or the system on his behalf) runs the sfc.exe
utility, that will detect the modification and revert it. It may also cause issues in other applications depending on new APIs that are only part of the newest version of the replaced DLL.
Since replacing a system DLL was not a good option, I eventually opted for the other approach: forcing Windows Media Center to load its own version of MSVidCtl.dll
instead of the global one stored in System32
.
Typically, you'd achieve that by simply dropping the DLL in the application's root folder (in our case, C:\Windows\ehome
). This usually works because the DLL loader first looks for the DLL in the directory the application was loaded from.
In this case, this didn't work: Windows Media Center kept loading the MSVidCtl
DLL contained in the System32
folder. Why? Because MSVidCtl.dll
is actually a very special DLL: it's a Component Object Model DLL. Unlike other DLLs, COM DLLs are almost never loaded via LoadLibrary
(at least, not directly): instead, they are assigned a GUID that corresponds to a special entry in the registry that contains the absolute path of the COM DLL. When an application wants to instantiate a COM component, it usually imports Ole32.dll
and calls CoCreateInstance
with the unique GUID: the COM loader locates the entry in the registry and loads the DLL from the associated path.
Since the path is absolute, trying to add a MSVidCtl.dll
in the ehome
folder was completely pointless: the loader would never look for it.
To mitigate this issue, Microsoft offers 2 mechanisms that allow overriding the default search order used by Windows' DLL loader even when the specified path is absolute:
DLL redirection: by creating an empty .local
file/folder whose name is the same as the executable (in our case, ehshell.exe.local
), one can easily force the OS to load DLLs from the application folder: it's dead simple and works flawlessly, even for COM DLLs. Unfortunately, it has a major caveat: it doesn't work when the application has an application manifest (there's a registry switch to override that but turning it on would impact the entire system, certainly not a good thing to do).
Side-by-side assemblies: first introduced in Windows 98 Second Edition, it allows an application to define its dependencies in its application manifest (and explicitly opt for a specific DLL version). DLL dependencies can be either stored in the same directory as the application (in this case, they are known as private assemblies) or in a special shared directory called winsxs
. Starting with Windows XP, it also allows defining registration-free COM components: unlike classical COM components, they don't have an entry in the registry and thus are not registered globally. Instead, they are defined in the application manifest: when the COM loader finds an entry in the manifest for the specified GUID, it loads the corresponding DLL and doesn't apply the default registry loading routine.
An application manifest is a special XML file that is either embedded in the executable or provided separately and that defines some core aspects of the application (e.g does it need to be executed with administrator rights?).
In our case, the first option is sadly not applicable: all the Windows Media Center executables come with an embedded manifest used to enable the DPI-aware mode. For instance, here's the manifest embedded in ehshell.exe
:
|
|
Removing the manifest to make the .local
approach work would be very easy, but we'd loose all the benefits of the DPI-aware mode: definitely not an option.
While powerful, defining registration-free COM components is far from being trivial and is rather error-prone.
TypeLib
tablesFirst, you need to extract the TypeLib
table from the COM DLL you want to use. For convenience, it's almost always embedded as a binary resource in the COM DLLs themselves. To extract these resources, you can use the popular Resource Hacker tool. In some cases, a single COM DLL might expose multiple TypeLib
tables: you'll have to extract them all and merge them later. MSVidCtl.dll
is one of them:
Then, you need to use the Manifest Tool coming with Visual Studio to generate the manifest files from the COM definitions.
For that, open a VS command prompt and run the following commands:
|
|
In a perfect world, you'd also use mt.exe
to merge the 2 manifests into a single one:
|
|
Unfortunately, the two manifests actually contain multiple elements with the same TypeLib
identifier, making them impossible to merge automatically:
Manifest2.manifest : manifest authoring error c1010001: Values of attribute "tlbid" not equal in different manifest snippets.
To work around this limitation, I manually merged the two XML files using Notepad++ and wrote a tiny C# script to remove the duplicate entries:
|
|
This is not done yet: if you try to use the resulting manifest as-is, Windows Media Center will simply crash as the mt
utility misses an important part of the COM definitions: the threading model of the COM classes.
The good news is that this information can be retrived from the registry of a Windows 7 machine (or on Windows 10 pre-1803). To make that process much easier, I wrote a second tiny script updating the manifest from the data found in the registry:
|
|
After cleaning the XML manifest a bit, here's what I ended up with:
|
|
This is the last step: writing the application manifest and importing the assembly manifest we just created.
For that, I took the original manifest embedded in ehshell.exe
, cleaned it up a bit, added the necessary parts and created ehshell.exe.manifest
with the resulting XML:
|
|
The Microsoft.Windows.Video.Tagging
dependency was generated using the same exact procedure, but using EncDec.dll
instead of MSVidCtl.dll
. For more information about this system DLL and why it's important for Windows Media Center, visit Windows 10 April Update: comment résoudre le problème de miniatures (Thumbnails) sous Windows Media Center (in French).
At this stage, it's important to note that external manifest files are NOT loaded if the executable already contains an embedded manifest. Since I preferred having an external manifest, I used Resource Hacker to remove the embedded manifest from ehshell.exe
and the other WMC executables.
Creating registration-free COM manifests to work around a breaking change in a system DLL is definitely a painful process (after all, it's called DLL Hell for a reason!). Fortunately, the result is worth it, as it makes an application safe from future DLL changes introduced in minor or major Windows versions.
Since I don't expect anyone to reproduce this painful process to fix their Windows Media Center setup, I decided to team up with Rémi Lachapelle – who ows the WindowsMediaCenter.fr blog – to offer an updated installer that includes the precious manifest and the MSVidCtl
/EncDec
DLLs needed for Windows Media Center to work properly with recent versions of Windows 10: keep an eye on his blog, it should be there shortly!
When doing so, I discovered that both their ASP.NET 4.x and OWIN/Katana SDKs were unfortunately prone to "session fixation", which is a form of cross-site request forgery allowing to force a victim to log in under an attacker's account.
Since it's a quite frequent vulnerability, here's a quick overview of what causes it and how you can concretly exploit it.
Auth0 was already aware of this issue internally and decided to switch to the official OWIN OpenID Connect middleware developed by Microsoft, which is not prone to this class of attack.
If you need to migrate, Auth0 has prepared a migration guide listing the steps required to replace Auth0-ASPNET-Owin
by Microsoft's OpenID Connect middleware.
The OAuth2 threat model RFC – a must read for anyone dealing with OAuth2 and OpenID Connect – gives an excellent definition of this threat and its practical implications:
Cross-site request forgery (CSRF) is a web-based attack whereby HTTP requests are transmitted from a user that the web site trusts or has authenticated (e.g., via HTTP redirects or HTML forms). CSRF attacks on OAuth approvals can allow an attacker to obtain authorization to OAuth protected resources without the consent of the user. This attack works against the redirect URI used in the authorization "code" flow.
An attacker could authorize an authorization "code" to their own protected resources on an authorization server. He then aborts the redirect flow back to the client on his device and tricks the victim into executing the redirect back to the client. The client receives the redirect, fetches the token(s) from the authorization server, and associates the victim's client session with the resources accessible using the token.
Impact: the user accesses resources on behalf of the attacker. The effective impact depends on the type of resource accessed. For example, the user may upload private items to an attacker's resources. Or, when using OAuth in 3rd-party login scenarios, the user may associate his client account with the attacker's identity at the external Identity Provider. In this way, the attacker could easily access the victim's data at the client by logging in from another device with his credentials at the external Identity Provider.
Usually, this (generally underestimated) threat is mitigated by correlating the authorization response with the authorization request: typically, by generating an unguessable value before redirecting the user to the Identity Provider and validating it before sending the token request.
Unfortunately, this kind of check is not made by the Auth0 ASP.NET 4.x and OWIN SDKs, making them vulnerable to this class of attack.
Reproducing the vulnerability is quite easy. For that, I wrote a simple self-hosted OWIN/Katana console application using the vulnerable Auth0-ASPNET-Owin
package.
The specific version doesn't matter much, as a quick look at the git history seems to indicate this vulnerability exists since the very first day: all versions are potentially vulnerable.
|
|
|
|
|
|
Thanks to Auth0's user-friendly portal, registering a new Web server application is quite straightforward. When created, don't forget to register http://localhost:[port]/signin-auth0
as a valid redirect_uri
:
Once the application is correctly registered, start the OWIN application and visit http://localhost:[port]/challenge
to start a new authentication flow. Doing that will redirect you to the Auth0 login page:
Then, create a new account that will be used as the attacker account and approve the authorization request. Once approved, you should be redirected to the OWIN application and the following message should appear:
You're logged in as [email address of your first account].
For that, visit http://localhost:[port]/challenge
a second time, create a new Auth0 account and approve the authorization request exactly like you did during the previous step.
At this point, you should be logged in as the victim:
You're logged in as [email address of your second account].
At this stage, the objective is to retrieve an authorization code associated with the attacker account. To do that, my preferred option is to use Fiddler and its breakpoints feature, that will allow you to abort the authentication flow before being redirected back to the OWIN application (and ensure the authorization code is not redeemed):
In a private browser window, visit http://localhost:[port]/challenge
, go back to Fiddler and allow the challenge request to be processed by clicking on Run to completion
.
After logging in as the attacker, you should be redirected by Auth0 back to the OWIN application with the precious authorization code:
Extract the authorization code from the query string and abort the request by choosing a fake response in Fiddler's list before clicking on Run to completion
:
Copy the attacker's authorization code and close the window.
Now, go back to your main browser window and visit http://localhost:[port]/
to confirm you're still logged in using the victim account.
Then, visit http://localhost:[port]/fix-session?attacker_authorization_code=[code]
. This page contains a malicious iframe that directly points to the redirect_uri
endpoint of the OWIN application. When visiting this page, you'll be immediately logged out of your main account – i.e the victim – and all requests will now be sent using the attacker's identity:
In a real world attack, this HTML page would be hosted by an attacker on a different server and would be embedded in a malicious site or directly reached by a victim after clicking on a link. Here, the /fix-session
endpoint is only provided for convenience.
Kudos to Marcin Hoppe and Jerrie Pelser for their promptness in getting back to me. While Auth0 doesn't have a real "bug bounty" program yet – which is frankly unfortunate for a such big player in the Identity sector (specially when vendors like Microsoft or Okta already have proper programs in place) – it's certainly a good thing to be able to reach them very quickly.
]]>With time, the core audience of OpenIddict has evolved a bit to not only include beginners but also developers who were already familiar with OAuth/OpenID Connect or had used OAuthAuthorizationServerMiddleware
or ASOS in the past. For them, the fact OpenIddict didn't allow them to take control of the request processing pipeline was often a blocker. With the introduction of OpenIddict RC3, we're changing that.
Using these advanced APIs is not recommended if you're not familiar with the OAuth/OpenID Connect specifications or with the events model used by the OpenID Connect server middleware. If you're not sure whether you should use these APIs, don't hesitate to reach us on Gitter or on GitHub.
The events model is structured around IOpenIddictServerEventHandler<TEvent>
and IOpenIddictValidationEventHandler<TEvent>
. These 2 interfaces represent handlers that are invoked every time an event of type TEvent
is triggered by the OpenIddict server or validation handlers.
At the time of writing, OpenIddict exposes 44 server events – grouped into the OpenIddictServerEvents
static class to make them easier to find – and 5 validation events, exposed under OpenIddictValidationEvents
.
Each event represents a specific moment in the request processing pipeline (e.g the moment the OpenIddict server determines whether the request is an OpenID Connect request it should handle, the moment it extracts it, handles it or returns a response).
Multiple handlers of the same type can be registered: they will be sequentially invoked in the same order as the one used to register them. As soon as a handler calls a method that indicates the request should no longer be processed (e.g HandleResponse()
or SkipHandler()
), OpenIddict will stop invoking the handlers and the next ones will be automatically ignored.
For security reasons, the custom handlers will be invoked by OpenIddict after its own validation routines. If a request is rejected by OpenIddict, your own handlers won't be invoked.
Creating an event handler is straightforward: pick the event you need and add a class that implements either IOpenIddictServerEventHandler<TEvent>
(for a handler that receives events triggered by the OpenIddict server services) or IOpenIddictValidationEventHandler<TEvent>
(for a handler that receives events triggered by the OpenIddict token validation services):
For instance, to return custom metadata in the discovery document, you'll need to implement IOpenIddictServerEventHandler<OpenIddictServerEvents.HandleConfigurationRequest>
:
|
|
To register it, use options.AddEventHandler<TEvent, THandler>()
(by default, the handler is registered as a scoped service):
|
|
Alternatively, if your handler can be trivialy implemented and doesn't use constructor injection, you can register it inline:
|
|
By default, OpenIddict uses a path-based endpoint resolution logic to determine whether the incoming request is an OpenID Connect request it should handle. This is done by comparing the request path to the endpoint paths registered in the OpenIddict server options. In some cases, you'll probably want to listen on multiple paths at the same time instead of a single one. For that, you can use the OpenIddictServerEvents.MatchEndpoint
event:
|
|
If you need to expose the external providers that are supported by your server application, you can use the HandleConfigurationRequest
event:
|
|
|
|
With OpenIddict, the "standard" way to process authorization or token requests is to have an authorization controller dedicated to handling these requests. Starting with RC3, this can also be done directly at the OpenIddict server handler level (e.g for those who don't need or don't want to use ASP.NET Core MVC).
Here's how it could be done by writing an OpenIddictServerBuilder
extension:
|
|
|
|
In some cases, flowing the access token in the HTTP request headers is not possible (e.g when using WebSockets with JS clients). To work around these limitations, you can transfer it as a query string parameter and configure the OpenIddict validation handler to use your extraction logic by adding an event handler for OpenIddictValidationEvents.RetrieveToken
:
|
|
In this release, we focused on reworking the OpenIddict registration APIs to offer a better user experience.
As part of this change, we split the OpenIddict services into three areas - Core
, Server
and Validation
- and the IServiceCollection
APIs have been updated to reflect that:
Each specialized builder only exposes the options that are relevant to its specific area:
Of course, the calls to AddCore()
, AddServer()
and AddValidation()
can be chained:
|
|
Introducing these specialized builders was also a great opportunity to revisit how the OpenIddict entities are registered. In the RC2 bits, this is controlled by the services.AddOpenIddict<...>()
method, that determines which entities are used depending on the overload.
In RC3, the generic services.AddOpenIddict<...>()
methods have been removed and replaced by a more explicit pattern:
Thanks to a great contribution from Chino Chang, OpenIddict now has its dedicated validation handler, based on the aspnet-contrib handler.
This handler supports both the default token format (opaque) and reference tokens. Like the aspnet-contrib handler, you can use it as a standalone handler (i.e without having to register the OpenIddict core or server services):
|
|
Resource servers that use reference tokens will have to configure the core services and register the appropriate stores to be able to use it:
|
|
The aspnet-contrib handler will continue to be fully supported and will still be usable with OpenIddict so existing applications can keep using services.AddAuthentication().AddOAuthValidation()
instead of services.AddOpenIddict().AddValidation()
for opaque token validation.
Note: OpenIddictValidationHandler
lives in the OpenIddict.Validation
package, which is referenced by the OpenIddict
metapackage. You don't have to add a new PackageReference
to be able to use it.
OpenIddict now natively supports MongoDB, one of the most popular NoSQL/document-oriented databases.
To configure OpenIddict to use MongoDB, reference the OpenIddict.MongoDb
package and call the options.UseMongoDb()
extension:
|
|
By default, the MongoDB stores will resolve the IMongoDatabase
service from the DI container so you'll have to register it using the usual ASP.NET Core DI extensions. E.g:
|
|
Alternatively, developers who work with multiple MongoDB databases in the same application will be able to explicitly set the one they want to use in the OpenIddict options:
|
|
If no database can be resolved, an exception will be automatically thrown at runtime.
Other helpers are available to allow you to customize the default entities or the collection names:
|
|
In the previous iterations of OpenIddict, an important effort was made to create a shared set of entities (contained in the OpenIddict.Models
package) that could be used not only by the official Entity Framework 6.x and Entity Framework Core stores, but also by third-party/custom stores.
This pattern had many pros - like avoiding code duplication or having base classes that simplify the development of custom stores (which is why it was also eventually adopted by the ASP.NET team for ASP.NET Core Identity in 2.0).
Unfortunately, this approach had also a major issue: we had to design the default entities as "lowest common denominators", so that they could be used by all/most ORMs or document databases. In practice, this meant that things like OpenIddictApplication.RedirectUris
or OpenIddictApplication.PostLogoutRedirectUris
had to be represented as JSON-serialized strings
for SQL-oriented ORMs like EF 6.x and EF Core to work natively.
In RC3, each stores package will come with its own models you'll be able to use exactly like in the previous iterations.
Make sure you don't reference the obsolete OpenIddict.Models
or OpenIddict.Stores
packages. The new models are automatically referenced by the stores packages they belong to and you don't need to add any reference to use them.
In RC2, we introduced application permissions. To make the migration from RC1 to RC2 smoother, application permissions were mostly optional and OpenIddict had a fallback mechanism called "implicit permissions" it used to determine whether an application could perform the requested action. For instance, if no permission was explicitly attached to the application, it was considered fully trusted and was granted all the permissions.
Similarly, if you granted the "token endpoint" permission to an application but NO "grant type" permission, it was assumed the client application was allowed to use the password or client credentials grants.
Retrospectively, this logic was too complex and I decided to remove it in RC3.
Starting with RC3, permissions are no longer optional nor implicit: if you don't explicitly grant an application the necessary permissions, it will be blocked by OpenIddict.
To attach permissions to an application, use OpenIddictApplicationManager
:
|
|
If you don't care about permissions (e.g because you don't have third-party clients), you can instead disable them:
|
|
Starting with RC3, OpenIddict will now enforce scope validation and reject token and revocation requests that don't specify a client_id
. In RC2, these checks were opt-in (enabled via options.EnableScopeValidation()
and options.RequireClientIdentification()
) ; in RC3, they are now opt-out.
If, after migrating to RC3, you see errors similar to these ones:
invalid_scope : The specified 'scope' parameter is not valid.
Simply add the scopes you want to use to the list of registered scopes:
|
|
invalid_request : The mandatory 'client_id' parameter is missing.
Add an application entry for the client application and send the corresponding client_id
as part of the token request:
|
|
If you prefer disabling these checks, you can use options.DisableScopeValidation()
and options.AcceptAnonymousClients()
:
|
|
Note: if you already use options.EnableScopeValidation()
and/or options.RequireClientIdentification()
in your code, you can safely remove these calls.
In this release, we also made debugging easier by adding custom exception messages instead of relying on the rather cryptic DI-related messages thrown by ASP.NET Core.
If you forget to register stores, you'll now get a much clearer exception:
System.InvalidOperationException : No application store has been registered in the dependency injection container.
To register the Entity Framework Core stores, reference the
OpenIddict.EntityFrameworkCore
package and callservices.AddOpenIddict().AddCore().UseEntityFrameworkCore()
.To register a custom store, create an implementation of
IOpenIddictApplicationStore
and useservices.AddOpenIddict().AddCore().AddApplicationStore()
to add it to the DI container.
If you use an entity that is not compatible with the underlying store, you'll also get a better exception:
System.InvalidOperationException : The specified application type is not compatible with the Entity Framework Core stores.
When enabling the Entity Framework Core stores, make sure you use the built-in
OpenIddictApplication
entity (from theOpenIddict.EntityFrameworkCore.Models
package) or a custom entity that inherits from the genericOpenIddictApplication
entity.
Similarly, if you forget to register the core services when enabling the server or validation components, you'll get an exception:
System.InvalidOperationException : The core services must be registered when enabling the server handler. To register the OpenIddict core services, use
services.AddOpenIddict().AddCore()
.
System.InvalidOperationException : The core services must be registered when enabling reference tokens support. To register the OpenIddict core services, use
services.AddOpenIddict().AddCore()
.
OpenIddict RC3 will be the latest release candidate and RTM will be the next step.
]]>I'd like to thank all the contributors – 47 at the time of writing – who helped make that possible, with a special mention to Chino Chang, who did a fantastic job by manually porting most of the social providers to the ASP.NET Core 2.0 authentication stack: sir, you rock!
Each provider comes with 2 versions: one for ASP.NET Core 1.x and one for ASP.NET Core 2.x. You can find the complete list at the end of this post.
Both versions will receive security updates/bug fixes but new providers will be backported to ASP.NET Core 1.x only if there's enough demand. If you still run an ASP.NET Core 1.x application, let us know!
Package name | Version for ASP.NET Core 1.x | Version for ASP.NET Core 2.x |
---|---|---|
AspNet.Security.OAuth.Amazon | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.ArcGIS | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Asana | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Autodesk | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Automatic | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.BattleNet | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Beam | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Bitbucket | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Buffer | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.CiscoSpark | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.DeviantArt | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Discord | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Dropbox | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.EVEOnline | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Fitbit | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Foursquare | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.GitHub | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Gitter | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.HealthGraph | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Imgur | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Instagram | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.LinkedIn | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.MailChimp | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Myob | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Onshape | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Patreon | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Paypal | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.QQ | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Reddit | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Salesforce | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Slack | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.SoundCloud | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Spotify | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.StackExchange | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Strava | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Twitch | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Untappd | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Vimeo | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.VisualStudio | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Vkontakte | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Weibo | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Weixin | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.WordPress | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Yahoo | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Yammer | 1.0.0 | 2.0.0 |
AspNet.Security.OAuth.Yandex | 1.0.0 | 2.0.0 |
Package name | Version for ASP.NET Core 1.x | Version for ASP.NET Core 2.x |
---|---|---|
AspNet.Security.OpenId | 1.0.0 | 2.0.0 |
AspNet.Security.OpenId.Steam | 1.0.0 | 2.0.0 |
Starting with RC2, using OpenIddict with third-party client applications (i.e applications you don't own and are managed by someone else) is officially supported. For that, new features – that were still work in progress in the previous iterations – have been added:
|
|
For more information about this feature, you can read the corresponding documentation.
options.RegisterScopes([list of authorized scopes])
or dynamically, using OpenIddictScopeManager.CreateAsync()
:
|
|
|
|
|
|
OpenIddictScopeManager
methods have been introduced to allow associating resources (aka API audiences) with specific scopes and retrieving all the resources corresponding to a set of scopes:
|
|
|
|
OpenIddictAuthorizationManager
methods have been added to make authorizations easier to create or retrieve:
|
|
|
|
OpenIddictApplication.RedirectUris
, OpenIddictApplication.PostLogoutRedirectUris
and OpenIddictAuthorization.Scopes
are now serialized as JSON arrays in the database.Before updating your packages, read the migration guide. It explains how to add an Entity Framework Core migration to update the OpenIddict tables and includes a migration script to convert the RedirectUris
, PostLogoutRedirectUris
and Scopes
columns to the new JSON format.