How to Secure Applications With OAuth + OpenID Connect + IdentityServer4

Demian Sclausero
Flux IT Thoughts
Published in
10 min readJun 10, 2021

--

For some time now, we’ve started building distributed and autonomous applications and applying architecture theories with well-defined contexts such as microservices. However, we are still used to developing and using applications in which their security and domain are in the same place, which poses new challenges when it comes to working with technologies and architectures in which everything is distributed in different domains, servers, and environments (both regarding the client applications and the different backends).

Before delving into OAuth 2.0 and OpenID Connect, we should take a look at the following key concepts: authentication and authorization.

Authentication is the act of verifying identity; that entails validating that users are who they claim to be. To authenticate their identity, users usually enter a username and a password.

On the other hand, authorization is a process that entails giving users permission to perform different actions. For example, users may add values to a list, but they can’t delete the list. Authorization follows user authentication.

What’s the purpose of these processes? Accessing our resources. When we talk about resources, we refer to a data list, a data set, something that provides a service or an application, which is stored and, to access it, we must be previously authenticated and/or authorized.

There may be cases in which clients communicate with each other or they communicate with one or more APIs (which, at the same time, communicate with other APIs). This is where tokens come into play. In these cases, having security implemented within our APIs is not enough. When there are several clients or APIs involved, our APIs’ security cannot fulfill all the clients' requests. Let’s also take into account that there are authentication methods that send usernames and passwords through HTTP, making our applications vulnerable.

So, how can we solve the issue of having too many APIs for several clients and still be able to “move” among them securely? With a token. Tokens are related to consent and permission: users delegate their permission to a client application to access an API and obtain resources on their behalf.

Thanks to tokens, we can stop using client credentials at an application level since security is centralized. Thus, we only need to enter the username and the password once; these will be hidden and travel through the token among requests to different APIs since tokens are secure enough to carry out the authentication and authorization processes in different applications. As long as the token is valid, access is guaranteed.

Hence, security must be centralized and, for that purpose, we need to use an authentication server that gathers all the requests and works as a token validator. That means that all the APIs and clients will ask this IDP (identity server) to validate whether the received tokens are valid. Therefore, the security task lies in only one place: the IDP.

In other words, we need to centralize the user management to have a single user and password control. This brings us to the core of this article:

IdentityServer4, OpenID Connect, and OAuth2 will help us manage all things related to authentication, authorization, and token creation, besides also validating tokens through an API or a client.

OAuth2

It was created as a means of fulfilling the security needs of distributed applications. OAuth2 is an authorization protocol, which means that it provides access to a resource. It’s as if I had a ticket for a show at a theatre: the security guard that’s at the entrance only needs a ticket to let me in. That’s how OAuth2 works: it doesn’t mind if I am who I claim to be but whether I have the “ticket” (token) to give me access to a resource. It provides a secure access delegation on behalf of a resource owner.

Let’s take a look at the main OAuth components:

  • Protected Resource: it’s the resource we want to access, an API.
  • Client: it’s the application that is trying to access the protected resource on behalf of the resource owner (it may be a web, mobile, or desktop application, an app for a smart TV, an IoT device, etc.)
  • Resource Owner: it’s the user. It’s called “owner” because, although it doesn’t own the API, it owns the data handled by it.
  • Authorization Server: it manages authorization requests. In our case, it’s IdentityServer4.

These four elements are connected to each other in the following way:

How Auth 2.0 Works

  1. The application requests that users register.
  2. Users are redirected to the authorization server for their identification. They may log in through a username and password, facial or voice recognition, or there may be a second authentication factor. Access through social media accounts such as Google, Facebook, or Amazon may also be allowed. Once users have been validated, they have to agree with what the application intends to do (this is called consent). A list with the permissions the application is requesting is usually shown
  3. Once the user identity is successfully validated, and users have agreed to what that authorization will be used for, they are redirected back to the client application, receiving a code which confirms they authorize the app to do things on their behalf regarding the protected resource.
  4. The client application makes a request to the authorization server, using the permission granted by the user and some information that identifies the application to verify that it’s a valid client allowed to access the requested resources.
  5. If things go well, the authorization server will return an access token.
  6. With that access token, the client application will be able to call the target API to fulfill its task.
  7. Once the protected resource receives the access token, it has to verify it. Once it has been validated, the protected resource checks whether the user has the permissions it had requested and, if things go well, the API will return the requested data.

Now we know about the OAuth2 standard, we can learn about OAuth2 authorization flows.

Here https://aaronparecki.com/oauth-2-simplified/#authorization we can see all the authorization flows, but first: what is a flow?

In OAuth2, flows determine how tokens are returned, but first, we have to focus on two types of clients: the confidential client and the public client (sometimes called “Back-channel” or “Front-channel” clients).

The confidential client is the one that can maintain its credentials’ confidentiality and be authenticated securely. This is so because it keeps the credentials safe without access to the user.

On the other hand, the public client is not capable of keeping a client password confidential. An example is a JavaScript application that runs in a browser because it can’t be securely authenticated.

There are several flows to carry out the authentication process, but the following are the most common ones:

A. Implicit Flow

  1. It is deprecated.
  2. It’s interactive.
  3. It’s meant for webs, only for SPA. The token is stored in the browser.
  4. The URL callback is important.
  5. The token must be long-lived: refreshing is not allowed because it wouldn’t be safe.

