How to Implement OAuth OpenID Connect Using Identity Server
While building any application, the most challenging part will be implementing authentication and authorization properly.
In the past, a vast majority of web applications used login forms with cookie-based authentication schemes to perform authentication/authorization.
The problem with this approach is as follows:
- Authentication/Authorization logic is mixed with the application logic, so the application is tightly coupled with the implementation.
- We cannot share the authentication/authorization logic across multiple applications, and eventually, we must duplicate it in many places.
- End-users must create, remember and maintain individual username/password combinations for every application, which is another headache.
- Most of the time, users will share the same credentials across multiple applications, which will be a security risk if one of the applications is compromised. If this happens, then all other applications will automatically get exposed as well.
- To avoid most of these issues, people started adopting token-based authorization schemes like OAuth/OpenID Connect. The recommended approach for most modern application architectures is to use these token-based schemes to implement authentication/authorization, which will decouple authorization logic from application logic.
What is OAuth (Open Authorization)?
It is an open standard for delegated authorization. In another way, it is the standard for allowing applications/websites to perform certain operations with the context of the actual user.
What is OIDC (Open ID Connect)?
Open ID Connect is an authentication layer built on top of OAuth 2.0.
OAuth meant to provide delegated authorization for protected resources. However, later, people misused it to implement authentication as well as using OAuth. So, to support authentication, they introduced a thin layer on top called OpenID Connect which will provide a way for performing authentication.
Before diving deep into OAuth/OpenID Connect, let us understand the difference between Authentication and Authorization.
What is Authentication?
Authentication is the process of proving identity credentials to access protected resources.
What is Authorization?
Authorization is a process of proving what operations can be performed on the protected resources.
OAuth Version History:
- OAuth 1.0
- OAuth 2.0
- OAuth 2.1
Using an Identity Provider (IDP) and Authorization Server:
An Identity Provider (IDP) is a system that stores and manages digital identities. The server where IDP is available is usually called the Authorization server. It is used to capture and store a user's identity information like username, password, email, address, etc.
Whenever a client needs to authorize a user, it will perform an HTTP redirect request to the authorization server and verify the incoming request's identity.
If the user already signed up with the identity provider, it simply checks the credentials. If it matches exactly, it will issue an id_token and the user information in a signed manner.
If the user has not signed up before then, the identity provider will capture the required user credential information and save it securely to the backend to validate properly from its backend for future requests.
With this approach, a decoupling of authentication logic and application logic occurs. As a result, we can reuse this logic across applications and manage it efficiently. End-users can reuse their single credentials to login to multiple applications seamlessly if they prefer to do so.
A few of the most popular IDP's available in the market are as follows:
- Microsoft
- AWS
- Fitbit
- WSO2
- Box
What is an IdentityServer?
IdentityServer is a free, open-source OpenID Connect and OAuth 2.0 framework for ASP.NET Core. Founded and maintained by Dominick Baier and Brock Allen, IdentityServer4 incorporates all the protocol implementations and extensibility points needed to integrate token-based authentication, single-sign-on, and API access control.
IdentityServer4 is officially certified by the OpenID Foundation and thus spec-compliant and interoperable. It is part of the .NET Foundation and operates under their code of conduct, licensed under Apache 2 (an OSI-approved license).
https://github.com/IdentityServer/IdentityServer4
Until Version 4, it was open-source, free to use and integrate with .NET applications. Later version it was commercialized, such as Duende IdentityServer5. IdentityServer4 will see continued maintenance with bug fixes and security updates until November 2022.
Let's briefly explain below some of the terminologies used in OAuth and OpenId Connect.
Confidential Clients:
Applications running on a server are commonly referred to as Confidential Clients, which can keep the token secrets safely since it is running on a trusted environment.
Public Clients:
Any JavaScript Apps, Single Page Apps, or Native Mobile Apps are public clients. However, these clients are not in a trusted environment, and anyone can see the view source and extract the token secrets for web apps, decompile and extract the token secrets in the case of mobile native apps.
Front Channel Communication:
Suppose communication between the client app and authorization server is initiated from a browser (no server involved). In that case, it is referred to as front channel communication, and usually, it will not be considered safe communication. Because the client cannot be trusted, and there is no way to keep the access tokens securely in a browser or a mobile device.
Back-Channel Communication:
If communication between the client app and authorization server is initiated from a server, it is referred to as back-channel communication and is usually considered safe. Because the communication is initiated from a secured server, the client can be trusted and considered a secure environment. Access tokens can be kept private and secured here.
Different OAuth Flows/Grants:
Authorization Code Flow:
- Defined in RFC 6749 4.1 Authorization Code Grant.
- Suited for applications that delegate the Resource Owner (RO) credentials to the authorization server require RO consent.
- Best suited for SPA/MVC client + Authorization Server + Web API combinations.
- Authorization code flow is recommended if the client app delegates the Resource Owner (RO) credential to the authorization server and consent is needed from RO.
- Resource owner trying to access the client application via a browser.
- The client app, in turn, redirects the RO through the browser back to the authorization server along with redirect_uri for authorization.
- It performs a GET request to the"/authorize?response_type=Code&…" endpoint. Note that the query string key called response_type having a value called "code," which tells the authorization server to send the authorization code upon successful authorization.
- Once authorized, the authorization server redirects to the redirect_uri and appends authorization_code to the redirect_uri.
- Client App receives the authorization_code and performs a POST call to the "/token" endpoint to get the access_token. It passes the authorization_code and redirect_uri as part of the POST request.
- The authorization server validates the authorization code and makes sure the redirect_uri is the same while issuing the authorization_code, then it redirects back to redirect_uri and appends the access_token.
- The client receives the access_token, starts communicating with the resource servers, and performs actions against the protected data.
It is a two-step process to retrieve the access token from the authorization server.
Usually, the first step is performed like a front-channel communication by a public client. The second step will be performed like a back-channel communication from a confidential client so that the access token will get stored securely in a server.
Implicit Flow:
- Defined in RFC 6749 4.2 Implicit Grant.
- OAuth spec recommended flow for public clients in early 2010 due to browser limitations.
- It suits JavaScript-based client-side applications, but it is not entirely secure because of the access_token exposure to the browsers.
- Implicit flow is a simple, singular process to get the access token from the authorization server comparing to the two-step process for Authorization Code flow.
In early 2010 there were no proper ways for browsers to perform cross-origin requests so that browsers could send the POST/token request to the authorization server along with the auth_code they receive from the first step of Authorization Code flow.
Mostly, the client (browser) apps and the authorization server will typically run in different domains. So, there is no way to get the access_token without a proper way to perform cross-origin requests. That is the reason for allowing users to go with the implicit flow.
Another problem is with Single Page Apps. Ten years ago, there were no APIs to modify the address bar data without refreshing the whole page. So, it is hard to remove the access_token-which is part of the URL segment after redirecting.
In the implicit flow, after successful authorization, the authorization server will append the access_token to the URL segment and perform a redirect to the redirect URL received before.
As a result, the access token is visible to everyone. Browser extensions can access the access_token, bookmarks, and other URL logging systems that inadvertently expose the access_token.
If using Implicit Flow for some reason, at least follow the below recommendations to avoid getting exposed fully:
- Do not load unknown third-party JavaScript files that are not specific to a project.
- Always use HTTPS so that the communication will get encrypted.
- Apply good Content Security Policies it is clear as to what code is running an app.
- Apply OWASP (Open Web Application Security Project) recommendations.
PKCE (Proof Key for Code Exchange)
In early 2010 OAuth specification recommends using implicit flow for mobile apps since there is no good way to keep the secrets like access token strings in mobile apps. So, anyone can recompile and extract secrets easily.
Later an extension to the OAuth Spec called PKSE got explicitly evolved for mobile apps, which is now more secure than the original implicit flow.
PKCE will not keep a single key per App install. Instead, it creates a new key every time it initiates the OAuth flow on the fly like a dynamic secret.
Every time an app wants to initiate the OAuth flow, it will create a dynamic key, hash the key, perform the OAuth flow, and get the auth_code.
Use the hashed key and auth_code in the back-channel to get the access_token from the authorization server.
This process became the recommended approach for mobile apps for several years. However, unfortunately, it will only prevent intercepting attacks while redirecting, and the inability to keep a secret in mobile apps remains.
Initially, PKCE with Auth Code flow was used in mobile apps, but later browser capabilities evolved and now can perform cross-origin requests. In addition, APIs are available to manipulate an address bar data without refreshing the whole page, becoming the default option for all browser-based apps.
Resource Owner Password Credentials Flow
Defined in RFC 6749 4.3 Resource Owner Password Credentials Grant.
Flows use backward compatibility with OAuth1.0 and are well suited for interactive applications. The user enters their username and password and then passes it to an Authorization server to get the access_token back directly.
We recommend using this flow if the client app is trusted. An example of a client app would be iOS and Android Apps.
Client Credentials Flow
Defined in RFC 6749 4.4 Client Credentials Grant.
Used by Confidential Clients like B2B apps. (All communication happens in the back-channel)
Client Credential flow is suited for machine-to-machine use cases like automated processes like windows services calling on the secure APIs, API to API communications.
In conclusion, this article went through the various terminologies. It explored concepts floating around the OAuth and OIDC context, its history, and high-level details of different flows available in OAuth and their use cases. Our next blog will discuss how to implement OAuth using a .NET Core Web API backend and a React front end App.