Server-to-Server Authorization Using Mutual TLS

Posted in

Leveraging a token-based architecture for API access is becoming much more popular, as it should. However, not all tokens are created equal, and there are things to consider when implementing a token-based API infrastructure.

Introduction

A common approach is to use so-called Bearer Tokens to authorize a client when making an API call. Passing a Bearer Token is usually enough to gain access to the API. However, Bearer Tokens are a lot like cash — if you find cash, you can use it to pay for goods even if the cash isn’t yours. If you get hold of a Bearer Token, you could use it to gain access to the API.

This article will look at how we can further strengthen the token-based approach for API security and server-to-server communication. We will look at some of the details defined in OAuth 2.0 about using Mutual Transport Layer Security to handle some of the weaknesses with traditional Bearer Tokens.

What is Mutual TLS?

Transport Layer Security (TLS) has replaced the more commonly known Secure Sockets Layer (SSL). Web browsers use TLS to secure the connection between the client and the server hosting the web page. When a connection is established, the web server provides a certificate to the client that can be validated. Thus, the server’s identity is trusted.

The server can also request that the client authenticate itself to establish secure communication between the server and the client. In that case, the client has to present a certificate to the server, and the server will validate that certificate. If the validation is successful, the trust would be mutual, hence Mutual TLS or mTLS.

Mutual TLS and OAuth 2.0

Mutual TLS can be leveraged in different ways and by different types of systems. Let’s take a look at how OAuth 2.0 specifically makes use of this technology.

RFC8705 is an OAuth 2.0 RFC that defines two main parts regarding the use of mTLS:

  • Client Authentication
  • Certificate-Bound Access Tokens

These approaches are commonly used together. So, let’s break them down and look at them individually.

Client Authentication

There are several different ways for a client to authenticate itself to the Token Service. A simple and common approach is to use a shared secret. This is basically a password that the client passes to the Token Service, together with the client’s ID. Both parts are merged together, base64-encoded, and then passed as a header in the Authorization Server request.

A more secure and flexible option would be to leverage mTLS for this authentication. Instead of passing a shared secret, the client sends the ID. The authorization server can pick up the certificate from the mTLS handshake performed between the client (calling the server or API) and the Token Service and then use that to authorize the client.

A Server-to-Server Example

Let’s review what this could look like in a server-to-server (or API-to-API) scenario.

When Server A wants to communicate with Server B, Server A will first need to obtain an Access Token from a Token Service. When initiating the communication to the Token Service, Server A can use mTLS to authorize itself to get an Access Token. This Access Token is later used to authorize the communication with Server B.

But remember the initial point about Bearer Tokens — if Server A obtains a Bearer Token in this process, there is still a risk of the token being lost or stolen. In that case, another server or client could initiate communication with Server B. As soon as the token is issued, there is no way to tie that token to the client it was given to. This token is now all that is required to access Server B, regardless of who is requesting access.

There is a more robust approach to verifying the client requesting an Access Token. This is where Certificate-Bound Access Tokens come into play. Let’s take a look at the details of that next.

Certificate-Bound Access Tokens

Certificate-Bound Access Tokens can alleviate the security concerns around lost or stolen Bearer Tokens. These tokens make it possible for the resource server (Server B in our example) to verify that the token’s sender is the same as whom the token was issued to.

The Token Service can leverage the certificate information from the mTLS session for the client that the token is being issued to (Server A in our example). Instead of just issuing an Access Token like before, the Token Service will encode the thumbprint (hash) of Server A’s certificate into the token and thereby create a Certificate-Bound Access Token. These types of tokens are also referred to as Holder-of-Key-, Proof-of-Possession- or Sender-Constrained tokens.

This Certificate-Bound Access Token is now specifically tied to the client that requested the token and to which the token was issued. It can now only be used by that client, and the constraints are that this client would have to establish a mTLS connection with the server or API. This mTLS-secured connection has to be established using the same certificate that was used in the connection with the Token Services.

Server-to-Server in a Partner Integration Ecosystem

A robust architecture for handling server-to-server communication can be achieved by combining Client Authentication and Certificate-Bound Access Tokens. One practical use case would be within a partner integration ecosystem.

The centralized Token Service would issue tokens in a much more controlled manner, and the risk of a partner wrongfully accessing another partner’s information would be mitigated. The tokens issued are bound to a specific partner or even a particular server or API. Depending on the underlying infrastructure for handling certificates, the tokens could technically be used by several systems or APIs that belong to a specific partner. However, the token would still be bound to a specific certificate. The certificate itself would not be shared between different partners in the ecosystem.

Let’s apply this to our example.

When Server A is issued a Certificate-Bound Access Token and later passes this token to server B to establish the communication, the certificate’s thumbprint is encoded directly in the token. This is done through a standardized confirmation claim called cnf.

Server A also uses the same certificate in its mTLS communication with server B. This allows Server B to verify that the certificate used to establish that mTLS session is the same as the certificate in the cnf claim in the token and verifies that Server A can use it. If the token got misused and some other server or API tried to use it, Server B would not accept the token since there would be a certificate mismatch in the verification process.

Summary

Traditional Access Tokens or Bearer tokens are very common but come with some vulnerabilities. In a typical token-based architecture, it would be enough to present a Bearer Token to gain access. However, this means that anyone in possession of a valid token can access protected resources.

Leveraging mTLS can enhance security in a token-based architecture. Using mTLS in the step where a client (server) requests an access token from a Token Service allows the Token Service to issue a token that is bound to that client (certificate) specifically and cannot be used by anyone else. This can then securely be validated when that client requests access to resources on another server (or API) by verifying that the token’s certificate is the same used to establish the mTLS session.

This approach is well suited for environments with high-security requirements — especially in scenarios with server-to-server communication and different partner environments requesting information from each other.