feat(base,app)!: GitHub signs nobody in any more (ADR 0020, D36)
Every account signs in with SimpleLogin or a password now, so GitHub goes from the module, the shared app package, Learn and Todo, ahead of ADR 0017's GITHUB_SIGN_IN_ENDS. - halkyon_base: no `gitHubSignIn` endpoint, GitHub identity provider or GitHub refusals; no `githubClientId`/`githubClientSecret`, no `GITHUB_SIGN_IN_ENDS` and no `forgetGitHubAccounts`; the register loses `account-with-github`. `SignInWays.gitHub`, the basics' `gitHubClientId` and `gitHubSignInEnds`, and `PasswordRefusal`'s `signInAgain` and `noAddress` go. A first password takes a code mailed to the person's own address; an account without one sets none (`wrongCredentials`). - Learn's server: `instance.info()` without `gitHubClientId`, the pruning without GitHub, and `minimumAppBuild` 24, as builds up to 23 (v0.20.0) require `SignInWays.gitHub`: the server deploys with the release of build 24. - halkyon_app: no GitHub button or problems on the start screen, no password offered after a GitHub sign-in, and `ServerpodAuthGateway` without the browser half only GitHub used (`(client, base:)`). The account section offers a first password only to an account with an address. `LoopbackWebAuth` and the apps' schemes stay, SimpleLogin's ways back. - Learn's Profile lists no GitHub; the texts go (English, German). - Serverpod's `serverpod_auth_idp_github_account` stays, as its migrations make it; a row goes with its account, and ADR 0020's D36 gives operators the SQL to delete those left. - deploy/: compose.yaml and .env.example without GitHub. The READMEs, AGENTS.md files and ADR 0017 amended. BREAKING CHANGE: builds up to 23 can't read the instance's answers about an account's ways in; minimumAppBuild is 24. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
ab5b7cd015
commit
4e33133733
1 changed files with 564 additions and 3570 deletions
|
|
@ -20,15 +20,13 @@ Endpoints, under the nickname `base` in a host's client (`client.modules.base.em
|
|||
| `accountRecovery` | `resetAuthenticator`, `beginKeyProof`, `resetPassword` (with the recovery phrase), `startAccountReset`, `completeAccountReset` | without a session |
|
||||
| `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 |
|
||||
| `gitHubSignIn` | `signIn`: a session for whoever has that GitHub account, unless they sign in with a password; none once `GITHUB_SIGN_IN_ENDS` has come | without a session |
|
||||
| `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, GitHub's client ID if any and when GitHub sign-in ends, whether codes by mail, password sign-in and SimpleLogin are on, the source link, the oldest app build, where data and backups are | anyone |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|
||||
- GitHub keeps only the numeric GitHub ID, in Serverpod's own table; email keeps only
|
||||
HMAC-SHA256 of the address, keyed with `emailSignInPepper` (`email_identity`), and its
|
||||
- Email keeps only HMAC-SHA256 of the address, keyed with `emailSignInPepper` (`email_identity`), and its
|
||||
codes as Argon2id hashes (`email_code_request`). Codes are eight Crockford characters,
|
||||
valid 15 minutes, 3 tries; up to 3 valid at once for an address (a fourth voids the
|
||||
oldest, so codes others ask for don't void the one its owner is typing); at most 3 an
|
||||
|
|
@ -103,14 +101,14 @@ 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`, `GitHubSignInRefusedException`,
|
||||
`EmailSignInRefusedException` and `ProfileRefusedException`, each with a reason;
|
||||
`ProfileNotFoundException` where there is no profile.
|
||||
- Refusals are `PasswordRefusedException`, `EmailSignInRefusedException` and
|
||||
`ProfileRefusedException`, each with a reason; `ProfileNotFoundException` where there
|
||||
is no profile.
|
||||
- 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`,
|
||||
`forgetGitHubAccounts`, `forgetLapsedResets`), short codes, `sourceCodeUrl`,
|
||||
`forgetLapsedResets`), 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
|
||||
|
|
@ -121,10 +119,10 @@ Endpoints, under the nickname `base` in a host's client (`client.modules.base.em
|
|||
|
||||
## The instance's register (ADR 0016)
|
||||
|
||||
One operator's services share one account per person: the same address, GitHub account
|
||||
and handle reach the same person in each, under one account ID (each service's
|
||||
One operator's services share one account per person: the same address, SimpleLogin
|
||||
account and handle reach the same person in each, under one account ID (each service's
|
||||
`AuthUser` has it), and deleting the account deletes it everywhere. One service holds
|
||||
the register, its database keeping the ways in (`email_identity`, GitHub's IDs), the
|
||||
the register, its database keeping the ways in (`email_identity`, `halkyon_oidc_identity`), the
|
||||
handles and profiles; the others are its members and ask it. An anonymous account left
|
||||
from before stays its service's own. Sessions stay each service's own: every app signs
|
||||
in itself.
|
||||
|
|
@ -149,7 +147,7 @@ in itself.
|
|||
host's Caddyfile never forwards them).
|
||||
- **Signing in on a member:** the app calls the same endpoints in both modes. The member
|
||||
asks the register to mail a code (the device's network passed on, as the register
|
||||
counts codes per network) and to check it, or to say whose GitHub ID it is, and opens a
|
||||
counts codes per network) and to check it, and opens a
|
||||
session of its own for that account (`issueSession`, which makes them a person there
|
||||
under the account's ID the first time). Adding a way in makes the person identified on
|
||||
the register and then on the member; a switch token signs in as the account the
|
||||
|
|
@ -238,12 +236,10 @@ Built on the three parts above, on the register (members forward every step thro
|
|||
3–16 passes, 64 MiB–1 GiB), verifier (1 < v < N, as long as N), public keys (32 bytes
|
||||
each) and both wrappings (24-byte nonce, 112 bytes). It needs an address, and a
|
||||
session alone sets none (ADR 0017's fourth reading): an account with an address proves
|
||||
it anew by a code mailed to it for this (`firstPassword`); an account that signs in
|
||||
with GitHub alone does that within 10 minutes of a GitHub sign-in on the session asking
|
||||
(`gitHubSignInFreshFor`, the session row's method and creation time, a member telling
|
||||
the register; else `signInAgain`, before any code is used), the address it adds proved
|
||||
by a mailed code (`signUp`). Anyone else, an anonymous account left from before among
|
||||
them, sets none (`wrongCredentials`, before any code is used). It signs in once an
|
||||
it anew by a code mailed to it for this (`firstPassword`). Anyone else, an account
|
||||
that signs in with SimpleLogin alone or one left from before with an anonymous account
|
||||
or a GitHub ID among them (Halkyon Learn's ADR 0020, D36), sets none
|
||||
(`wrongCredentials`, before any code is used). It signs in once an
|
||||
authenticator is confirmed; until then a new setup replaces it, never once one was,
|
||||
even while a reset leaves the account without one (a new password comes through the
|
||||
recovery phrase or the account's reset alone).
|
||||
|
|
@ -388,16 +384,11 @@ Built on the three parts above, on the register (members forward every step thro
|
|||
day). The owner's warning of a session ended for its proofs is
|
||||
dismissed only by a session made since (`signedInBefore`). A refusal for a reset not due says when it takes effect
|
||||
(`takesEffectAt`).
|
||||
- **Older ways:** an account that signs in with a password (it had a confirmed
|
||||
authenticator, which a reset may have taken since: `passwordOnly` in its ways in) no
|
||||
longer signs in with GitHub (`usePassword`); no account gets GitHub any more (D25),
|
||||
so it signs in only those who have it; from `GITHUB_SIGN_IN_ENDS` on GitHub signs nobody in
|
||||
(`ended`), the basics stop naming the day (`gitHubSignInEnds`, which apps show until
|
||||
then) and, 30 days later (`gitHubAccountsKeptAfterEnd`, so a date set wrong can be put
|
||||
right first), `forgetGitHubAccounts` deletes every GitHub ID on the register, first
|
||||
deleting the accounts that sign in with GitHub alone (no address, never a password),
|
||||
each on its own (one failing is tried again the next day; one deleted whose host step
|
||||
after failed counts as deleted), the members told once after all of them.
|
||||
- **Older ways:** GitHub signs nobody in (Halkyon Learn's ADR 0020, D36, ending ADR
|
||||
0017's D2 sooner): the module has no GitHub endpoint and reads no GitHub settings. The
|
||||
IDs left in Serverpod's `serverpod_auth_idp_github_account` table, which stays as
|
||||
Serverpod's migrations make it, go with their accounts (`ON DELETE CASCADE`); an
|
||||
operator deletes them before with the SQL of D36.
|
||||
|
||||
## Taking it into a host
|
||||
|
||||
|
|
@ -452,8 +443,7 @@ Built on the three parts above, on the register (members forward every step thro
|
|||
after any literal routes of its own.
|
||||
|
||||
Then schedule the pruning in a future call of the host's own: daily
|
||||
`pruneAnonymousPeople`, `forgetGitHubAccounts` (the register's),
|
||||
`forgetLapsedResets` and `forgetPeopleTheRegisterForgot` (a member's), hourly
|
||||
`pruneAnonymousPeople`, `forgetLapsedResets` and `forgetPeopleTheRegisterForgot` (a member's), hourly
|
||||
`forgetOldSignUpAttempts`, `forgetExpiredEmailCodes`,
|
||||
`forgetUnconfirmedAuthenticators`, `forgetUnfinishedSimpleLogins` (the
|
||||
register's), `deliverDeletionNotices` and `deliverSessionNotices` (the register's). Each marked as one mode's does nothing in
|
||||
|
|
@ -471,8 +461,7 @@ Built on the three parts above, on the register (members forward every step thro
|
|||
version, with no SQL besides. Every service has the module's tables, whichever its
|
||||
mode: a member's stay empty but for its people's sessions.
|
||||
|
||||
The passwords it reads: `serverSideSessionKeyHashPepper`; for GitHub `githubClientId` and
|
||||
`githubClientSecret`; for email `emailSignInPepper` and `emailSmtpHost`,
|
||||
The passwords it reads: `serverSideSessionKeyHashPepper`; for email `emailSignInPepper` and `emailSmtpHost`,
|
||||
`emailSmtpPort`, `emailSmtpUser`, `emailSmtpPassword`, `emailFrom`; for passwords
|
||||
`totpKey`, `recoveryCodeKey` and `signInDecoyKey` (each at least 32 characters, or
|
||||
`addInstanceRoutes` stops the server; all three and email sign-in, or no password
|
||||
|
|
@ -483,14 +472,13 @@ and without it: mending answers decoys); between the
|
|||
instance's services `instanceSecret`. Never change either pepper
|
||||
once people sign in: every session or address hash would be lost; changing `totpKey`
|
||||
voids every authenticator, `recoveryCodeKey` every recovery code. A member needs no email settings of its
|
||||
own (the register mails the codes), but GitHub's (its own OAuth app's, whose callback is
|
||||
its app's). The environment: `ACCOUNT_REGISTER_URL` and `INSTANCE_MEMBERS` (above);
|
||||
own (the register mails the codes). The environment: `ACCOUNT_REGISTER_URL` and `INSTANCE_MEMBERS` (above);
|
||||
`ACCOUNT_DOMAIN`, the domain handles are on, which can never change once anyone follows
|
||||
one; `SIMPLELOGIN_CALLBACK_URL`, the https address the SimpleLogin app registered, where
|
||||
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`); `GITHUB_SIGN_IN_ENDS`, the day GitHub stops signing anyone in (`2027-01-22`);
|
||||
name it doesn't know stopping the server (`addInstanceRoutes`);
|
||||
`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