B. Authorization Code Flow

  1. It’s interactive: the user has to enter its credentials.
  2. It’s meant for webs: it uses redirection.
  3. Ideally, it should have a back end.
  4. It’s recommended to replace the Implicit Flow.
  5. A code is generated in the front channel (not secure communication)

C. Authorization Code Flow + PKCE

  1. It’s the most secure one.
  2. It’s a CODE extension.
  3. A challenge and a method are added to the initial request.
  4. The code verifier and method are included to request the token.

D. Client Credentials Flow

  1. It can’t be used by a user: it’s meant for communication among applications or APIs, always through a backchannel (secure communication)
  2. It uses refresh tokens.
  3. It uses backchannel communication (secure communication)

E. Hybrid

  1. It’s a combination of Implicit Flow and Authorization Code.
  2. It allows us to use a combination of identity token, access token, and code through a front channel, using fragment encoded redirect (native and JS-based clients), or forms (web applications on a server).
  3. Tokens are returned from the authorization and token endpoint, according to the response_type in the authorization endpoint request.

F. Resource Owner

  1. It’s used for legacy flows
  2. It’s the only flow that works without browsers
  3. It involves handling user credentials.
  4. It uses refresh tokens.

The most appropriate one is the Authorization Code + PKCE flow:

There are several kinds of tokens, but the most common ones are JWT (JSON Web Tokens), reference tokens, and, in some flows, refresh tokens.

JWT enables secure data exchange since it’s a JSON object. It’s a self-validated token since part of the signature checks whether the token has been altered.

A JWT has 3 main parts:

  1. Header: It contains data about the type of token and the cryptographic algorithms used.

2. Payload: it contains all the claims we’ll send through the token as well as its expiration time.

3. Signature: it’s used to verify whether the token is valid. The token’s authenticity is also checked, thus validating that it hasn’t been tampered with.

Since the JWT is signed and we don’t need another entity to validate it, this sort of token allows us to revoke it: we just have to wait until it expires; that’s why we should use a short expiration time.

Unlike the JWT, the reference token has to be validated by the IDP. That’s why it can be revoked.

Last but not least, the refresh token is used to generate a new access token. If the access token expires, the user will have to be authenticated again to obtain a new access token. The refresh token allows us to skip that step and, through an API request, it obtains a new access token that allows the user to keep accessing the application’s resources.

The refresh token requires more security than the access token when it’s stored because, if subtracted, it could be used to get an access token and, thus, access protected resources. To avoid that scenario, we have to implement a system that allows us to revoke a refresh token in the server and also establish an expiration time that should be longer than the one for access tokens.

OpenID Connect

We’ve previously learned that OAuth enables access to resources and different applications, but what happens if I don’t only want to get access but I also want to find out if the user that is accessing is who he/she claims to be? That’s why we need to add one more layer on top of OAuth. Going back to the previous theatre ticket example, it’s like saying that I need to know if the person who has the ticket is actually Demian Sclausero.

How do we do that? We add scopes on top of the OAuth layer, which will define the user’s information. That will allow us to obtain information about the user, such as the e-mail or the ID.

This protocol defines the user’s personal information and applies other access policies. For example, role-based access.

IdentityServer4

Once we understand the theory and how OAuth2 and OpenID work, we have to take a third aspect into account: who provides that physical authentication mechanism? That’s why IdentityServer4 was created.

IdentityServer is a middleware that’s compatible with OpenID Connect and OAuth 2.0 specifications, exposing endpoints to obtain tokens and manage security.

When an application that contains login and logout pages -and even a consent page, according to the requirements- is developed, the IdentityServer middleware fulfills those needs, adding endpoints to ensure secure communication and comply with the requested standards.

To get started with IdentityServer 4, .NET allows us to download a template with everything we’ll need for the development.

First, we have to write this in a console:

It will show us all the templates available for download:

Here we can see all the available templates to get started. Once we’ve chosen the template we need, we download it to start using it.

With the dotnet new is4ef -n AerolineasSecurity command, we are downloading IdentityServer4 and naming the project “AerolineasSecurity”.

Once we’ve downloaded it, we open the project and we’ll be able to see its basic structure:

The Config.cs class has the first Identity configuration approach, where we can see:

  1. Scopes: they delimit access. Each scope indicates where users are allowed to access.
  2. Clients: they define the flows.

3. In this case, we can see a ResourceOwnerPassword flow (the AllowedGrantTypes property is the one that determines the access flow), used to access with a username and password. It has its CORS and lifetime configurations, as well as a list of allowed scopes. We can also see the IDS4 defaults, the use of OpenID Connect, and the Offline_Access that determines the use of the refresh token.

To sum up, when we work with applications distributed in both domains and servers, it’s difficult to ensure security in each of those distributions. To support and solve this issue, OAuth comes into play because its 2.0 version provides security outside our application or service to manage all components involved, delegating the user permission through a token. That is what authorization is about.

If we also want to find out who is consuming that service, there has to be an authentication process. Said process is carried out through OpenID Connect, which adds a layer on top of OAuth to identify the user.

But those two processes cannot occur without physical support: an identity server. That’s why we use IdentityServer, which, in its fourth version, is the middleware that connects the user and the security.

Know more about Flux IT: Website · Instagram · LinkedIn · Twitter · Dribbble · Breezy

--

--