Skip to content

Device token

Helsa is a single-user system. There is nobody to distinguish from anybody else, so a username-and-password flow would add a login form, a password reset path, and a session store without answering any question the system actually has.

What it needs instead is device authentication: is this connection one of my devices? That is answered in two independent layers.

Layer Mechanism Checked by
Transport Mutual TLS. The client presents a certificate signed by your private CA. The reverse proxy, before any HTTP is parsed. See TLS and mutual TLS.
Application A long-lived bearer token in the Authorization header. The API.

Both are required on the public interface. The browser dashboard uses only the token, because it is reachable only over your LAN or VPN, where the network itself is the gate.

There is deliberately no HTTP endpoint that issues tokens — an endpoint that mints credentials is an endpoint anyone can call. Tokens are issued on the server, by you, with a command.

Terminal window
cd helsa/deploy
docker compose --profile tools run --rm token -subject iphone

Output:

subject: iphone
access_token: eyJhbGciOiJIUzI1NiIs...
refresh_token: ...

The access_token is what you paste into the app or the dashboard.

Issue one token per device, with a name you will recognise later:

Terminal window
docker compose --profile tools run --rm token -subject iphone
docker compose --profile tools run --rm token -subject browser

The token TTL comes from HELSA_ACCESS_TTL. The token tool is configured with a long default (a year) rather than the API’s normal 15 minutes.

That is a considered trade-off, not laziness:

Set your own value when issuing:

Terminal window
HELSA_DEVICE_TOKEN_TTL=8760h docker compose --profile tools run --rm token -subject iphone

A device token is a bearer credential. Whoever holds it can read your entire health history through the API — subject to also getting past the mutual-TLS gate on the public interface, or being on your LAN or VPN for the dashboard.

  • Store it in a password manager. The app keeps it in the iOS Keychain; the dashboard keeps it in an httpOnly cookie.
  • Do not paste it into a shell history, a chat, a screenshot, or an issue report.
  • Do not commit it. Not to a private repository either — git history is forever.

Tokens can be revoked through the Redis deny-list, which is what POST /v1/auth/logout writes to. With one or two devices, the blunter instruments are usually right:

Situation Do this
One device compromised or retired Log it out, then issue a fresh token for the replacement.
HELSA_JWT_SECRET leaked Change the secret and restart the API. Every existing token stops working; reissue for each device.
Phone lost Rotate the secret and rotate the CA — the client certificate went with the phone. See certificate rotation.

Point the app at the server and run a first sync.