Related: CDN & Caching · Concepts
API tokens grant programmatic, scoped access to the public CDN API. On the CDN read endpoints (CDN, DEV_TOOLS routers) they are passed as a ?token=<tokenId> query parameter; there is no cookie-based auth. The MANAGE router (bulk translation writes and schema push) is the one exception — it authenticates via an X-API-KEY header instead of the query param (see V1 Functions API for that flow). Both MANAGE endpoints — POST /translations/:locale and POST /schemas (schema push) — require the DEV_TOOLS permission, which only a TokenV2 with DEV_TOOLS in its permissions array can satisfy; a legacy TokenV1 is always rejected with 403. This doc covers the query-param auth path used by the CDN/DEV_TOOLS routers.
Tokens are stored in Firestore:
spaces/{spaceId}/tokens/{tokenId}
No version field. Implicitly grants all read permissions:
TRANSLATION_PUBLIC,TRANSLATION_DRAFT,CONTENT_PUBLIC,CONTENT_DRAFT- Does not grant
DEV_TOOLS
Has version: 2 and an explicit permissions: TokenPermission[] array. Only grants what is listed.
Also supports an optional cacheTtl?: number field, which overrides the default CDN redirect cache TTL (CACHE_REDIRECT_MAX_AGE_DEFAULT, 60s) for requests made with that token. cacheTtl: 0 disables redirect caching entirely (Cache-Control: no-cache). See CDN & Caching.
| Permission | Grants access to |
|---|---|
TRANSLATION_PUBLIC |
Published translations |
TRANSLATION_DRAFT |
Draft translations (requires version param) |
CONTENT_PUBLIC |
Published content |
CONTENT_DRAFT |
Draft content (requires version param) |
DEV_TOOLS |
Dev-tools endpoints, MANAGE endpoints (translation bulk-write, schema push), and all content/translation access. TokenV2 only |
Published request (no `version` param):
→ requires CONTENT_PUBLIC | CONTENT_DRAFT | DEV_TOOLS
Draft request (`version` present):
→ requires CONTENT_DRAFT | DEV_TOOLS
Same pattern applies to translation endpoints with TRANSLATION_* permissions.
To reduce Firestore reads during traffic spikes, tokens are cached in memory inside the Function instance:
// functions/src/v1/middleware/query-auth.middleware.ts
const TOKEN_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes- Cache key:
{spaceId}:{tokenId} - On cache miss: Firestore read, result cached for TTL
- On token not found: entry removed from cache
- Revocations take effect within the TTL window (max 5 min)
publicv1runs withmaxInstances: 10(functions/src/v1.ts) andconcurrency: 20, so the cache is per-instance: a token may be read from Firestore once per active instance. Revocation still takes effect within the TTL window on each instance.
Never log a raw query object. The token arrives as the
?token=query param, soJSON.stringify(req.query)would persist a usable credential into Cloud Logging for its full retention period. Every V1 log statement goes throughredactQuery()(functions/src/utils/log-redact.ts).
functions/src/models/token.model.ts— Token types and type guardsfunctions/src/v1/middleware/query-auth.middleware.ts— Auth middleware (token cache lives here)functions/src/utils/api-auth-utils.ts—canPerform/canPerformAnyhelpersfunctions/src/services/token.service.ts— Firestore token queries