Accounts and Sessions on the wire: sign up, sign in, sign out and refresh behind the guard #378

Closed
opened 2026-10-11 16:26:46 +00:00 by root · 22 comments
root commented 2026-10-11 16:26:46 +00:00 (Migrated from gitlab.core-project.space)

Imported from GitHub amaurycoudr/chess-project#378, opened by @amaurycoudr on 2026-10-11 16:26 UTC.


Parent

#364

What to build

A Player can create an Account with an email, a password and a Username, and is signed in as soon as it exists. They can sign in from any browser with their email in any letter case, sign out of this browser only, and stay signed in for 30 days after last use. Each signed-in browser is a Session: one Postgres row and the two cookies ADR 0030 names, rotated on refresh with the 30-second grace. A global guard reads the access cookie. Each procedure declares one of three modes: required, optional (every Room and game procedure, and account.me) or none (sign-up, sign-in, refresh). An expired or invalid access token is UNAUTHORIZED in every mode, never an anonymous call. A deleted Account is refused at once.

This ticket also writes the Username ADR, adds Username to the glossary and rewords Account. It makes the signing secret required config, and confirms that Chromium keeps Secure and __Host- cookies on plain-HTTP localhost before anything builds on them.

Seam: loadConfig; the RPC wire (the real app in process on the PGlite database, with a cookie jar per client, a fake clock and a cheap hash cost)

Acceptance criteria

  • auth.signUp, signIn, signOut, refresh, usernameAvailability and account.me exist in the contract, resource-oriented, with the errors the spec lists (EMAIL_TAKEN, USERNAME_TAKEN, USERNAME_NOT_ALLOWED, INVALID_CREDENTIALS, UNAUTHORIZED); UNAUTHORIZED joins the refusal status table as 401
  • Sign-up, sign-in and sign-out set and clear both cookies with exactly ADR 0030's names and attributes; the access cookie lives as long as the Session while the JWT inside expires in 15 minutes
  • The Username follows the format (3 to 20 characters, ASCII letters, digits, _, -) and the server-side reserved and blocked lists, each refusal tested; email and Username are unique ignoring case, tested with the other letter case; the availability read answers free, taken or not allowed
  • Passwords are refused under 8 characters and over 72 bytes; sign-in with an unknown email and with a wrong password give the same INVALID_CREDENTIALS, with a dummy hash compared for the unknown email
  • Refresh rotates the token and pushes expiry to 30 days; the replaced token works within 30 seconds and revokes the Session after; an unknown token and a token after sign-out fail; a failed refresh answers UNAUTHORIZED and clears both cookies
  • An expired access token on a Room read is UNAUTHORIZED; no cookie at all is a Spectator; a deleted Account's still-valid access token is refused
  • Only hashes of passwords and refresh tokens are stored
  • The RPC context carries the optional Account (id and Username) beside the Guest
  • loadConfig requires the signing secret, tested beside the existing config tests; dev, CI, previews and production provide it, so every stack still boots
  • Chromium's handling of both cookies on plain-HTTP localhost is confirmed and recorded in the PR body
  • The Username ADR and its index row land; GLOSSARY gains Username (aliases to avoid: display name, handle, nickname, login) and the reworded Account
  • The PR body proposes rewording the API standards' "one feature module" and Seat token header lines
  • The API's new auth/ folder is declared in the API's layer order and framework-import rules (the owner ruled on the accounts breakdown that declaring a folder the spec creates counts as the requested lint change; nothing else in lint config changes)
  • The owner has created production's signing secret before the spec reaches main, so production still boots
  • The ADR is this ticket's first commit, before any code it governs
