Increase the security and simplicity of your Information System with OAuth 2.0, OpenID Connect and @axa-fr/react-oidc
This article aims to introduce what OAuth 2.0 is, then OpenID Connect and finally the @axa-fr/react-oidc library available on GitHub (of which I am one of the repository-maintainers). This article is based on a course on REST APIs available on GitHub that I give at IG2I Lens (school of the Groupe Centrale Lille in France).
You can now play with the demo of @axa-fr/react-oidc, it will probably help you better understand the rest of the article.
Introduction to the demo of @axa-fr/react-oidc and OpenID Connect (OIDC) — YouTube
Introduction
In this article, I will start by explaining some prerequisites, then the OAuth 2.0 protocol, then I will present the advantages of OIDC that build on Oauth 2.0 and complements it. Finally, I will introduce why @axa-fr/react-oidc is a real innovation in terms of security and simplicity.
Preamble
Authentication, identification and authorization management are central and very technical things; often overlooked, they generate enormous additional costs for each application and in the overall management of information systems.
OAuth 2.0 is a protocol for managing permissions. Unfortunately, there are no details on how to implement it technically. As a result, each implementation is different.
OpenID Connect (OIDC) is the next step. It is an identification layer based on the OAuth 2.0 protocol. It normalizes the technical part via a REST API and standardizes the retrieval of credentials.
The result is that the consumer can switch OIDC providers without writing a single piece of code. It also allows you to make OAuth 2.0 compatible with your old authentication systems. It can act as an interface.
Understand the concepts of identification, authorization and authentication
It is necessary to master the distinction between these concepts, because this vocabulary is important to understand the article.
- Identification : Who are you? Example: Login
Who can be authenticated? It is often either a person or a machine. - Authentication : Are you really that person/machine? Example: Password.
- Authorization : Does this person/machine have the right to access this resource?
On the HTTP client side, this is transcribed as below
- Authentication
Response code HTTP 401, if I am not authenticated and trying to access a private resource - Authorization
Response code HTTP 403, I am authenticated but I do not have the right to access the resource - Identification
It can be carried out in many different ways (form, certificate, validation by an application, etc.).
If you make an identification via a web form:
Response code HTTP 400, the information I send by form via HTTP POST is not valid.
OAuth 2 defines 4 distinct roles
- Resource Owner
A human or a machine - Resource Server
Hosts data that is protected for access - Client Application
An application requesting data from the resource server. - Authorization Server
Issues access tokens to the client.
Why OAuth 2.O?
Imagine that you start a startup and because you are pragmatic, you realize a “Monolith” well divided into functional areas, domains.
Example of functional areas:
- Order Management
- Identification, Authentication, Authorization
- Sending SMS/email
- Inventory Management, Items
- Image Management
Super! Your application is successful and is now used by thousands of customers simultaneously around the world. Response times are starting to degrade because your API is under heavy strain.
You went from 2 developers to 45, developments and deliveries become complicated.
You have a new need to open your API to third-party partners!
The solution
To be able to keep acceptable response times and that the application remains maintainable, the application is rearchitected into microservices.
In order to be able to manage authentication on all functional “services”, the first bricks to be extracted are those that manage authentication, identification and authorization.
You must be able to trace everything who/what accesses what at a time T, to control and limit access.
This protocol allows third-party applications to gain limited access to an exposed service via HTTP through prior authorization from the resource owner.
This is the opposite of the fortified castles. It’s hard to get into a castle but once you’re there you do what you want. And that’s not good! This is also why we don’t build castles anymore.
With OAuth 2.0 we start from the opposite principle, that is to say that we know that “yes”, we will be hacked one day. We will be hacked BUT on a very limited resource, and over a very short period of time.
Moreover, in the event of an intrusion, the entities and tokens involved are revocable by the authorization server.
For this OAuth 2.0 is based on a set of two tokens
Access Token
Allows the resource server to allow a user’s data to be made available. This token is sent by the client (application) in the request to the resource server. It has a limited lifetime that is set by the authorization server: for example, 20 minutes.
Refresh Token, the renewal token
This token is issued at the same time as the access token. It is used to renew the access token when it has expired. It has a limited lifespan, but longer than the access token: for example, a week.
Concretely, the “access_token” is the one you send to your API all the time. It is mostly a JSON Web Token (JWT). It is a standard format for exchanging information between clients and servers which makes it sure that the information has not been modified along the way. The “refresh token” is not necessarily a JWT. It transits less on the network but is more sensitive from a security point of view because it allows renewal.
Scopes and Audiences
In the content of the access token and if it is a JSON Web Token (JWT), you will hear about “Scope” and “Audience”. These 2 are very important and quite complex pieces of information to understand.
Scope
Used to limit the range of access rights.
The authorization server defines the list of available scopes. The client must send the scope(s) it wishes to use when initializing the identification. A good practice is to request only the scopes useful for the current functionality, and not more. The more you reduce the list of requested scopes, the more you will reduce your attackable surface.
Example: openid email profile account-read account-payment
Audience
Specifies the API(s) you are targeting.
The authorization server defines the list of available audiences.
Example: api-bank api-payment
Security: Your “resource” server must always:
- Control the scopes necessary for each call.
- Check that the necessary audience is present.
If you don’t, your API can be accessed with any token generated by your authorization server. For example a token generated via another application.
Roles
“Audience” and “Scope” are not roles. The notion of role is yet another thing very specific to each API.
In your API, you must check:
1) that the access token carries the “Audience” that corresponds to you,
2) for each functional part or “resources” in REST terms (because your API can offer several) you check that the “Scope” associated with the functional part of the current HTTP request is indeed present; A Restful API is normally structured in functional domains that are well decoupled from each other.
3) Finally, if you need a finer management of access rights, you check if the user has the right to perform this action using a role (example: user, validator, reviewer, administrator). The notion of role is much finer and is not part of the OAuth 2.0 protocol. It is related only to your application. It is up to you to inject the necessary information, for example, into the key/value fields of the JWT access token from your OIDC server.
For example, adding a “claim” to give the roles of the connected user:
- member_of: user, administrator
Be known to the authorization server
A client cannot use the OAuth 2.0 protocol without being known to the authorization server. It must therefore register with the authorization server. For this, it must provide a set of data:
- App name
- Website URL in HTTPS
- Return URL
- Etc.
In exchange, the server will provide an identifier (“client id”) and, if necessary, a secret code (“client secret”) as strings, which will allow the client to authenticate.
Authorization Types
- Authorization Code Grant, authorization via a code [Deprecated]
The client is a web server. Allows you to get a long-term access token that can be renewed via a renewal token. - Implicit Grant [Deprecated]
The application is on the client side (Javascript, mobile application). - Authorization Code Flow with Proof Key for Code Exchange (PKCE)
The application is on the client (Javascript, mobile application) or server side. - Client Credentials Grant, server-to-server authorization
Used by clients to get an access token outside of a user’s context. - Resource Owner Password Credentials Grant, authorization via password [Deprecated]
Focus on “Authorization Code Grant with PCKE”
This flow can be used on the client side (a browser or a mobile application, for example) and on the server side.
Client side
This mode is the easiest to set up (and the least expensive), however the access tokens are found on the client side. The client application is therefore very sensitive to “Cross Site Scripting” (XSS) type attacks.
In addition, if a hacker accesses the “refresh tokens”, he can then recover access tokens. Fortunately, you only offer a limited “scope” and “audience” per application!
Server side
This mode is more secure but more complex to set up (more expensive). Today we hear about “Backend For Frontend” (BFF). Access tokens and rollovers are done on the server side (this is authorization delegation). Tokens are normally not accessible from the client (via javascript, in particular).
The session between the client and the server must be done via an exchange of cookies, which must be highly secured because they are also sensitive to XSS type attacks.
« Level up » with OpenID Connect
OpenID Connect (OIDC) adds everything that OAuth 2.0 lacks as standard. With OAuth 2.0, each implementation is different and requires vendor-specific libraries. With OIDC, the implementation becomes simpler.
OpenID Connect standardizes
- User information retrieval
- An API (User Info endpoint)
- Using the ID Token Scope
- Authentication
- SSO session management (ex: Single Logout)
- An OpenID server discovery system, through the use of a “/.well-known” URL; a “/.well-known” URL that lists all other URLs.
All this allows you to change OIDC providers without changing your code
The REST API is simple and standardized:
- authorization: to authenticate a user
- token: to request a token (access / refresh / ID)
- user info: to retrieve user information (identity, rights)
- revocation: to revoke a token (access / refresh)
- introspection: to validate a token (access / refresh)
Added a third token: IDToken
The “ID Token” is a JWT that contains the identity of a user.
- It contains the authentication parameters: expiry date, creation date, authentication date, means to validate the “ID Token” and the “Access Token”.
- User credentials (roles, authorisations).
- User attributes (claims) associated with scopes.
standard attributes:
- “profile” scope: last name, first name, nickname, date of birth,
- “email” scope: email, verified email
- “address” scope: address
- “phone” scope: phone number, verified phone number
- etc.
private attributes:
- attributes offered by the identity provider. It is necessary to specify them in order to avoid any collision with existing claims.
OpenID Connect lets you federate
Your OpenID Connect server allows you to set up a kind of interface with all your identification, authentication and authorization systems. This makes your old systems compatible with OAuth 2.0, which allows you to make “Single Page Application” (SPA).
It is the OIDC server that manages the complexity and the technical updates with the other systems. Client applications only have to use standard OIDC compatible libraries. A huge gain in simplicity and cost for your Information System!
Finally, let’s talk about AXA React OIDC
React OIDC V4 is a real innovation. It allows you to use the “Authorization Code Grant with PCKE” flow on the client side. This flow is sensitive to XSS type attacks.
The little extra thing that @axa-fr/react-oidc offers is a mode that uses a “ServiceWorker” that acts as a proxy between the client and the server. It allows the “access_token” and “refresh_token” code to be completely hidden from the Javascript client code. The “ServiceWorker” is able to automatically inject your “access_token” in your requests to your API as well as the “refresh_token” in requests to the OIDC server.
Your application therefore becomes much less sensitive to XSS-type attacks and therefore significantly increases its level of security.
This is a real alternative that costs much less to make than setting up a BFF.
The library also allows you to do multiple authentication. This can be practical, in particular for securing certain sensitive operations that require a particular scope or audience. You may not even request a renewal token.
For example when you connect to your bank’s site you can see the balance of your bank account. When you want to make a money transfer, the site will ask you again to authenticate yourself (an over-authentication). In this case, you receive a new scope and/or more sensitive audience valid for a very short period of time and without a renewal token.
For more information, visit the React OIDC Github page.
To summarize, you can finely and permanently give limited access for a limited period.
The future in vanillajs
The library behind @axa-fr/react-oidc is native javascript, so it is not linked to React, there is just a “binding” with React. There is a good chance of seeing new bindings appear very quickly. For example: webcomponent, angular, vuejs, blazor, etc.
Conclusion
You now have an overview of what OpenID Connect is and the keys to simply secure your Information System.
Security is not negotiable, it must be present by default in your applications AND precisely, this is what the tools presented in this article allow you! So what are you waiting for?
A big thank you to AXA France which accompanies me and pushes me to give lessons as well as to do Open Source.
Many thanks to everyone who helped proofread this article:
- Gilles Cruchon
- Lilian Delouvy
- Francois Descamps
- Antoine Blancke
- Louise Ryckewaert
- Preclin Amaury