Skip to content

Access tokens ​

An access token authenticates a machine against one project. Reach for one when an agent session, a CI pipeline or a direct API call needs to act on your project without a browser sign-in.

Quick start ​

bash
npx @extension.dev/mcp login --project <workspace>/<project>

The command prints a user code and the address extension.dev/device. Approve the code there in a browser signed in to extension.dev, and a project scoped token lands in your local token store.

How it works ​

The project is the scope. A token acts on the one project it was minted for. It cannot read another project in the same workspace, and the TOKEN_PROJECT_MISMATCH refusal names that boundary.

Two doors mint a token. The console does it in project settings, at console.extension.dev/<workspace>/<project>/project-settings/access-tokens. The device flow does it without a pasted secret: the extension_auth MCP tool with action: "login", or the package bin above, prints a code that you approve at extension.dev/device. That address lands on www.extension.dev/device. GitHub is federated server side, so no GitHub token reaches your machine.

A token travels in the Authorization header only. The platform API refuses a token in a query string with the TOKEN_IN_QUERY error. Registry reads at registry.extension.land are the one exception: they accept a ?t= token in the URL, and the mint response shows that usage. Treat a registry URL that carries a token as the credential it contains.

bash
export EXTENSION_DEV_TOKEN=...
curl -s -X POST https://www.extension.dev/api/cli/stores/submit \
  -H "Authorization: Bearer $EXTENSION_DEV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"browsers":["chrome"],"buildSha":"<sha>","channel":"stable"}'

The MCP server reads the same variable. A build reads its own environment values the same way. See the Content Env template for the pattern.

Lifetime ​

One to seven days, chosen at mint time. The default and the maximum are both seven, and the server enforces the cap: a token that leaks stays useful for days, not forever.

WARNING

A seven day maximum means a pipeline with a stored token starts failing within a week. That is the point, and it is the cost of the cap: a pipeline that calls the platform API needs its secret rotated on that cadence. POST /api/oidc/exchange mints a one hour token per run and stores nothing, but that token reads the registry only. It does not authenticate the platform API.

Revocation ​

Revoke from the Access tokens page or with DELETE /api/projects/{id}/access-tokens/{tokenId}. Revocation writes to the record the platform keeps about your token, and two paths read that record differently.

The project API, which builds, releases and store submissions use, checks the record on every request and fails closed. A revoked token is refused with TOKEN_REVOKED, and a token whose record cannot be read is refused with TOKEN_NOT_TRACKED.

The artifact sharing path refuses a token only on an explicit revocation record. When the record store cannot be read, the request proceeds on the strength of the signature, so an outage in one store does not stop sharing everywhere. The registry worker caches the revocation list for 60 seconds, so a fresh revocation takes about a minute to reach it. A leaked token is not dead there the moment you click revoke.

Logging out locally does not revoke the token. extension_auth with action: "logout" removes the local copy and says so. If a token may have leaked, revoke it from the console and treat the next minute as exposed on the sharing path.

Reference ​

FactValue
ScopeOne project
Lifetime1 to 7 days, default 7, clamped by the server
TransportAuthorization: Bearer <token>
Registry exception?t=<token> on registry.extension.land reads
Device code validity15 minutes
Revocation reach, project APIEvery request
Revocation reach, sharing pathAbout 60 seconds
Local store~/.config/extension-dev/auth.json, $XDG_CONFIG_HOME honored, %APPDATA%\extension-dev\auth.json on Windows
CodeStatusMeaning
BAD_TOKEN401The token is invalid
TOKEN_IN_QUERY401The token was sent as ?token= instead of the header
TOKEN_NOT_TRACKED401The platform has no record of the token
TOKEN_REVOKED401The token was revoked
SHARE_TOKEN403A share link token was presented where a project token is required
TOKEN_PROJECT_MISMATCH403The token belongs to another project

A token never reads store credentials, which the platform holds and never returns, and never reads another project.

When it fails ​

  • A pipeline starts failing after a week: the token expired. Mint a new one and rotate the secret. There is no longer option.
  • TOKEN_IN_QUERY: move the token into the Authorization header. Only registry reads accept ?t=.
  • TOKEN_PROJECT_MISMATCH: the token was minted for another project. Mint one in the right project's settings.
  • SHARE_TOKEN: you pasted a ?share= link token. Mint a project token instead.
  • A revoked token still reads a shared artifact: the sharing path caches revocations for about a minute. Wait it out.
  • The OIDC exchange token is refused by the platform API: that token reads the registry only. Use a stored project token for API calls.

Next ​