Authentication
Calling the API with a Cognito ID token.
Every route except GET /health and GET /recipients/verify requires a
Cognito ID token in the Authorization header:
GET /v1/aliases HTTP/1.1
Host: api.darkmailer.net
Authorization: Bearer eyJraWQiOiJ...Why the ID token
The API's Cognito authorizer declares no OAuth scopes, and API Gateway treats
the bearer token as an identity token in that case: it verifies the token's
signature against the user pool and its aud claim against the app client.
Access tokens carry no aud, so they are rejected. (To accept access tokens
instead, the authorizer would need authorization_scopes set to a scope the
access token carries.)
Obtaining a token
The API validates tokens issued by the deployment's Cognito user pool. Tokens
come from Cognito's sign-in flows (USER_PASSWORD_AUTH, the hosted UI, or
InitiateAuth against your app client) — the API does not issue them itself.
The browser app (projects/web) signs in with Amplify's SRP flow against the
public ${application_name}-web client and sends the session's ID token.
What the API reads from the token
| Claim | Use |
|---|---|
sub | The account key. Every key the API builds is derived from it. |
aud | Verified by API Gateway against the app client. |
exp | Expiry, enforced by API Gateway. |
Only sub is ever used to look up data. See Tenancy model
for why no request parameter can override it.
Failures
An absent, malformed, or expired token is rejected by API Gateway before any
handler runs. The response is a bare {"message": "Unauthorized"} with status
401 — a different shape from the API's own errors, because it never reaches
application code. Clients should treat any 401 as "re-authenticate".
CORS
The API's browser-facing CORS story is described on the CORS page. In short: preflight requests succeed, but cross-origin calls from a web app need the browser to accept the response's origin — check that page before building a browser client.