Device token
Why there is no login screen
Section titled “Why there is no login screen”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.
Issuing a token
Section titled “Issuing a token”cd helsa/deploydocker compose --profile tools run --rm token -subject iphoneOutput:
subject: iphoneaccess_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:
docker compose --profile tools run --rm token -subject iphonedocker compose --profile tools run --rm token -subject browserLifetime
Section titled “Lifetime”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:
HELSA_DEVICE_TOKEN_TTL=8760h docker compose --profile tools run --rm token -subject iphoneTreat it as a credential
Section titled “Treat it as a credential”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
httpOnlycookie. - 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.
Revoking
Section titled “Revoking”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. |