# mTLS via curl: Should you be looking to grant unscoped access to a specific API Route to trusted consumers,
Source: https://speakeasy.com/blog/api-auth-guide

## API Authentication Overview

If you're selling an API Product, one of the first decisions you're going to have to make is how to authenticate your users. This decision, once made, is hard to go back upon; any significant change will require user action to keep their integration working.

This blog post is intended to be a guide to the different API Authentication methods in common use, and the tradeoffs to consider between them, generally categorized into the following 3 metrics.

* **Time to API Call**
* **Ease of API Producer Implementation**
* **Ease of API Consumer Integration**

This post is inspired by work we recently undertook at Speakeasy (API DevEx tooling company) to build an API Key authorization flow that integrates into any API Gateway, and allows users to self-service and rotate their keys. For this project we evaluated all the standard approaches to authentication before deciding on a novel approach: signed tokens as API Keys, but with 1 signing key per API key. What follows are a deep dive on the typical methods, as well as our approach.

## Types of Authentication

Authenticated APIs require some way of identifying the client making the request, so that the API provider can authorize the requests to be processed, and identify the subset of data that can be accessed.

From an API Consumer perspective, the flow is usually:

1. You get a **Secret Key** from the service (e.g. in an authenticated Web UI).
2. You store that **Secret Key** somewhere securely, such that your application can access it.
3. You use that **Secret Key** within your application logic to authenticate with the API.

This is incredibly simple, and hence has great Developer Experience (DX).

However, from an API Producer Perspective, it's not so simple. There are choices you need to make about how the **Secret Key** is implemented which greatly impacts the Security Model of your application. Once you have users in production, Machine to Machine (M2M) authentication is hard to change, assuming you don't want to break existing integrated users. Therefore, choose wisely:

1. **Opaque Token / Shared Secrets**
2. **Public / Private Key Pairs**
3. **OAuth 2.0 Secrets**
4. **Signed Tokens (one Signing Key)**
5. **Signed Tokens (many Signing Keys)**

Let's look at the advantages and disadvantages of each approach…

### Opaque Tokens / Shared Secrets

An _Opaque Token_ is a _Shared Secret_ that is used to authenticate a client. It is _Opaque_ in that there is no message to read: all you can do with it is look up its existence in a centralised store (e.g. a database), and then, if it exists, you know who the user is.

This is functionally the same as a password, except that it is ideally generated by a process which ensures the entropy of the token is high enough that it is entirely unguessable.

Assuming this token is passed into the API in the `Authorization` header, from an API Consumer perspective, accessing the API is as simple as:

```sh
curl https://api.example.com/v1/endpoint
  --header "Authorization ${API_KEY}"
```

From an API Producer perspective, there's a few more moving parts, but it's usually pretty trivial to implement:

```mermaid
sequenceDiagram
  participant API Consumer[Bob]
  participant API
  participant Authorization Server
  rect rgba(0, 0, 0, 0.05)
  note over API Consumer[Bob],Authorization Server: Configure Key
  API Consumer[Bob]-->>API: AUTHORIZED SESSION [Bob] with [API]
  API-->>API Consumer[Bob]: 
  API Consumer[Bob]->>API: Request New Secret Key
      Note right of API: Generate some random bytes.<br>Render bytes as [Secret Key]
  API->>Authorization Server: Store [Bob, [Secret Key]]
  Authorization Server-->>API: Success
  API-->>API Consumer[Bob]: [Secret Key]
  end
  rect rgba(0, 0, 0, 0.05)
  note over API Consumer[Bob],Authorization Server: Authorized API Request
  API Consumer[Bob]->>API: [Req] with [Secret Key]
  API->>Authorization Server: Who is this? [Secret Key]
  Authorization Server-->>API: Bob
      Note right of API: Process Request under context Bob
  API-->>API Consumer[Bob]: [Resp]
  end
```

* **Time to API Call**: As Fast as it gets.
* **Ease of API Consumer Integration**: Very Easy
* **Ease of API Producer Implementation**: Very Easy
* **Other Considerations**:
  * Often Difficult to integrate into an API Gateway
  * Any validation requires a lookup where-ever these are stored. If you solely store them in the DB, this means that all lookups require a DB Read, which can cause additional latency to every request.

### OAuth 2.0 Secrets

