R2 accounts in decePub: forgotten password, email by code, sessions, the theme kept with the login
Completion program R2, with PrivaPub's routes of the same release.
- Pages/PasswordRecovery.razor (/password-recovery): asks for a login or an address and shows the server's one
sentence; with ?rc= it takes the code out of the address bar at once, checks it and sets a new password. Login links
there ("Forgot your password?").
- Settings, the login: its email address with a verified badge, changed with the password and the code emailed to it
(DOtp), removed with the password; "Sign out everywhere" (every device and persona, asked first).
- A password change takes the new JWT PrivaPub answers and exchanges the persona's token again, since every persona
token ended with it.
- The theme kept with the login (PageSettings.SyncWithLogin, Settings > General): taken at sign-in and start-up once a
client saved it, saved 1.5 s after a change here, never posted back when nothing changed, never the language.
- Login, Setup and Join start the session through CascadingState.SignedIn, which takes the login's theme.
- 38 keys in both resx files; CLAUDE.md follows.
Verified: dotnet build decePubClient.csproj -c Release (0 errors, no new warnings).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpVCoDeM1qUTRp4fXQdfof
This commit is contained in:
1 parent
fd2f2e7c65
commit
7d4b35f0d7
15 files changed
+1056
-17
No files matched your search
@@ -101,7 +101,14 @@ Ionicons 4.5 (`wwwroot/vendor/ionicons.min.css`, `<i class="ion-md-...">`) is lo
|
||||
- picking a hue leaves grey.
|
||||
- `AppStatusService.IsDarkTheme()` tells which variant is showing.
|
||||
- Settings → General edits every theme setting. NavMenu's toggle and slider are shortcuts to the dark and hue ones.
|
||||
The other sections of Settings belong to the persona (see **Settings** under PrivaPub wiring).
|
||||
The other sections of Settings belong to the login or the persona (see **Settings** under PrivaPub wiring).
|
||||
- **The theme is the login's** while `PageSettings.SyncWithLogin` is on (the default; Settings → General turns it off
|
||||
for this device). `CascadingState` takes the login's theme (`ViewUserSettings`; never its language, which would reload
|
||||
the app) at sign-in from the `JwtUser` (`SignedIn`, which Login, Setup and Join call instead of `LoadSession`) and at
|
||||
start-up from `/clientapi/user/settings`, once `savedAt` says a client saved one; while none did, it gives the login
|
||||
this device's. `UpdatePageSettings` saves them to `/clientapi/user/update/settings` 1.5 s after the last change, one
|
||||
save waiting at a time, only when they differ from what the login is known to keep (so taking them never sends them
|
||||
back), with the language as two letters and the icons' hue at 25; a failure is logged, never shown.
|
||||
- `Styles/neo.css` holds the neumorphic layer:
|
||||
- `neo-raised` / `neo-inset` / `neo-flat`, with sizes `neo-2xs` / `neo-xs` / `neo-sm` / `neo-md` (these replace the
|
||||
old `neomorph is-nxsmall` classes);
|
||||
@@ -232,8 +239,8 @@ Never redeclare the base's members in a component.
|
||||
`SUtility.DefaultSerializer` (camelCase, enums by name like PrivaPub, `ClientJsonContext`) for `clientapi/`. A payload
|
||||
can be an object (JSON), name/value pairs (a form) or a `Func<HttpContent>` (multipart), built once per attempt.
|
||||
- Auth levels: `Get` / `Post` / `Put` / `Patch` / `Delete` need credentials and end the session when PrivaPub refuses
|
||||
them; `GetAnon` adds them when there are some; `*TotallyAnon` send none (the login must not carry a JWT:
|
||||
PrivaPub redirects it).
|
||||
them; `GetAnon` adds them when there are some; `*TotallyAnon` send none (the login and the password recovery must
|
||||
not carry a JWT: PrivaPub redirects them).
|
||||
- `retryOnError` backoff: 1, 2, 4, 8, 16, 29 s, then 60 s, for at most 10 minutes.
|
||||
- Each request has its own timeout (`Measure`): 100 s, or 10 minutes for a multipart body (an upload); the
|
||||
`HttpClient`'s own is off.
|
||||
@@ -250,7 +257,13 @@ Never redeclare the base's members in a component.
|
||||
- **One sign-in** (`Services/AuthService.cs`, `Pages/Login.razor`): the root signs in on `/clientapi/user/login`, then
|
||||
each persona the client uses gets its own Mastodon token in exchange for the JWT (PrivaPub's `PersonaExchange`,
|
||||
RFC 8693 on `/oauth/token`, first-party client id `AppConfiguration:ClientId`). `AuthData` keeps the JWT and the
|
||||
persona tokens; logout revokes every persona token, then forgets the session.
|
||||
persona tokens; logout revokes every persona token, ends this device's session (`/clientapi/user/logout`), then
|
||||
forgets the session.
|
||||
- **Sessions:** "Sign out everywhere" (`AuthService.LogoutEverywhere`, `/clientapi/user/logout/everywhere`) ends every
|
||||
session of the login, this one too, and PrivaPub revokes every persona's token with them, so the client only forgets
|
||||
its own and signs out. A password change ends every other session and revokes every persona token, this device's
|
||||
too: `AuthService.ChangePassword` keeps the new `JwtUser` PrivaPub answers with, drops the stored persona tokens and
|
||||
exchanges one for the current persona at once (the others when next used), so the device stays signed in.
|
||||
- **The ways in** (sign-up is closed on PrivaPub; owner decision 2026-10-16):
|
||||
- **Setup** (`Pages/Setup.razor` at `/setup`): while PrivaPub has no administrator (`/clientapi/user/setup`), the first
|
||||
login is made there, first come, first served, with its first persona. It becomes the administrator, and setup
|
||||
@@ -259,6 +272,13 @@ Never redeclare the base's members in a component.
|
||||
the group (`/clientapi/user/invitation/signup`). Login links there, and a group's page shows the link with its code.
|
||||
- Both sign in at once like Login (`AuthService.SetUp` / `Join`, sent with no credentials). A login without a
|
||||
persona is sent from Login to Settings to make one.
|
||||
- **A forgotten password** (`Pages/PasswordRecovery.razor` at `/password-recovery`, linked from Login): the login's
|
||||
name or its address asks for a link (`/clientapi/user/recover/password`), and the page shows PrivaPub's answer, the
|
||||
same sentence for everyone, so it never tells whether a login or an address exists. PrivaPub mails only a verified
|
||||
address. The link comes back as `/password-recovery?rc=`: the page keeps the code, takes it out of the address bar
|
||||
and the history at once (`replace`), checks it (`/recover/valid`, the code as a JSON string), and sets the new
|
||||
password with it (`/recover/update/password`), which ends every session of the login; then the reader signs in.
|
||||
All of it goes with no credentials.
|
||||
- **Services** (`Services/`): `TimelineService` (every feed as `FeedPage`s of `FeedPost`s, paged by `max_id`/`min_id`;
|
||||
circles are read as ids from `/clientapi` and hydrated through `/api/v1/statuses?id[]=`; located posts come from
|
||||
`/clientapi/post/nearby` and have no Mastodon actions), `StatusService` (publish, favourite, boost, bookmark, delete,
|
||||
@@ -338,9 +358,13 @@ Never redeclare the base's members in a component.
|
||||
- **Interaction policies** (GoToSocial's, and FEP-5624's `canReply` on a PeerTube video whose comments are moderated): a
|
||||
reply or boost whose target's author must approve it shows so on its card (`privapub.approval`: pending, then nothing
|
||||
once approved, or rejected); one the policy shuts out is refused by PrivaPub (422) and shown as an error.
|
||||
- **Settings** (`Pages/Settings.razor`, `Components/Settings/`): the login itself (`AccountEditor`: its password changed,
|
||||
or the whole of it deleted, every persona with it, through `/clientapi/user/update/password` and `/clientapi/user/delete`;
|
||||
the client then signs out), the persona's profile (`ProfileEditor`:
|
||||
- **Settings** (`Pages/Settings.razor`, `Components/Settings/`): the login itself (`AccountEditor`: its email address
|
||||
(`/clientapi/user/account`), set only with the password and a 6-digit code mailed to the new address (`/user/email`,
|
||||
then `/email/verify` through `DOtp`; the old `/user/update` is refused), shown verified or not and with an address
|
||||
waiting for its code, and removed with the password (`/email/remove`); a wrong password is PrivaPub's 403, read as
|
||||
such; its password changed; every session of it ended ("Sign out everywhere", see **One sign-in**); or the whole of it
|
||||
deleted, every persona with it, through `/clientapi/user/update/password` and `/clientapi/user/delete`; the client then
|
||||
signs out), the persona's profile (`ProfileEditor`:
|
||||
`update_credentials` as multipart, its words as written from `Account.Source`, pictures (streamed from the file,
|
||||
and removed with `DELETE /api/v1/profile/avatar|header`), fields, locked, bot,
|
||||
discoverable, indexable and posting defaults; the composer starts a new post at its default visibility), its mutes,
|
||||
|
||||
Reference in new issue
Block a user