Documentation Menu

Authentication

6 min read

A request to Quillet's two APIs carries one of four credentials, and each route accepts only some of them.

Why the credential you mint must match the route

Access explains why Quillet has four credentials, a session, a personal token, a CLI key and a delivery key, and why ownership belongs to a person. This page covers the practical side. It shows how each credential is sent, which routes take it, and what the API answers when you send the wrong one. Read it before you write a script or a build step, so that the credential you mint is one the route will accept.

How each credential is sent and checked

How a credential is sent

  • A session is what the dashboard uses. It signs in with POST /api/v1/auth/login and holds the quillet_session cookie, which your browser sends on every request. A script should not imitate this. Use a token or a key instead.
  • A personal token is sent with HTTP basic auth, with the token id as the user name and the secret as the password. Token ids start with qt_.
  • A CLI key and a delivery key are sent with HTTP basic auth in the same way, using the key id and its secret. Key ids start with qk_. The Keys page calls a CLI key's kind CLI.

With curl, that is -u '<id>:<secret>'. In any other client it is the header Authorization: Basic followed by the base64 of <id>:<secret>. Every secret is shown once, when it is minted, and never again.

Which route accepts which

Routes Session Personal token CLI key Delivery key
Personal tokens, /api/v1/auth/tokens yes no no no
Listing and creating projects; listing members yes no no no
Adding and removing members an owner an owner no no
Changing project settings; creating, editing and deleting environments; keys, webhooks, search reindex an owner an owner no no
Reading the project and its environments yes yes any CLI key of the project no
Preview tokens an editor or owner an editor or owner no no
Content: documents, versions, publishing, schemas, folders, assets, translations, work queue, sync, import, compare, promote by role by role by scope no
GET /api/v1/auth/me yes yes yes no
Delivery: content, list and search under /d/v1 no no no yes

A public file, /d/v1/<project>/assets/<id>, needs no credential at all. That is just as well, because an <img> sends none.

"By role" means the person's role in that project. A viewer reads, an editor writes and publishes, and an owner does both and also runs the project. "By scope" means the key's scopes: content:read to read, content:write to save and delete, and content:publish to publish and unpublish. A CLI key either works across every environment or is bound to one. A delivery key is always bound to one environment, and it reads published content only. With the assets:private scope it also gets signed links to private files.

What a refusal looks like

When the API refuses a request because of its credential, it answers with a small JSON object, { "error": …, "message": … }. The error is a code, and the message is a sentence you can show to a person:

  • 401 unauthorized: there is no credential, or the API does not recognise the one sent. With no credential, the management API answers Sign in to continue., and a personal token sent to delivery gets Invalid API key.
  • 403 forbidden: the credential is real, but it cannot do this. The message says why: This needs the owner role, which an API key never has., This needs a person: sign in or use a personal token., A delivery key can only be used on the delivery API., or This key cannot read that environment.
  • 404 not_found: the project, the environment or the document does not exist, or this credential is not allowed to know that it does.

A token or key that has been revoked or has expired gets a 401 on its next request.

How to

Dashboard

  1. To mint a personal token, open the account menu in the header, choose Account, and press New token under Personal access tokens.
  2. To mint a key, open Settings, then Keys, and press New key. You need the owner role to do this.
  3. Choose the Kind: CLI for a script or a CI job, Delivery for a site or an app.
  4. Choose the Environment. A delivery key needs one, while a CLI key can have Every environment.
  5. Tick the Scopes. A delivery key always has content:read. Tick assets:private only if the site serves private files.
  6. Press Create key and copy the secret from Your new key. It is not shown again.

API

Before you store a credential, ask the API who it is:

curl -s -u '<keyId>:<secret>' http://localhost:3000/api/v1/auth/me

For a CLI key the answer is "kind":"key", with its project, its env (null when it works in every environment) and its scopes. For a personal token the answer is "kind":"user", with "via":"token".

To read published content, use a delivery key:

curl -s -u '<keyId>:<secret>' 'http://localhost:3000/d/v1/<project>/<env>/list'

Send a CLI key to that same delivery route and the answer is the 403 This key cannot read that environment.

CLI

The CLI signs in with a personal token or a CLI key, never with a password. At a terminal it asks for the id and then for the secret. Before it stores the pair, it checks it against auth/me:

npx quillet login --api-url http://localhost:3000
npx quillet whoami --api-url http://localhost:3000

When there is no terminal, pass --key-id <keyId> and pipe the secret on standard input. In CI, set QUILLET_MANAGEMENT_KEY to <keyId>:<secret> instead of signing in.

Limits

  • Only a person signed in to the dashboard can create or revoke personal tokens, create a project, list their own projects or list a project's members. A personal token gets a 401 or a 403 on those routes. An owner's token can still add and remove members, though, so a leaked owner's token can do that too.
  • A CLI key never has the owner role. Keys, webhooks, members, changes to project settings, and creating, editing or deleting environments all need a person. The key can still read the project and its environments.
  • A delivery key reads /d/v1 and nothing else, and only in one environment.
  • You cannot narrow a personal token to one project or to some scopes, and nobody can see another person's tokens.
  • There is no OAuth flow for third-party apps. There is also no way to sign a request other than basic auth or the session cookie.
  • Where a route has a rate limit, each token and each key spends its own budget. The budgets are listed on Configuration.

Last updated