mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-08 14:41:28 -07:00
increase cardinality of platform accounts
This commit is contained in:
+42
-12
@@ -48,7 +48,8 @@ route without documenting it fails rather than silently shipping an incomplete s
|
||||
- **`password`** — the fallback for any unrecognised or absent `grant_type`. Identifies
|
||||
the account by `username` or numeric `account_id` and requires the matching password
|
||||
(PBKDF2-SHA256, `salt:hash`). An account with no stored hash cannot be logged into at
|
||||
all, which is what closes id/username-only takeover.
|
||||
all, which is what closes id/username-only takeover. When it also carries a verifying
|
||||
`platform_auth`, that identity is **linked** to the account (see below).
|
||||
|
||||
Access tokens live for 1 hour (`TOKEN_TTL_SECONDS` in `@repo/jwt`) and carry a `role`
|
||||
claim, so developer/moderator powers refresh on every login and every refresh grant.
|
||||
@@ -72,16 +73,45 @@ asserts a platform) must be a platform we can verify. Two are:
|
||||
login at all without the app secret — an unset `META_APP_SECRET` answers 500 rather
|
||||
than falling back to trusting the client.
|
||||
|
||||
Everything else is refused. Whichever platform, the value written to an account's
|
||||
`platformId` is the verified one, never the raw `platform_id` field.
|
||||
Everything else is refused. Whichever platform, the identity that gets bound or linked
|
||||
is the verified one, never the raw `platform_id` field.
|
||||
|
||||
### One account, many platform identities
|
||||
|
||||
An account can be reached from several platform identities — a player's PC and their
|
||||
headset both open the same account, with no password after the first time. The links
|
||||
live in the `platform_account` table (`src/platform-db.ts`, migration 0007), one row per
|
||||
(platform, platform id, account).
|
||||
|
||||
That table is the **one source of truth** for both halves of a cached login: the picker
|
||||
(`/cachedlogin/forplatformid`) lists the accounts an identity links to, and the
|
||||
`cached_login` grant asks it whether the account it was handed is linked to the identity
|
||||
just proven. They used to be two separate checks over the account blob's single
|
||||
`platformId`, which could disagree — the client would be offered an account that then
|
||||
answered "no linked account" forever.
|
||||
|
||||
A second device is linked by **logging in with a password there**: the client posts its
|
||||
`platform_auth` alongside the password, and a proof that verifies becomes a link. Only a
|
||||
verified identity is ever linked, since a link is a password-free way into the account.
|
||||
A proof that doesn't verify never fails the login — it just leaves that device without a
|
||||
cached login.
|
||||
|
||||
The account blob keeps `platform`/`platformId` as the account's **primary** identity
|
||||
(the first one linked). It feeds the account DTO and a refreshed token's claims, and
|
||||
nothing authorizes off it.
|
||||
|
||||
## Signup caps
|
||||
|
||||
`create_account` is capped on two independent arms, per verified platform id and per
|
||||
signup IP. The platform arm can't be spoofed or reset by changing networks; the IP arm
|
||||
is coarse and will produce false positives behind NAT, shared campus and mobile
|
||||
`create_account` is capped on two independent arms, per verified platform identity and
|
||||
per signup IP. The platform arm can't be spoofed or reset by changing networks; the IP
|
||||
arm is coarse and will produce false positives behind NAT, shared campus and mobile
|
||||
networks. Both default to 3.
|
||||
|
||||
The platform arm also caps **linking**, or it wouldn't be a cap: an identity at the
|
||||
limit could otherwise have accounts created for it with a password and link its way into
|
||||
all of them. Hitting it never fails a password login — the account just doesn't get a
|
||||
cached login on that device.
|
||||
|
||||
Override per environment via the root `.env` (`RECFLARE_MAX_ACCOUNTS_PER_PLATFORM_ID`,
|
||||
`RECFLARE_MAX_ACCOUNTS_PER_IP`), injected at deploy time so tuning them never means
|
||||
editing a versioned file. Setting an arm to `0` disables it — worth reaching for on a
|
||||
@@ -89,12 +119,12 @@ small private server, or when a shared network is being locked out.
|
||||
|
||||
## Bindings
|
||||
|
||||
| Binding | Type | Notes |
|
||||
| -------------------- | ------------- | ------------------------------------------------------ |
|
||||
| `DB` | D1 | Shared `recflare` database; this worker owns `account` |
|
||||
| `JWT_SECRET` | Secrets Store | Shared HS256 signing key |
|
||||
| `META_APP_SECRET` | Secrets Store | Meta app secret; only used to validate a login nonce |
|
||||
| `MAX_ACCOUNTS_PER_*` | vars | Optional signup caps; read via `intVar` |
|
||||
| Binding | Type | Notes |
|
||||
| -------------------- | ------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| `DB` | D1 | Shared `recflare` database; this worker owns `account`, `refresh_tokens` and `platform_account` |
|
||||
| `JWT_SECRET` | Secrets Store | Shared HS256 signing key |
|
||||
| `META_APP_SECRET` | Secrets Store | Meta app secret; only used to validate a login nonce |
|
||||
| `MAX_ACCOUNTS_PER_*` | vars | Optional signup caps; read via `intVar` |
|
||||
|
||||
Migrations live in `migrations/` and are tracked in their own `d1_migrations_auth`
|
||||
table, so they stay independent of the `rooms` worker's migrations on the same
|
||||
|
||||
Reference in New Issue
Block a user