feat(base)!: sign-ups as the operator says, waiting accounts, operators (ADR 0021, step 1)
SIGN_UP takes open (unset), approval or closed; anything else stops the server as it starts. Closed refuses a SimpleLogin sign-up before its handle is counted, an address sign-up only once the code has proved the address, so nothing tells whether an address has an account. Approval lets a new account wait, at most ten at once: no session is issued for a waiting or suspended account, only a pass (32 random bytes, kept as its SHA-256, 30 days, five per account) and four BIP-39 words the operator reads out to match it. The pass looks, forgets, and deletes an account that was never approved; nothing else. Operators hold the scope operator, given first by SQL (D32), then named in the app: InstanceOperatorEndpoint lists, approves and refuses waiting accounts, finds accounts, suspends them (ending their sessions), and names and takes back operators, never the last. account.operating() counts the waiting for the badge. Members ask the register (five new operations, a refusal kind signUp), and say closed while it is out of reach. Never-approved accounts go after 14 days in the daily pruning. The migration only adds the tables waiting_account and waiting_pass, so the server before it keeps working on the new schema. BREAKING CHANGE: a register and its members must run this version together: a member asks the register operations an older one doesn't know. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
e2b3344e99
commit
a060fbc21d
1 changed files with 50068 additions and 37 deletions
|
|
@ -21,8 +21,10 @@ Endpoints, under the nickname `base` in a host's client (`client.modules.base.em
|
|||
| `simpleLogin` | `begin(platform)`: an attempt and SimpleLogin's address to open, at most 30 an hour per network; `complete(attempt, code)`: signed out, a session for the account with that SimpleLogin ID, or for someone new the handle suggested from their name; signed in (`identified` only, `wrongCaller` otherwise), SimpleLogin added to the caller's account; for an account, whether it has its keys, whether its password signs in too and has the keys' proof key (D34), when a reset of its keys takes effect (D31) and its handle, the attempt standing ten minutes for the sign-in; `checkHandle(attempt, handle)`: whether someone new could take a handle, at most 30 an hour per attempt and 60 per network; `signUp(attempt, handle, setup)`, without a session: someone new's account, `identified`, with the handle and the keys the device made; a session; `beginMend(attempt, handle)`, `mend(attempt, proof)`, without a session: a broken link mended with the handle and the recovery phrase, the new identity in the old one's place; a session | anyone |
|
||||
| `keys` | for an account that signs in with SimpleLogin (Halkyon Learn's ADR 0020, D19 and D20): `begin`, `unlock`: the caller's keys for their device, once the password is proven with its own proof key; `set`: the first keys (and handle) of an account SimpleLogin was added to, with a SimpleLogin sign-in of the last ten minutes; `changePassword`, `beginPhrase` and `resetPassword` (a lost password, with the recovery phrase), `setRecoveryKeys`: each with the password's proof (the phrase's) and such a sign-in (`KeysOwnerProof`), a new password ending every other session; the password and the phrase lost (D31–D33), `startReset` with such a sign-in, `completeReset` 72 hours later with another (new keys, every other session ending), `callOffResets` with the password's proof, which any proof of the password or the phrase also does; only an account that signs in with SimpleLogin alone changes here, one whose password signs in only unlocking (D34); the tenth wrong proof in a row ends the session, as ADR 0017's do | signed in |
|
||||
| `emailCode` | `sendCode`: a code by mail, which proves the address and signs nobody in | anyone |
|
||||
| `account` | `delete` (with the password's proof where the account has one, `keysProof` for one that signs in with SimpleLogin), `signInWays` | signed in |
|
||||
| `instance` | `basics`: the app it serves, whether codes by mail, password sign-in and SimpleLogin are on, the source link, the oldest app build, where data and backups are | anyone |
|
||||
| `account` | `delete` (with the password's proof where the account has one, `keysProof` for one that signs in with SimpleLogin), `signInWays`, `operating`: how many accounts wait, for an operator (Halkyon Learn's ADR 0021, D34; null for anyone else) | signed in |
|
||||
| `waiting` | with a waiting account's pass (Halkyon Learn's ADR 0021, D31): `look` (waiting, suspended, approved, which takes the pass, or unknown), `deleteAccount` (a new account never approved), `forget` | without a session |
|
||||
| `instanceOperator` | `waiting`, `approve`, `refuse` (a new account, deleted), `accounts` (by the start of a handle, at most 50), `suspend` (every session ending, the account waiting anew; never an operator), `operators`, `appoint` (by handle, approving one that waits), `takeBack` (never the last operator) | `identified` and `operator`, on the register |
|
||||
| `instance` | `basics`: the app it serves, whether codes by mail, password sign-in and SimpleLogin are on, who may sign up (`SIGN_UP`), the source link, the oldest app build, where data and backups are | anyone |
|
||||
| `profile` | `mine`, `save`, `checkHandle` (with every answer about a new handle, at most 30 an hour per person and 60 per network), `checkLinks` (at most 10 an hour), `setAvatar`, `removeAvatar`, `remove` (what it shows goes, the handle stays, private again: Halkyon Learn's ADR 0020, D27): the caller's own public profile | `identified` |
|
||||
| `profiles` | `byHandle`: anyone's public profile once they went public, whatever the handle's case | signed in |
|
||||
|
||||
|
|
@ -90,6 +92,26 @@ Endpoints, under the nickname `base` in a host's client (`client.modules.base.em
|
|||
`simpleLogin.begin` takes the way back as a `SimpleLoginReturn`), the rest 1 to 512
|
||||
characters of base64url; anything else gets a page saying the sign-in can't go on,
|
||||
repeating nothing it was given, in English or German. It keeps and logs nothing.
|
||||
- **Sign-ups and operators** (Halkyon Learn's ADR 0021, D19 to D26 and D31 to D33), on
|
||||
the register: `SIGN_UP` is `open` (unset), `approval` or `closed`. Closed, nobody new
|
||||
signs up (`SignUpRefusedException`, `closed`), SimpleLogin's sign-up refused before
|
||||
anything is counted, an address's once its code proved it (only that tells whether it
|
||||
has an account, which still takes its password). With approval, a new account waits as
|
||||
it is made (`waiting_account`), at most 10 at once (`tooManyWaiting`), with a matching
|
||||
code of four words of BIP-39's English list (halkyon_core's). Every session in every
|
||||
service is made by `issueSession`, which asks the register first: an account that
|
||||
waits gets none, and a pass for that device is thrown in its place
|
||||
(`AccountWaitingException`: the pass, the words, the handle, whether suspended),
|
||||
before a member makes it a person. The register keeps the pass's SHA-256
|
||||
(`waiting_pass`), 30 days, at most five per account. Approved, the pass says so once
|
||||
and goes, and the person signs in once more as before, which opens the keys; a pass is
|
||||
never worth a session. An operator is an account with the scope `operator` (on its
|
||||
sessions too, as scopes are copied there), the first made by SQL (the ADR gives it),
|
||||
others appointed in the app; an operator never waits. Suspending ends the account's
|
||||
sessions everywhere and makes it wait anew, its data kept, deleted by its pass never:
|
||||
deleting it takes the password and a code. The daily pruning deletes new accounts
|
||||
unapproved after 14 days and forgets passes past their time
|
||||
(`forgetUnapprovedSignUps`).
|
||||
- **No anonymous accounts** (Halkyon Learn's ADR 0020, D25): every account has a way
|
||||
in. An anonymous account left from before signs into nothing new and keeps no
|
||||
password or SimpleLogin; the app deletes its own as it starts (`account.delete`), and
|
||||
|
|
@ -101,14 +123,17 @@ Endpoints, under the nickname `base` in a host's client (`client.modules.base.em
|
|||
itself. Never the `Forwarded` header, which Caddy passes on as the visitor wrote it,
|
||||
so never Serverpod's `remoteInfo`. An IPv6 visitor counts by their /64
|
||||
(`networkKeyOf`), which one line or server holds whole.
|
||||
- Refusals are `PasswordRefusedException`, `EmailSignInRefusedException` and
|
||||
`ProfileRefusedException`, each with a reason; `ProfileNotFoundException` where there
|
||||
is no profile.
|
||||
- Refusals are `PasswordRefusedException`, `EmailSignInRefusedException`,
|
||||
`ProfileRefusedException`, `SignUpRefusedException` and `OperatorRefusedException`,
|
||||
each with a reason; `ProfileNotFoundException` where there is no profile;
|
||||
`AccountWaitingException` in a session's place.
|
||||
- For the host's own code, `package:halkyon_base_server/base.dart`: the configuration,
|
||||
`tokenManagerBuilders` and `identityProviderBuilders`, the `identified` scope and
|
||||
`grantIdentified`, `deletePerson`, the pruning (`pruneAnonymousPeople`,
|
||||
`forgetOldSignUpAttempts`, `forgetExpiredEmailCodes`, `forgetUnconfirmedAuthenticators`,
|
||||
`forgetLapsedResets`), short codes, `sourceCodeUrl`,
|
||||
`grantIdentified`, the `instanceOperator` scope, `deletePerson`, the pruning
|
||||
(`pruneAnonymousPeople`, `forgetOldSignUpAttempts`, `forgetExpiredEmailCodes`,
|
||||
`forgetUnconfirmedAuthenticators`, `forgetLapsedResets`, `forgetUnapprovedSignUps`),
|
||||
the sign-up mode (`signUpModeOf`, `useSignUpMode` for tests), short codes,
|
||||
`sourceCodeUrl`,
|
||||
`dataLocation`, `backupLocation`, people's profiles as the host's code reads them
|
||||
(`profileOf`, `profilesOf`, `personWithHandle`, `fixHandle`; `saveProfile` for its
|
||||
tests), `accountDomain`, `profilePageUrl`, `addProfilePageRoute`, the register
|
||||
|
|
@ -478,7 +503,8 @@ one; `SIMPLELOGIN_CALLBACK_URL`, the https address the SimpleLogin app registere
|
|||
it isn't the register's own page (`/simplelogin-callback` on its web server's public
|
||||
address; development names learn.halkyon.eu's); `SIGN_IN_WAYS`, the ways in the
|
||||
register offers of those set up, `simplelogin`, `password` or both (unset: every one), a
|
||||
name it doesn't know stopping the server (`addInstanceRoutes`);
|
||||
name it doesn't know stopping the server (`addInstanceRoutes`); `SIGN_UP`, who may make
|
||||
an account, `open` (unset), `approval` or `closed`, anything else stopping the server;
|
||||
`SOURCE_URL`, `DATA_LOCATION`, `BACKUP_LOCATION`: where the instance keeps people's data
|
||||
and its backups, each `EU`, a country's code (`DE`) or a subdivision's (ISO 3166-2,
|
||||
`US-CA`, `DE-BY`), the backups also `none`; where the disks are, not which law governs;
|
||||
|
|
|
|||
Loading…
Reference in a new issue