Open Morse

Reference

Authentication

One personal access token, sent as a bearer. There are no keys, no secrets to pair, and no OAuth dance. A token is a sign-in, and it acts as the person who made it.

The header

Every request carries it. Nothing else authenticates: there is no query parameter and no body field, because either would end up in a log.

header
Authorization: Bearer mp_YOUR_MORSE_TOKEN

GET/auth/me

curl -H "Authorization: Bearer $MORSE_TOKEN" \
  "https://onmorse.com/api/auth/me"

What a token looks like

The same shape GitHub uses, and for the same two reasons. The prefix is what lets a secret scanner spot one in a commit. The checksum is what lets Morse refuse a forged or mistyped token without touching the database, which is the only defence against a flood of junk while there is no rate limit.

mp_4Xn2qK…8wZa7Bq2F

mp_
Prefix
What a secret scanner matches on
4Xn2qK…8wZ
40 random characters
Base62, about 238 bits
a7Bq2F
Checksum
CRC32 of the 40, in base62

49 characters

Making one

In the app, at Settings → API tokens. Never through the API; see the 403 below for why.

  • Name it after whatever will use it, up to 60 characters. It is how you will recognise it later.
  • Choose an expiry: 30, 60 or 90 days, a day you pick, or never.
  • Copy it. This is the only time it exists anywhere but your clipboard.

Twenty live tokens per person. Revoke one to make room rather than asking for the cap to move.

What Morse keeps

A SHA-256 of the token and its last four characters. That is all: the last four are for recognising a row in the list, and the hash is for matching a request. Nothing stored can be turned back into a token, so a lost one is lost and a leaked database yields nothing to replay.

The 40 random characters carry about 238 bits, so there is nothing to precompute against and a slow password hash would add cost to every request while protecting nothing.

When one stops working

Three states, worked out per request rather than stored.

StateMeaningWhat you get
activeNot revoked, not past its date, used within the year.Works.
expiredPast the expiry you chose, or unused for 365 days.401. Make a new one; there is no renewing.
revokedYou revoked it, or an admin disabled the account.401, from the next request.

Idleness is the one that surprises people: a token unused for 365 days is finished, whatever its expiry said. Morse stamps last_used_at at most once a minute, so reading through a token does not turn every request into a write.

Refusals

A 401 carries WWW-Authenticate: Bearer, naming the scheme that would have worked. Browsers ignore a bearer challenge, so it never raises a sign-in prompt.

StatusdetailWhat happened
401This token has expired. Create a new one in Settings.The token is real and past its date.
401Not authenticated.Missing, malformed, checksum failed, revoked, idle a year, or the account is disabled. One answer for all of them, deliberately.
403Manage tokens in the Morse app.A token reached a token-management route.

The second row collapses several causes on purpose. A prober must not be able to tell a revoked token from a malformed one, or learn that an account exists; the distinction lives only in Morse’s own logs.

The 403 is narrow: tokens cannot list, make or revoke tokens. A token that could make tokens could quietly mint itself a spare before you revoked it, so that one door is shut to them and open only to a signed-in browser.

Tokens and sessions

The API accepts two ways in: this token, and the browser session cookie the app itself uses. Both are declared as security schemes, which is why the Authorize button in the OpenAPI explorer offers both.

  • The token wins. A request carrying both is a script run by someone who also happens to be signed in, and the token is the identity that request chose.
  • A token never renews a session. Sessions slide forward as you use the app; a token request leaves that alone, because the session is not what it signed in with.
  • Signing out does not touch tokens. Clearing a browser’s cookie ends that browser, nothing else. Revoke in Settings.