Skip to content

Authentication

Every WebSocket connection is authenticated via either an API key or an OIDC id_token, sent in the first JSON message. REST endpoints (/jobs/*) accept the same two credential types in a single Authorization: Bearer header — see Downloads.

The very first message you send on a new connection must be a compile.start message containing your credential — api_key or oidc_token, not both:

{
"type": "compile.start",
"api_key": "pk_live_...",
"entry": "src/main.ts",
"release": true
}

If the key is valid, the server proceeds without sending an explicit acknowledgement — you will receive compile.queued when the job is enqueued.

If the credential is invalid, missing, or the first message is not compile.start, the server sends an auth.error message and closes the connection.

{
"type": "auth.error",
"message": "human-readable description",
"code": "INVALID_KEY"
}
CodeCause
MISSING_KEYNeither api_key nor oidc_token was present on the first message
INVALID_KEYKey does not exist in the system
KEY_EXPIREDKey has passed its configured expiry date
KEY_REVOKEDKey was manually revoked
MISSING_ACCOUNToidc_token was sent without account_id and/or org_id
INVALID_TOKENoidc_token failed signature/issuer/audience verification, or is missing a required claim
REF_NOT_PROTECTEDoidc_token’s branch/tag isn’t a GitLab protected ref (see below)
NOT_CONNECTEDNo connection made by account_id matches oidc_token’s project/ref
CREDENTIAL_REVOKEDThe connection matching account_id + oidc_token’s project/ref was revoked from the dashboard
ORG_MISMATCHThe matching connection bills to a different org than org_id claimed

Pure API keys start with pk_live_ for production keys and pk_test_ for test keys (test keys are for internal use). The full value is 40–60 characters.

Instead of storing an API key as a CI secret, a GitLab pipeline can authenticate with a short-lived id_token GitLab itself mints per-job — no credential is stored anywhere, on either side.

Setup (once per project/branch): on the dashboard’s Integrations page, add a GitLab connection for the project path and branch you want to authorize. GitLab only stamps ref_protected: true onto an id_token for a protected branch or tag, and Pure requires that — so the branch you connect must be protected in GitLab’s own settings first. The page shows two IDs next to the connection once it’s created: your account ID (a user id) and the org ID it bills to — copy both, you’ll need them in the next step.

In .gitlab-ci.yml:

compile:
id_tokens:
PURE_ID_TOKEN:
aud: https://api.pure.dev
script:
- curl -fsSL https://apt.pure.dev/install.sh | sh
- pure build src/main.ts \
--oidc-token "$PURE_ID_TOKEN" --account <your-account-id> --org <your-org-id>

--oidc-token (or the $PURE_OIDC_TOKEN environment variable, which the CLI checks automatically — so passing the id_token as an env var works too, no flag needed) takes precedence over any stored/PURE_API_KEY credential when set. --account/$PURE_ACCOUNT_ID and --org/$PURE_ORG_ID are required whenever an oidc token is used — an id_token alone doesn’t identify a single connection, since the same GitLab project/branch can be connected by more than one person, billing to more than one org (see below). Neither value is a secret — safe to hardcode directly in .gitlab-ci.yml, or set as a plain (unmasked) CI/CD variable.

What Pure checks, in order:

  1. account_id and org_id were both provided — rejected outright (MISSING_ACCOUNT) before any token verification if either is missing.
  2. The token verifies against GitLab’s own JWKS (https://gitlab.com/oauth/discovery/keys), with the expected issuer (https://gitlab.com) and audience (https://api.pure.dev).
  3. ref_protected is true — an id_token from an unprotected branch is rejected outright, so a random merge-request branch can never authenticate as a connected project.
  4. The token’s project_path + ref, together with account_id, match an active (non-revoked) connection made by that user. A revoked connection behaves like a revoked API key: recognized, but rejected (CREDENTIAL_REVOKED).
  5. That connection’s own org matches org_id — a mismatch is rejected (ORG_MISMATCH) rather than silently billing wherever the connection happens to point.

There’s no ownership check on which GitLab projects a person may connect — connecting some/project doesn’t require proving you control it, the same way an AWS IAM role trust policy doesn’t require the trusting account to prove anything about the trusted one. Any number of different people, in the same org or different orgs, may independently connect the exact same project/ref; each such connection is authenticated separately by its own account_id. Only one connection per (your user, that exact project/ref) pair is allowed — connecting it again fails until you revoke the existing one.

  • Send the credential only in the first compile.start message. Do not include it in subsequent messages on the same connection.
  • Never log the compile.start message — it contains the raw key or token.
  • Use environment variables or a secrets manager to store an API key. Do not hardcode it. GitLab OIDC avoids this entirely — there’s no long-lived secret to store or leak.
  • See API Keys for key rotation and revocation.