Building a centralized OAuth server With OpenIddict in .NET

Server

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.

OpenIddict

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.

Register authentication

Authentication Endpoint

Here is the implementation of the “/authorize” endpoint.

Authentication Endpoint

  1. Fetching the Request using the GetOpenIddictServerRequest() method.
  2. Extract the parameters that we have passed from the client while initiating the “/authorize” request.
  3. Next, get the authentication result based on the cookie.
  4. Fetch the client application based on the ClientId in the request after successful authentication.
  5. Implement user identity and create the ClaimsPrinciple.
  6. Return the SignIn Result with ClaimsPrinciple and the cookie scheme “OpenIddictServerAspNetCoreDefaults.AuthenticationScheme”

Token Endpoint

Here is the implementation of the “/token” endpoint.

Token Endpoint

  1. Fetching the Request using the GetOpenIddictServerRequest() method.
  2. Check the “grant_type” should be “authorization_code".
  3. Next, get the authentication result based on the cookie.
  4. Build the user identity with required claims and scopes.
  5. Return the SignIn Result with ClaimsPrinciple and with cookie scheme “OpenIddictServerAspNetCoreDefaults.AuthenticationScheme”
  6. 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.
  7. 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.

External Identity

For client_id and client_secret register your application here.

Callback Endpoint

Let's see this endpoint code in a few steps.

Callback Endpoint

  1. On successful authentication, the OpenIddict server inserts the cookie in the user's browser OpenIddictClientAspNetCoreDefaults.AuthenticationScheme.
  2. The next step is to check the authentication result with the OpenIddictClientAspNetCoreDefaults.AuthenticationScheme.
  3. After receiving the successful authentication result, we will build the user identity to return this SignIn Result.
  4. Upon successful authentication, I also create a user if the user does not exist in the user’s table.
  5. 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

  • ASP.NET Core Web API Application template in Visual Studio.

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.

EF core migration

Client Application Configuration With OpenIddict

Client Application

References for further reading about oAuth


Similar Articles