#   this can usually be configured by some API Gateway products in infrastructure configuration. 
curl --cacert ca.crt \
     --key client.key \
     --cert client.crt \
     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**:
  * Severity of the public key being leaked is functionally zero -- no intermediary system holds enough data to make/replay requests except the original sender.

### Signed Tokens as API Keys

A Signed Token is secret; in the same way that a Shared Secret is secret. 

However, due to standardization of technologies, it is starting to become commonplace to use long-lived Signed Tokens, in the form of JWTs (JSON Web Tokens), as API Keys. This enables the following pattern: 

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

```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]->>Authorization Server: Request New Secret Key
      Note left of Authorization Server: Make JWT[Secret Key] with Signing Key [K] via: <br>SIGN({"sub": Bob, "exp": 3 months}, K).
  Authorization Server-->>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: SECURE SESSION [Unknown] with [API]
  API-->>API Consumer[Bob]: 
  API Consumer[Bob]->>API: [Req] with header "Authorization Bearer \${JWT["Bob"]}
  API->>Authorization Server: Check JWT["Bob"]
    Note left of Authorization Server: Validate JWT[Secret Key] with Signing Key [K]
  Authorization Server-->>API: Success
      Note left of API: Process Request under context Bob
  API-->>API Consumer[Bob]: [Resp]
  end
```

* **Time to API Call**: Fast.
* **Ease of API Consumer Integration**: Simple
* **Ease of API Producer Implementation**: Simple
* **Other Considerations**:
  * Difficult to revoke tokens: either a whitelist/blacklist is used (in which case, little advantage exists over shared secrets), or the signing key must be rotated.
  * Easy to add complexity through custom claims into the token, which can lead to complexity migrating tokens into a new form.
  * Easy to block requests from reaching application servers through an API Gateway and Asymmetric Keys (split into Private JWTs and Public JWKs).

### Our Approach: Signed Tokens as API Keys, but 1-Signing-Key-Per-API-key

There are 3 problems with the Signed Tokens as API Keys pattern:

1. Revoking a key is hard: it is very difficult for users to revoke their own keys on an internal compromise.
2. Any compromise of the Signing Key is a compromise of all keys.
3. API Gateways can't usually hook into a whitelist/blacklist of tokens.

To tackle these, we can do three things:

1. Use Asymmetrically Signed JWTs, storing and exposing a set of Public Keys via a JWKS (JSON Web Key Set) URI.
2. Sign each Token with a different Signing Key; burn the Signing Key immediately afterwards.
3. Ensure that API Gateways only retain a short-lived cache of JWKS (the Public Key to each Signing Key).

```mermaid
sequenceDiagram
  participant API Consumer[Bob]
  participant Authorization Server
  participant API
  rect rgba(0, 0, 0, 0.05)
  note over API Consumer[Bob],API: Configure Key
  API Consumer[Bob]-->>Authorization Server: AUTHORIZED SESSION [Bob] with [Authorization Server]
  Authorization Server-->>API Consumer[Bob]: 
  API Consumer[Bob]->>Authorization Server: Request New Secret Key [K]
      Note left of Authorization Server: Create a new Signing Key Pair [K]<br> (e.g. EdDSA Certificate with High Entropy)<br>Sign [Bob] with [K["PrivateKey"]] as [Secret Key]<br>Store [Bob, K["PublicKey"]]<br>Burn K["Private Key"]       
  Authorization Server-->>API Consumer[Bob]: [Secret Key]
  end
  rect rgba(0, 0, 0, 0.05)
  note over API Consumer[Bob],API: Authorized API Request
  API Consumer[Bob]-->>Authorization Server: SECURE SESSION [Unknown] with [Authorization Server]
  Authorization Server-->>API Consumer[Bob]: 
  API Consumer[Bob]->>Authorization Server: [Req] with header "Authorization Bearer \${JWT["Bob"]}"
    Note left of Authorization Server: Decode["Secret Key"] into JWT<br>Lookup Bob["Public Key"] via JWT.KID<br>Validate ["Secret Key"] matches Public Key["KID"]
  Authorization Server->>API: [Req], [Bob]
      Note left of API: Process Request under context Bob
  API-->>Authorization Server: [Resp]
  Authorization Server-->>API Consumer[Bob]: [Resp]
  end
```

This gives us a flow that's very similar to a shared secret, but with the advantage that:

* Revoking a key is easy: the Public Key just needs to be removed from the JWKS and caches invalidated.
* There is no Signing Key to compromise ; all Authorization Server state can be public.

With the tradeoff:

* Any API Gateway that validates a JWT must be able to regularly fetch JWKS (and cache them) from the Authorization Server.
* After creating/revoking a key, there will be a short time delay (we generally configure this to be 15 seconds) whilst the key propagates into all API Gateway JWK caches.
* More compute is required when constructing the Initial API Key; due to the generation of a public/private key pair.
* You need to give the API Gateway application cluster slightly more memory to keep all Public Keys (1 per API key) in memory.


  <video controls={false} loop={true} autoPlay={true} muted={true} width="100%" >
    <source src="/assets/videos/auth-clip.mp4" type="video/mp4" />
  </video>

#### In Practice: Envoy / Google Endpoints / EspV2

Envoy is a proxy server that is used to route traffic to a backend service. We ran extensive tests with ESPV2 (Envoy Service Proxy V2) and Google Endpoints, and found/validated the following performance characteristics as the number of keys increased. 

```yaml