OpenIddict, an open-source library, was the foundation for developing a comprehensive authorization server application.
Those unfamiliar with OpenIddict can find more information here.
Understanding OpenIddict
OpenIddict is an open-source library designed to implement OpenID Connect and OAuth 2.0 protocols in ASP.NET Core applications. It furnishes developers with a flexible and extensible framework to incorporate authentication and authorization capabilities into their web applications, APIs, and services.
Key Features Supported by OpenIddict
- OpenID Connect and OAuth 2.0 Support
- Token Management
- Security Features
- Integration with External Identity Providers
- Authorization Flows
Authorization flows
With OpenIddict, you can explore various authorization flows. OAuth offers several authorization flows, among which we will focus on implementing the Authorization Code Flow for our identity service.
- Resource Owner Password Credentials Flow (not recommended for new applications)
- Client Credentials Grant (recommended for machine-to-machine communication)
- Authorization Code Flow (recommended for new applications)
- Implicit Flow (not recommended for new applications)
- For information on other flows, refer to this resource.
You can read more about flows here.
Understanding the authorization code flow
The authentication process is initiated by the client constructing the "/connect/authorize" endpoint, which we will delve into later in the implementation.
Subsequently, the authorization server receives the request on this endpoint and validates the user cookies. Cookies play a vital role in this process, as our authentication process revolves around them.
The cookie validation process involves several steps.
- Check the authentication result and verify the cookie expiry.
- Verify if the cookie is present in the user's browser; if not, return false.
- If the cookie is present, check its expiry.
Based on this result, the flow continues. If unsuccessful, a challenge result will redirect the user to the login page. Upon entering their credentials, which are validated by Identity in the login post action:
If the user exists and the password is correct, set the cookie to “CookieAuthenticationDefaults.AuthenticationScheme” from the Login page post-action. Subsequently, redirect the user (from where the challenge result was issued) to "/connect/authorize" with the cookie now present in the user's browser.
Next, at the “/connect/authorize” endpoint, validate the cookie and build the user identity with claims and scopes. The result, along with the following parameters, is returned.
- grant_type: authorization_code
- code: code
- client_id: client id
- client_secret: client-secret
- redirect_uri: callback URL
This exchange occurs to obtain the access token from the “/connect/token” endpoint.
The token endpoint receives the request and validates the result again using the “OpenIddictServerAspNetCoreDefaults.AuthenticationScheme” set previously. Subsequently:
Rebuild the user identity and override the user claims present in the principal if they have changed since the authorization code refresh token was issued. Returning a SignInResult prompts OpenIddict to issue the appropriate access/identity tokens.
This overview covers the theoretical aspects of the authorization workflow.
Let’s dive into the practical and see how we can implement it.
Setting Up the Identity Service Project
- ASP.NET Core Razor Application template in Visual Studio.
- Configuring PostgreSQL as the database.
- Setting up Entity Framework for data access.
- We will use Microsoft identity for user management.
Install the OpenIddict Required Packages
Open the Nuget package manager and install the packages below.
- OpenIddict.AspNetCore
- OpenIddict.EntityFrameworkCore
Configuring OpenIddict in ASP.NET Core
In this code block, OpenIddict services are configured in an ASP.NET Core application. Enable the authorization and token endpoints. Register the signing and encryption credentials.
Register authentication scheme. You can configure your login page URL. In my case, I’m using Microsoft Identity for user management, to manage that I’m using identity pages.
Authentication Endpoint
Here is the implementation of the “/authorize” endpoint.
- Fetching the Request using the GetOpenIddictServerRequest() method.
- Extract the parameters that we have passed from the client while initiating the “/authorize” request.
- Next, get the authentication result based on the cookie.
- Fetch the client application based on the ClientId in the request after successful authentication.
- Implement user identity and create the ClaimsPrinciple.
- Return the SignIn Result with ClaimsPrinciple and the cookie scheme “OpenIddictServerAspNetCoreDefaults.AuthenticationScheme”
Token Endpoint
Here is the implementation of the “/token” endpoint.
- Fetching the Request using the GetOpenIddictServerRequest() method.
- Check the “grant_type” should be “authorization_code".
- Next, get the authentication result based on the cookie.
- Build the user identity with required claims and scopes.
- Return the SignIn Result with ClaimsPrinciple and with cookie scheme “OpenIddictServerAspNetCoreDefaults.AuthenticationScheme”
- Return a SignInResult from the token endpoint, essentially instructing OpenIddict to issue the appropriate access and identity tokens based on the provided claims. The OpenIddict server middleware handles this process internally, generating and issuing the tokens according to the configured options and settings.
- Behind the scenes, the OpenIddict server middleware will validate the incoming request, authenticate the client, validate the user's credentials (if applicable), generate the appropriate tokens (access token, identity token, refresh token), and return them in the response to the client.
Login With External Identity Provider
To configure the external identity provider register the client with your OpenIddict server configuration.
For client_id and client_secret register your application here.
Callback Endpoint
Let's see this endpoint code in a few steps.
- On successful authentication, the OpenIddict server inserts the cookie in the user's browser OpenIddictClientAspNetCoreDefaults.AuthenticationScheme.
- The next step is to check the authentication result with the OpenIddictClientAspNetCoreDefaults.AuthenticationScheme.
- After receiving the successful authentication result, we will build the user identity to return this SignIn Result.
- Upon successful authentication, I also create a user if the user does not exist in the user’s table.
- After our authentication flow, we will again follow the same authorized endpoint flow and then the token endpoint flow to return the access_token to the user.
Setting Up the Web API Project
Client Application Registration and Scopes
The “client” is the app that wants to interact with our identity service. For that, we need to register it with OpenIddict in our identity service database. After running the EF core migration with openiddict configuration, you will 4 tables in your identity service database.
Client Application Configuration With OpenIddict
References for further reading about oAuth