> _Imported from GitHub [amaurycoudr/chess-project#378](https://github.com/amaurycoudr/chess-project/issues/378), opened by [@amaurycoudr](https://github.com/amaurycoudr) on 2026-10-11 16:26 UTC._ <!-- github-issue:378 --> --- ## Parent #364 ## What to build A **Player** can create an **Account** with an email, a password and a **Username**, and is signed in as soon as it exists. They can sign in from any browser with their email in any letter case, sign out of this browser only, and stay signed in for 30 days after last use. Each signed-in browser is a **Session**: one Postgres row and the two cookies ADR 0030 names, rotated on refresh with the 30-second grace. A global guard reads the access cookie. Each procedure declares one of three modes: required, optional (every **Room** and `game` procedure, and `account.me`) or none (sign-up, sign-in, refresh). An expired or invalid access token is UNAUTHORIZED in every mode, never an anonymous call. A deleted **Account** is refused at once. This ticket also writes the Username ADR, adds **Username** to the glossary and rewords **Account**. It makes the signing secret required config, and confirms that Chromium keeps `Secure` and `__Host-` cookies on plain-HTTP localhost before anything builds on them. Seam: `loadConfig`; the RPC wire (the real app in process on the PGlite database, with a cookie jar per client, a fake clock and a cheap hash cost) ## Acceptance criteria - [ ] `auth.signUp`, `signIn`, `signOut`, `refresh`, `usernameAvailability` and `account.me` exist in the contract, resource-oriented, with the errors the spec lists (`EMAIL_TAKEN`, `USERNAME_TAKEN`, `USERNAME_NOT_ALLOWED`, `INVALID_CREDENTIALS`, `UNAUTHORIZED`); `UNAUTHORIZED` joins the refusal status table as 401 - [ ] Sign-up, sign-in and sign-out set and clear both cookies with exactly ADR 0030's names and attributes; the access cookie lives as long as the **Session** while the JWT inside expires in 15 minutes - [ ] The **Username** follows the format (3 to 20 characters, ASCII letters, digits, `_`, `-`) and the server-side reserved and blocked lists, each refusal tested; email and **Username** are unique ignoring case, tested with the other letter case; the availability read answers free, taken or not allowed - [ ] Passwords are refused under 8 characters and over 72 bytes; sign-in with an unknown email and with a wrong password give the same `INVALID_CREDENTIALS`, with a dummy hash compared for the unknown email - [ ] Refresh rotates the token and pushes expiry to 30 days; the replaced token works within 30 seconds and revokes the **Session** after; an unknown token and a token after sign-out fail; a failed refresh answers UNAUTHORIZED and clears both cookies - [ ] An expired access token on a **Room** read is UNAUTHORIZED; no cookie at all is a **Spectator**; a deleted **Account**'s still-valid access token is refused - [ ] Only hashes of passwords and refresh tokens are stored - [ ] The RPC context carries the optional **Account** (id and **Username**) beside the **Guest** - [ ] `loadConfig` requires the signing secret, tested beside the existing config tests; dev, CI, previews and production provide it, so every stack still boots - [ ] Chromium's handling of both cookies on plain-HTTP localhost is confirmed and recorded in the PR body - [ ] The Username ADR and its index row land; GLOSSARY gains **Username** (aliases to avoid: display name, handle, nickname, login) and the reworded **Account** - [ ] The PR body proposes rewording the API standards' "one feature module" and Seat token header lines - [ ] The API's new `auth/` folder is declared in the API's layer order and framework-import rules (the owner ruled on the accounts breakdown that declaring a folder the spec creates counts as the requested lint change; nothing else in lint config changes) - [ ] The owner has created production's signing secret before the spec reaches `main`, so production still boots - [ ] The ADR is this ticket's first commit, before any code it governs
root commented 2026-10-11 16:26:46 +00:00 (Migrated from gitlab.core-project.space)

GitHub issue relations, imported as "related" links (GitLab CE has no sub-issues or blocking links).

> _GitHub issue relations, imported as "related" links (GitLab CE has no sub-issues or blocking links)._ <!-- github-relations --> - Parent (sub-issue of): #364 - Blocked by: #377, #376 - Blocking: #382, #379
root commented 2026-10-13 08:39:47 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #379

mentioned in issue #379
root commented 2026-10-13 08:39:49 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #382

mentioned in issue #382
root commented 2026-10-13 08:41:22 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #364

mentioned in issue #364
root commented 2026-10-13 08:41:26 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #376

mentioned in issue #376
root commented 2026-10-13 08:41:26 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #377

mentioned in issue #377
root commented 2026-10-13 08:42:28 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #364

marked this issue as related to #364
root commented 2026-10-13 08:42:36 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #376

marked this issue as related to #376
root commented 2026-10-13 08:42:37 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #377

marked this issue as related to #377
root commented 2026-10-13 08:42:37 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #382

marked this issue as related to #382
root commented 2026-10-13 08:42:37 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #379

marked this issue as related to #379
root commented 2026-10-14 10:36:00 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 32b51615a2

mentioned in commit 32b51615a24ac9e2fad9336ba84e37025f6d1f03
root commented 2026-10-14 10:36:00 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 045e0c70f1

mentioned in commit 045e0c70f1a616271ade5d8e8e992125efd74ce8
root commented 2026-10-14 10:36:01 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 71fdd96928

mentioned in commit 71fdd969287abb61120dc70a2789419dd647522c
root commented 2026-10-14 10:36:01 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit a6157e1eee

mentioned in commit a6157e1eeed3447cb75cdf29d988ceae3a2364b8
root commented 2026-10-14 10:36:01 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 0634afd438

mentioned in commit 0634afd438d7827123e204ffd3c9d926065d655e
root commented 2026-10-14 10:36:16 +00:00 (Migrated from gitlab.core-project.space)

mentioned in merge request !523

mentioned in merge request !523
root commented 2026-10-14 12:19:55 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 6b480f21be

mentioned in commit 6b480f21be780e8f289f062d3bd97a9a88f375ad
root commented 2026-10-14 12:19:55 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 938a5409c7

mentioned in commit 938a5409c797461ae28c59400be970da7df69857
root commented 2026-10-14 12:19:56 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit f06de8b474

mentioned in commit f06de8b4744d9d2fcf8d319669ede92fabce59a7
root commented 2026-10-14 12:19:56 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 402c03186b

mentioned in commit 402c03186be64163ef3358d06d5e5602949f274e
root commented 2026-10-14 12:19:56 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 081eca8464

mentioned in commit 081eca846411abafeac0ccbd2cc031182073017d
root (Migrated from gitlab.core-project.space) closed this issue 2026-10-14 13:28:23 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
amaurycoudr/chess-project#378
No description provided.