uloggdDevelopers
Developer portaluloggd
Get startedResourcesArcade
Developer portaluloggdOverviewAuthenticationScopesRate limitsErrorsPaginationVersioning

Authentication

One header, one token format, and four refusals that answer alike on purpose.

An integration carries its key in one header, and none of its requests are answered without it.

Authorization: Bearer ulg_live_...

The token

A token is ulg_live_ followed by 32 hexadecimal characters. The prefix makes it recognisable in a log or a paste, which is what makes leak scanning possible at all.

Only a hash of the token is stored. Nobody, including us, can read a key back after it is made: the column holding the hash is granted to no role that speaks to the API, so a query reaching for it is refused even to the key's own owner.

What a refusal means

A key that is unknown, revoked, expired or malformed all answer the same: 401 invalid_key. They are deliberately not told apart, so a token cannot be probed for which kind of wrong it is.

A key that is live but lacks the permission answers 403 insufficient_scope, and the body names the scope it was missing:

{
  "error": {
    "code": "insufficient_scope",
    "message": "This key does not hold library.write.",
    "scope": "library.write"
  }
}

What a key can reach

The request runs as the account that owns the key. A key holding lists.write on an account that cannot write to some private list still cannot write to it, because the list's own rule is what refuses, not the scope.

A scope is a ceiling. It narrows what a key may attempt; it never widens what its owner may do.

The website comes in the same way

uloggd does not reach the database around its own API: every button on the site calls these same routes. What it carries is the session cookie of whoever is signed in rather than a key, because a key the site issued to itself would be one nobody could revoke.

That changes three things, and only three:

  • A session holds no scopes. It is the account itself, so 403 insufficient_scope never happens to it.
  • A session spends no key's allowance, and the answer carries no X-RateLimit-*. The limits the account already had still apply.
  • On /api/v1/me, key comes back null.

A cookie is only accepted when the request comes from this origin, because a cookie travels on its own and a header does not. A key skips that check: it only arrives if somebody wrote it.

When the header is there, it decides. A revoked key answers 401 invalid_key even if the browser is also carrying a valid session: presenting a key is saying which identity you want, and a key that keeps working after it is revoked, for as long as its author is signed in, is a revocation nobody can test.

Session-only routes

Some routes answer only a session of the site itself, never a key, because a key must not gain more power than it was given or change the account that made it:

  • everything under /api/v1/account: apps and keys, sessions, the account's state and bootstrap, the export, the data, the username, the birth date, identities and connections, and every Arcade developer and player route, the development URL's (GET, PUT and DELETE /api/v1/account/apps/{id}/arcade/override) included;
  • the profile layout's images (/api/v1/profile/layout/media), which spend the account's own storage;
  • a report on an Arcade game (POST /api/v1/reports with on ARCADE_APP).

A key gets 403 forbidden on them. The Arcade ones are in Resources: Arcade. The development URL's also want a session verified with two-factor, even to read it (see The development URL).

Keeping a key

  • Treat it like a password, because it is one that skips the password.
  • Give each integration its own key. Revoking one then costs nothing else.
  • Revoke it from the app's Keys tab in the portal the moment a key is loose. Revocation takes effect on the next request.

Overview

Read and write a uloggd account from your own code, with a key that can never do more than its owner.

Scopes

What each permission covers, and the ones that are deliberately absent.

On this page

The tokenWhat a refusal meansWhat a key can reachThe website comes in the same waySession-only routesKeeping a key