```mermaid
sequenceDiagram
  participant API Consumer[Bob]
  participant API
  participant Authorization Server
  rect rgba(0, 0, 0, 0.05)
  note over API Consumer[Bob],Authorization Server: Configure Key
  API Consumer[Bob]-->>API: AUTHORIZED SESSION [Bob] with [API]
  API-->>API Consumer[Bob]: 
  API Consumer[Bob]->>API: Request New oAuth Application
      Note left of API: Generate some random bytes.<br>Render bytes as [Client ID], [Client Secret]<br>(AKA Secret Key). 
  API->>Authorization Server: [Bob, [Secret Key]]
  Authorization Server-->>API: Success
  API-->>API Consumer[Bob]: [Secret Key]
  end
  rect rgba(0, 0, 0, 0.05)
  note over API Consumer[Bob],Authorization Server: Authorized API Request
  API Consumer[Bob]-->>Authorization Server: SECURE SESSION [Unknown] with [Authorization Server]
  Authorization Server-->>API Consumer[Bob]: 
  API Consumer[Bob]->>Authorization Server: GET /oauth/token with [Secret Key]
    Note left of Authorization Server: Lookup [Secret Key] as Bob<br>Generate short-lived JWT with {"sub":"Bob"} claims
  Authorization Server-->>API Consumer[Bob]: JWT["Bob"]
  API Consumer[Bob]-->>API: SECURE SESSION [Unknown] with [API]
  API-->>API Consumer[Bob]: 
  API Consumer[Bob]->>API: [Req] with header "Authorization Bearer \${JWT["Bob"]}"
      Note left of API: Process Request under context Bob
  API-->>API Consumer[Bob]: [Resp]
  end
```

OAuth is commonly associated with user authentication through a social login. This doesn't make sense for API applications, as the system authenticates and authorizes an application rather than a user.

However, through the Client Credentials Flow ([OAuth 2.0 RFC 6749, section 4.4](https://tools.ietf.org/html/rfc6749#section-4.4])), a user application can exchange a **Client ID**, and **Client Secret** for a short lived Access Token.

In this scenario, the **Client ID** and **Client Secret** pair are the **Shared Secret** which would be passed to the integrating developer to configure in their application. In the OpenID Connect (OIDC) protocol, this happens by making a request to the **/oauth/token** endpoint.

```sh
TOKEN=$(curl --header "Content-Type: application/x-www-form-urlencoded" \
  --request POST \
  --data "grant_type=client_credentials" \
  --data "client_id=CLIENT_ID" \
  --data "client_secret=CLIENT_SECRET" \
  https://auth.example.com/oauth/token | jq -r '.access_token')

curl --header "Authorization Bearer $TOKEN" \
  https://api.example.com/v1/endpoint
```

* **Time to API Call**: Slow.
* **Ease of API Consumer Integration**: Difficult
* **Ease of API Producer Implementation**: Difficult
* **Other Considerations**:
  * Can enable additional use-cases, such as granting third party systems the capability to make API calls on behalf of your users.

### Public/Private Key Pairs

A Public Private Key Pair allows for a user to hold a secret and the server to validate that the user holds a secret, without the server ever holding the secret. For the purpose of authenticating users, this mechanism has the lowest attack surface : i.e. it is the most secure.

```mermaid
sequenceDiagram
  participant API Consumer[Bob]
  participant API
  participant Authorization Server
  rect rgba(0, 0, 0, 0.05)
  note over API Consumer[Bob],Authorization Server: Configure Key
  API Consumer[Bob]-->>API: AUTHORIZED SESSION [Bob] with [API]
  API-->>API Consumer[Bob]:  
  Note right of API Consumer[Bob]: Generate Public/Private Key Pair
  API Consumer[Bob]->>API: Send Public Key
  API->>Authorization Server: Store [Bob, [Public Key]]
  Authorization Server-->>API: Success
  API-->>API Consumer[Bob]: Success 
  end
  rect rgba(0, 0, 0, 0.05)
  note over API Consumer[Bob],Authorization Server: Authorized API Request  
  API Consumer[Bob]-->>API: SECURE SESSION [Unknown] with [API]
  API-->>API Consumer[Bob]: 
  API Consumer[Bob]->>Authorization Server: Hello, I am Bob
  Note left of Authorization Server: Generate Random Number [NONCE]
  Authorization Server-->>API Consumer[Bob]: Hello Bob, please sign [NONCE]
  Note right of API Consumer[Bob]: Sign [NONCE] with Private Key as [PROOF]
  API Consumer[Bob]->>Authorization Server: [PROOF] 
  Note left of Authorization Server: Validate [NONCE] signature using [Public Key[Bob]]
  Authorization Server-->>API Consumer[Bob]: Success. [Session Token]
  API Consumer[Bob]-->>API: AUTHORIZED SESSION [Bob] with [API] via [Session Token]
  API-->>API Consumer[Bob]: 
  end
```

The cost and complexity of building and maintaining a Public/Private Key Authentication mechanism, without exposing the Private Key, opening up replay attacks, or making a mistake in implementation somewhere can be high.

If you're selling an API to multiple consumers, it's unlikely that it will be as trivial as the following `curl` to invoke the API that you want; as the integrating system will need to understand the protocol you choose. There are also complexities regarding the certificate lifecycle, and the need to either manage certificate rotation, or pin (hardcode) each certificate into the system.

```sh