Discover the true costs of ecommerce platforms in our free guide.
See how industry leaders succeed with Virto.
Boost ecommerce with advanced marketing.
Security is one of the core questions when building any websites and applications. A standard solution for automated authorization and access today is OAuth 2.0. There are many frameworks for ASP.NET Core apps to get secured this way, and one of them is OpenIddict. In this article, we will explain how OpenIddict works with the Virtocommerce platform, and provide real examples of integration and use.
At Virto Commerce platform, an example of asp net open source ecommerce, we take advantage of our good third-party library and use OpenIddict, which provides a simple and easy-to-use solution to implement an OpenID Connect server to access RESTful endpoints from Single Page Applications (SPAs), native clients, using bearer token authentication. These types of applications do not work with cookies but can easily retrieve a bearer token and include it in the authorization header of subsequent requests.
To enable token authentication, ASP.NET Core supports several options for using OAuth 2.0 and OpenID Connect. In more detail, ASP.NET Core comes with two built-in OAuth 2.0 and OpenID Connect client handlers (to act as a relying party) and also offers a JWT bearer authentication handler for token validation, but nothing to act as an OAuth 2.0/OIDC server (i.e., an identity provider). However, they aren't sufficient for cases when you need to issue security tokens for local ASP.NET Core Identity users rather than using an external identity provider, which the Virto Commerce platform actually does.
Openiddict is an open-source framework used to build servers in ASP.NET Core applications. It fully complies with OAuth 2.0 and OpenID Connect and even supports legacy ASP.NET apps.
OpenID Connect is a layer usually used by third-party apps to verify the end user's identity and get basic profile information before starting other processes. Basically, it means that the end user can use a single sign-on to access different related apps. OpenID Connect is totally free and can be used by anyone. It works in a transparent and clear way, just like OAuth:
Virto Commerce is an open-source platform for building extensible ecommerce applications. We provide complex digital commerce solutions for B2B, B2C, and B2B2C businesses, marketplaces, and delivery of the platform as cloud-ready or SaaS.
The architecture of the Virto Commerce B2B-First .NET Platform
Virto Commerce architecture was designed to help the development team focus on the implementation of business features without having to worry about CLEAN ARCHITECTURE.
In 2019, as part of the update project from ASP.NET (.NET 4.6) to ASP.NET Core (.NET Core), the Virto Commerce team faced the challenge of replacement for AspNet.Security.OpenIdConnect.Server (ASOS). It is a low-level OpenID Connect OWIN/Katana that had been used for securing and token authentication for Virto Commerce Web API before that was included as part of ASP.NET Core and worked on even the most recent versions.
We chose the JWT token for authentication for the Virto Commerce platform RESTful endpoints because of the following advantages:
We rejected some stateful (session) tokens that OpenIddict provides in the early version based on the following:
Here are the main requirements by which we were guided:
There were only two open-source projects at the moment, including IdentityServer and OpenIddict, but we rejected IdentityServer since it hadn't adopted well to .NET Core. Also, OpenIddict is a little bit more low-level than IdentityServer. IdentityServer gives you a running solution out of the box, whereas for OpenIddict to work, you need to implement some details yourself. It was an ideal solution for us at that moment because “less is more”, and we didn’t want to introduce extra complexity to our solution.
Finally, we continued with OpenIddict since it satisfied all our requirements and was natively integrated with the ASP.NET Core Identity membership system. As it turned out, in the future, the creators/maintainers of IdentityServer decided to dual-license future versions of IdentityServer. Now, unless you are working on an open-source project, you will have to pay for a commercial license, so we did not make a wrong choice (as it seems to us).
An example would be a recent blog post about IdentityServer alternatives, which they called "bare metal"; see this here.
Kévin Chalet, author of OpenIddict, commented: "In 2020, ASOS was merged into OpenIddict 3.0 to form a unified stack under one library, which we no longer needed."
Technically, ASOS should work on even the most recent ASP.NET Core versions, but as mentioned here, its deprecation was motivated by two aspects:
1) ASOS, the aspnet-contrib token authentication handlers and OpenIddict were in three separate repositories, and it was time-consuming to keep the three repositories in sync when introducing changes. Moving to a single product/monorepo approach greatly simplified the development story.
2) ASOS actually had two flavors: an ASP.NET Core version and an OWIN/Katana one. While very similar, the two versions didn't share much code. Merging ASOS into OpenIddict and making OpenIddict host-agnostic allowed introducing native OWIN/Katana support in OpenIddict without having to keep two separate but similar packages like ASOS. In OpenIddict 3.x, there's now a main "server" package and two ASP.NET Core/OWIN hosts that only contain the code needed to integrate OpenIddict with ASP.NET Core or OWIN. It's a much more efficient approach.
Because the desired requirements were to be implemented in different ways, we divided our work into four parts:
In most cases, the Virto Commerce platform plays the Authorization and Resource server roles at the same physical instance, so we must include the extra configuration into the application to configure the resulting application, depending on its role.
This code is almost identical to the original sample, but contains some code that stores a limited set of user permissions in the user cookies session in order to implement Hybrid (Cookies + Bearer) authorization.
Along with a JWT token, Virto Manager also still uses cookie-based authentication. This additional check is necessary due to the impossibility of intercepting and injecting the Authorization header with a bearer token for all API calls that are called not through the $http service. These calls can be produced by other third-party JS components; direct http links and cookie-based authorization are used to solve this problem.
When the user is authorized in the platform, the system intersects all user permissions with permissions described in Authorization: LimitedCookiePermissions and adds them into cookies along with issuing the JWT token. When the user makes a request to the platform, they are challenged against the helper cookie and the authentication token by following rules.
Since the access token issued by an application has a limited lifetime, it’s important to provide a good user experience and not push the user to the login form every time. The user has to authenticate only once through the web authentication process. Subsequent reauthentication can take place without user interaction using the refresh token. The refresh token is a special kind of token used to obtain a renewed access token. Implementing a refresh token flow with a session on your own is quite a challenging task and carries the risk of making unwise decisions that might introduce security vulnerabilities to the product.
Luckily, OpenIddict supports refresh token flow out of the box, naturally fitting into the existing Virto Commerce (VC) platform infrastructure. We didn’t do anything for this and used it as-is.
All requests that are passing from the VC-manager SPA application include authorization header with actual JWT token value.
Virto Commerce platform authenticates and authorizes the app rather than a user. For this scenario, typical authentication schemes like username + password or social logins don't make sense.
Instead, Virto Commerce apps use the Client Credentials Flow (defined in OAuth 2.0 RFC 6749, section 4.4), in which it passes along their Client ID and Client Secret for authentication to get a token.
OpenIddict provides native support of Client Credentials Flow along with API and storage to manage registered applications and authorize the clients by stored ClientId & ClientSecret pair. All we need to do is make our own UI.
In some deployment scenarios, when they are running multiple platform instances, one of them usually plays an authentication server role and has access to user accounts’ storage. Other platform instances play a role as resource servers that simply need to limit access to those users who have valid security tokens provided by an authentication server.
The Shared Token Authentication Map
Because of custom claims and OpenID Connect protocol offers, we can do any authorization check on Resource servers without access to the user data and work autonomously.
As Kévin Chalet, author of OpenIddict, said, "we still have to use Microsoft.AspNetCore.Authentication.OpenIdConnect for communicating with OIDC provider."
OpenIddict only provides the server and token validation parts (though we plan to create a client in the future, as I'm not 100% satisfied with existing options), so if you need to communicate with an external OIDC provider, you'll still need Microsoft.AspNetCore.Authentication.OpenIdConnect. Using OpenIddict as a proxy between your clients and an external provider is also possible, but you'll want to implement an interactive flow like the "code flow" to support this scenario.
Overall, we are quite happy with how we eliminated much of the work related to authorization and authentication tasks in our project. This is thanks to OpenIddict’s great integration with ASP.NET Core services and comprehensive documentation and sampling, along with the support of the author and soul of this project, Kévin, because of whom we could focus on other things during our development, and tackle such complex subjects as security in the easiest way.