Securing an API with OAuth2.0
This article is the second in a series of articles about a POC on creating agnostic APIs.
You can read the first article here
Security in the context of API creation can be an obstacle to application development. In this article, we will detail a common solution: the implementation of an authentication server supporting OAuth and Open ID Connect. This authentication server will allow third-party and in-house applications to open sessions for users. This will allow us to develop services and applications using the same authentication source, making the system very flexible.
OAuth is an authentication protocol. It gives an application or a service the possibility to communicate with the assurance of identity through security tokens. In general, a dedicated authentication server is set up to create these tokens. Here we will overview the OAuth2.0 protocol and Open ID Connect and then in the next series of articles we will set up our own OAuth server and show how to use it.
Overview
OAuth is based on the properties of asymmetric cryptographic keys. The principle is that the authentication server will sign tokens with its private key, allowing other services to verify the signature without being able to forge the signature. We won't go deeper into this cryptographic aspect and stay on an overview of OAuth2.0. The protocol "seen from above" is very simple:
Once the application has a token, it can make calls to different APIs by accompanying its requests with the security token provided by the OAuth server. The APIs will simply have to validate the security tokens received from the authentication server to define the authentication and then the permissions.
The security of OAuth is enhanced by the nature of the tokens, which have a sometimes very short lifespan. An application can therefore request a new token for each new call, thus limiting the possibility of identity theft. The management of the token renewal can be implemented by the authentication server through a cookie (policy named by refresh token), but this is not defined in OAuth and depends on the implementation in the authentication server.
Another interesting property of authentication servers is that they are completely decentralized. We can for example use Google or Facebook servers to authenticate users. In this series of articles, we will use our own authentication service to centralize our users in one place and allow applications to use this server in the same way as Facebook authentication.
By using a widely used protocol, we allow many applications/services/websites to connect to the same user base while keeping complete control over the location of our user data. But before setting up this server, it is important to understand a little bit more about how it works.
Request a token
The OAuth protocol defines the token format, its exchange, and validation. The way the token is requested varies between implementations and two common implementations have emerged.
To better understand these different implementations, let's take the case of Facebook, which has contributed to popularizing the use of the OAuth protocol. In the case of Facebook, two different use cases are distinguished:
Connection through third party applications (soundcloud, amazon, etc.)
Connection through in-house applications (messenger, facebook site)
These two cases are handled differently because in the first case, we cannot trust the third-party application completely - it might provide an insecure or even misleading login form. In the case of in-house applications, we have the assurance that the security standards will be respected, or at least we have direct control over how the login forms are displayed.
The criterion for how to request a token is therefore dependent on the trust placed in the application or service.
OIDC
In order to deal with third-party applications, the Open ID Connect protocol(OIDC) will provide an additional layer to OAuth2.0. OIDC will allow control of the form that will be used for the connection. It is ultimately another protocol specifically for managing third-party applications.
The protocol is summarized by this diagram:
With OIDC, we have the guarantee that the connection form is secure because it is developed by the authentication server. Moreover, thanks to this code exchange (point 2/3), we know that the same application that initially redirected the user is the one that receives the token. In practice, authentication servers implementing OIDC give a very short validity period (a few seconds) for the code transmitted in case of success, in order to reduce the risks of token theft.
It is important to note that OIDC requires a redirection, so the login form can never be coded by the application. This is a UX constraint that is important to take into account when designing the third-party application.
Trusted applications
If the application implementing the login form is trusted, we can use another method to obtain a security token. In the authentication service, we specify the URL of the trusted application and generate an API key unique to the application. The application will then need to attach the API key to its token request to be eligible to retrieve tokens.
In the case of Facebook Messenger, for example, the application can create its own login form and then ask the OAuth server for a token with the email/password pair. This is only possible because the Facebook authentication server completely trusts the Messenger implementation.
Note that the API key used by a trusted service is critical information that must not be exposed to the public. It is therefore essential in the case of a javascript application to create a lightweight backend that can act as an intermediary. The javascript application sends the form data to the backend, and the backend sends it to the authentication service with the API key. Thus only the lightweight backend will know the API key, the javascript application being fully exposed to the public can never be considered as a trusted application.
These two methods to retrieve a security token must be present in the case of an "agnostic" API development because we don't know yet who will use the service and if the applications will be trusted or not.
Action plan
In order to realize our POC, we will set up an authentication server, a React application, and a Ruby on Rails backend. We will focus on the creation of users by an administrator, as this feature allows us to work on a very common case.
In the first part, the React application will connect through OIDC to the authentication server. So there will be the temporary code exchange described in this article, and the login form will be the one displayed by the authentication server.
In the last two parts, we will detail how an administrator will be able to add a user by going through a trusted application, here a Ruby On Rails backend. This Rails backend will validate the token received by the React application, verify that the token is that of an administrator, and will transmit the creation of the user and its associated roles to the authentication server.
The React part and the Ruby On Rails part will be detailed in future articles.
Conclusion
The implementation of an authentication server offers many advantages and opens the doors to Software As A Service(SaaS). Thanks to an authentication server we can add great extensibility for example in the following cases:
Enable enthusiastic developers to create applications that integrate our users and data. For example, InstaGantt implements an online time view of Asana tasks.
Create many independent web and native applications that always use the same user IDs, such as in associations, universities, large companies, or even some governments.