Skip to content

mTLS

Mutual TLS provides transport-layer client authentication for all traffic from the Amplify BFF to the Arda API Gateway, complementing the existing OAuth 2.0 authorization layer.

  • All external access enters through a single AWS HTTP API Gateway per Environment
  • The API Gateway uses AWS Cognito for OAuth 2.0 user authentication
  • The Arda Frontend Application consists of a React SPA in the browser and a Next.js Amplify BFF
  • All private key material is stored in a dedicated OAM AWS account, separate from workload accounts
  1. Defense in Depth — mTLS adds a second authentication layer alongside OAuth 2.0
  2. Zero Trust — client identity verified at the network layer before any request is processed
  3. Centralized Key Management — all private keys stored in the OAM account; never in workload accounts
PathAuthentication
Browser → AmplifyHTTPS + OAuth 2.0
Amplify BFF → API GatewayHTTPS + mTLS + OAuth 2.0
Mobile App → API GatewayNot in scope (future)
  1. Root CA — 10-year validity, stored in OAM Secrets Manager ({Environment}/Arda/RootCA)
  2. Client Certificate — 365-day validity, stored in OAM Secrets Manager ({Environment}/NextJs/MtlsKeys)
    • Common Name: {Environment}-{Application}-Client (e.g., Prod-NextJs-Client)
    • Rotated automatically every 90 days via Lambda + EventBridge Scheduler
  3. Truststore — S3 bucket in workload account containing only the Root CA public certificate
mTLS Two-Account Architecture

OAM Account (Account A):

  • Generates and stores Root CA and client certificates in Secrets Manager
  • Hosts the rotation Lambda and EventBridge Scheduler
  • Grants cross-account read access to the Amplify service role in the workload account

Workload Account (Account B):

  • Hosts the API Gateway and Amplify application
  • Hosts the public truststore S3 bucket (arda-mtls-truststore-{environment})
  • API Gateway uses the truststore URI to validate client certificates
EnvironmentmTLS EnabledCertificate Naming
DevelopmentOptionalDev-NextJs-Client
StagingRequiredStaging-NextJs-Client
ProductionRequiredProd-NextJs-Client

All environments share a single OAM account but use separate secrets with environment-prefixed names.

Terminal window
# Generate Root CA
openssl genrsa -out MyRootCA.key 4096
openssl req -x509 -new -nodes -key MyRootCA.key -sha256 -days 3650 -out MyRootCA.pem -subj "/CN=ArdaPrivateRootCA"
# Generate Client Certificate
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/CN=Prod-NextJs-Client"
openssl x509 -req -in client.csr -CA MyRootCA.pem -CAkey MyRootCA.key -CAserial MyRootCA.srl -out client.pem -days 365 -sha256

Store Root CA and client certificate in Secrets Manager. Configure cross-account resource policy to allow the Amplify service role to call secretsmanager:GetSecretValue.

API Gateway Configuration (Phase 2: Workload Account)

Section titled “API Gateway Configuration (Phase 2: Workload Account)”
  1. Upload MyRootCA.pem to s3://arda-mtls-truststore-{environment}/MyRootCA.pem (versioning enabled, all public access blocked)
  2. Disable the default *.execute-api.amazonaws.com endpoint to prevent mTLS bypass
  3. Enable mTLS on the API Gateway custom domain with the S3 truststore URI

The Next.js BFF fetches the client certificate from Secrets Manager at startup and caches the https.Agent for 4 hours. On TLS errors, the agent is refreshed and the request is retried once.

Key environment variables:

Terminal window
OAM_REGION=us-east-1
MTLS_SECRET_ARN=arn:aws:secretsmanager:us-east-1:{OAM_ACCOUNT_ID}:secret:Prod/NextJs/MtlsKeys-xxxxxx
API_DOMAIN=api.arda.cards

A Lambda function in the OAM account runs every 90 days (EventBridge Scheduler) to:

  1. Read the Root CA from Secrets Manager
  2. Generate a new client key pair (2048-bit RSA)
  3. Sign a new client certificate (365-day validity)
  4. Update the {Environment}/NextJs/MtlsKeys secret

The Next.js BFF picks up the new certificate automatically via the TTL-based cache refresh — no deployment required.

If mTLS enforcement causes an outage:

  1. Toggle mTLS OFF on the API Gateway custom domain (reverts to HTTPS-only)
  2. Investigate: check S3 truststore URI and certificate validity
  3. Re-enable after fixing configuration

Disabling mTLS is a temporary emergency measure only — it exposes the API to requests without client certificates.

  • API Gateway rejects requests without a valid client certificate (HTTP 403)
  • API Gateway rejects requests from certificates signed by unknown CAs
  • Amplify BFF successfully makes API calls with mTLS + OAuth
  • Default *.execute-api.amazonaws.com endpoint is inaccessible
  • Certificate rotation completes without service interruption
  • Secrets Manager access is logged in CloudTrail