QCecuring - Enterprise Security Solutions

API Authentication with Certificates

Ayush Kumar Rai

Key Takeaways

  • Certificate-based API auth uses mTLS: the client presents a certificate during the TLS handshake, proving identity cryptographically
  • Stronger than API keys (can't be stolen from logs/headers) and more suitable for machine-to-machine than OAuth (no token refresh complexity)
  • The certificate's subject (CN, SAN, SPIFFE ID) identifies the caller — the API server maps this to permissions
  • Best for: service-to-service communication, partner integrations, financial APIs, and any high-security machine-to-machine flow

API authentication with certificates uses mutual TLS (mTLS) to verify the identity of API clients. Instead of passing an API key in a header or obtaining an OAuth token, the client presents an X.509 certificate during the TLS handshake. The API server validates the certificate chain, checks that it was issued by a trusted CA, and extracts the client’s identity from the certificate’s subject or SAN fields. The authentication happens at the transport layer — before any HTTP request is processed — and the private key never leaves the client.


Why it matters

  • No secrets in transit — API keys are sent in every request (headers, query params). If any request is logged, intercepted, or leaked, the key is compromised. With certificates, the private key never crosses the network — only a signature proving possession.
  • Non-replayable — an API key can be copied and reused from anywhere. A certificate-based authentication is bound to the TLS session — you can’t replay it because each handshake uses fresh cryptographic values.
  • Mutual verification — both sides verify each other. The client knows it’s talking to the real API server (server cert), and the server knows it’s talking to an authorized client (client cert). API keys only authenticate the client.
  • Identity-rich — a certificate carries structured identity information (organization, service name, environment) that can be used for fine-grained authorization. An API key is just an opaque string.
  • Rotation without downtime — deploy a new client certificate alongside the old one, verify it works, then remove the old one. No “change the API key and update all clients simultaneously” coordination.

How it works

  1. Client obtains certificate — issued by a trusted CA (private CA, cert-manager, cloud PKI) with the client’s identity in the subject/SAN.
  2. Server configured for mTLS — API server’s TLS configuration requires client certificates and specifies which CAs to trust.
  3. TLS handshake — client connects, server presents its certificate (standard TLS). Server sends CertificateRequest. Client presents its certificate.
  4. Client proves possession — client signs the handshake transcript with its private key (CertificateVerify message).
  5. Server validates — checks: certificate chain valid, issued by trusted CA, not expired, not revoked. Extracts identity from certificate.
  6. Identity mapping — server maps the certificate identity (CN, SAN, or SPIFFE ID) to an internal principal with specific permissions.
  7. Request processed — HTTP request is processed with the authenticated identity. Authorization policies determine what the client can access.

In real systems

Nginx API gateway with client certificates:

server {
    listen 443 ssl;
    ssl_certificate     /etc/ssl/server.pem;
    ssl_certificate_key /etc/ssl/server-key.pem;

    # Require client certificate
    ssl_client_certificate /etc/ssl/trusted-client-cas.pem;
    ssl_verify_client required;
    ssl_verify_depth 2;

    location /api/ {
        # Pass client identity to backend
        proxy_set_header X-Client-DN $ssl_client_s_dn;
        proxy_set_header X-Client-CN $ssl_client_s_dn_cn;
        proxy_set_header X-Client-Serial $ssl_client_serial;
        proxy_pass http://backend:8080;
    }
}

curl with client certificate:

# Call API with client certificate authentication
curl --cert client.pem --key client-key.pem --cacert server-ca.pem \
  https://api.partner.com/v1/transactions

# The server verifies client.pem was issued by a trusted CA
# No API key, no Bearer token, no credentials in the request

Python requests with client certificate:

import requests

response = requests.get(
    "https://api.internal.example.com/v1/data",
    cert=("/path/to/client.pem", "/path/to/client-key.pem"),
    verify="/path/to/server-ca.pem"
)

Open Banking / Financial APIs (PSD2):

# European Open Banking (PSD2) mandates certificate-based auth:
# - TPPs (Third Party Providers) authenticate with eIDAS certificates
# - Certificates issued by qualified trust service providers (QTSPs)
# - Certificate contains: organization ID, roles (AISP, PISP), regulatory number
# - Banks verify the certificate to determine what the TPP can access

# Example: MTLS + OAuth2 combined (FAPI profile)
# 1. Client authenticates to authorization server with client certificate
# 2. Authorization server issues access token bound to the certificate
# 3. Client calls resource server with both: access token + client certificate
# 4. Resource server verifies token AND certificate match

AWS API Gateway with mutual TLS:

# Upload truststore (CA certificates for client validation)
aws apigateway create-rest-api \
  --name "secure-api" \
  --mutual-tls-authentication truststoreUri=s3://bucket/truststore.pem

# Clients must present certificates signed by CAs in the truststore
# API Gateway validates the certificate before routing to Lambda/backend

Where it breaks

Certificate identity not mapped to authorization — the API server validates the client certificate (chain is good, not expired) but doesn’t check what the certificate’s identity is authorized to do. Any valid client certificate gets full API access. Certificate authentication must be paired with authorization: map the certificate’s CN/SAN to specific permissions and enforce them per-endpoint.

Client certificate renewal breaks integration — a partner’s client certificate expires. They renew it (new serial number, possibly new key). Your API server still works (same CA, valid chain), but your logging/monitoring that tracked the old serial number loses continuity. Or worse: your allow-list was based on the old certificate’s fingerprint, and the renewed certificate is rejected. Use CA trust (accept any cert from the trusted CA) rather than individual certificate pinning for partner integrations.

Load balancer strips client certificate — the API is behind a load balancer that terminates TLS. The client certificate is validated at the LB, but the backend receives a plaintext HTTP request with no certificate information. The LB must forward the client certificate (or its DN) via headers. If this isn’t configured, the backend can’t perform identity-based authorization — it doesn’t know who the client is.


Operational insight

Certificate-based API authentication is strongest for machine-to-machine communication where both sides are controlled infrastructure (internal microservices, partner integrations, financial APIs). It’s weakest for scenarios with many diverse clients (public APIs with thousands of developers) because certificate provisioning to each client is operationally heavy. The sweet spot: use certificates for high-trust, low-client-count integrations (10-100 partners/services) where the security benefit justifies the provisioning overhead. For high-client-count APIs (thousands of developers), OAuth2 with client credentials is more practical — but for the most sensitive operations (payment initiation, data access), layer certificate-based mTLS underneath OAuth2 (the FAPI pattern used in Open Banking).


Ready to Secure Your Enterprise?

Experience how our cryptographic solutions simplify, centralize, and automate identity management for your entire organization.

Stay ahead on cryptography & PKI

Get monthly insights on certificate management, post-quantum readiness, and enterprise security. No spam.

We respect your privacy. Unsubscribe anytime.