R3 two-factor sign-in: TOTP with recovery codes, asked for only after a correct password
Build / Build (push) Successful in 36s
Build / Build (push) Successful in 36s
Completion program R3 (docs/ROADMAP.md, 2026-10-17). - Services/TwoFactor.cs: TOTP (RFC 6238, 30 s, 6 digits, SHA-1, one step of slack, Otp.NET 1.4.1) and ten recovery codes (SHA-256 kept, pulled when used). RootUser.Totp holds the secret, the last step taken (moved only by a conditional update, so a code works once even for two sign-ins at the same moment) and the codes' hashes. - /clientapi/user/totp/setup (the password) answers the secret, its otpauth:// URI and a QR code as an SVG data URI (QRCoder 1.8.0); /totp/enable takes the app's first code, shows the recovery codes once and ends every other session (this device gets a new one); /totp/disable takes the password and a code. GET /clientapi/user/account says whether it is on and how many recovery codes are left. - Sign-in (/clientapi/user/login, the invitation login, /oauth/login) asks for LoginForm.OneTimeCode with a 428, and only once the password was right; the OAuth page has the field. Recovery by email leaves two-factor on. - PrivaPub admin reset-totp <root>: the way back for a login that lost its authenticator and its codes. - scenarios/accounts.sh checks it all, with codes computed in Python; the OAuth page with its antiforgery token. Verified: dotnet build PrivaPub.sln -c Release; the pasture (gts, mailpit): accounts 45/45. 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
cff75e3a47
commit
1f3bb27e17
13 files changed
+372
-5
No files matched your search
@@ -89,7 +89,7 @@ PrivaPub/ ASP.NET Core Web API, net10.0
|
||||
Jobs/ JobQueue (leases), JobWorker, Backoff, HostCircuitBreaker
|
||||
Ids/ PrivacyIds (day-only ids for personas and groups, arrival-ordered ids for remote posts)
|
||||
Data/ Indexes (created at start), EntityMaps.Warm, Migrations/_NNN_*.cs
|
||||
Cli/ AdminCommands (`PrivaPub admin promote|demote|create-root|move-persona|delete-root`,
|
||||
Cli/ AdminCommands (`PrivaPub admin promote|demote|create-root|move-persona|delete-root|reset-totp`,
|
||||
`media audit`, `backup`, `backups`, `restore`)
|
||||
Backup/ ServerBackup, ServerRestore, ProtectiveMerge, MaintenanceLock, TransferStore
|
||||
Geo/ GeoLocator, GeoUpdater (DB-IP Lite), CdnCatalog, CdnUpdater
|
||||
@@ -185,6 +185,7 @@ echo '<password>' | ASPNETCORE_ENVIRONMENT=Production ./PrivaPub admin create-ro
|
||||
ASPNETCORE_ENVIRONMENT=Production ./PrivaPub admin promote|demote <root>
|
||||
ASPNETCORE_ENVIRONMENT=Production ./PrivaPub admin move-persona <persona> <root> # its link moves, its Mastodon tokens end
|
||||
ASPNETCORE_ENVIRONMENT=Production ./PrivaPub admin delete-root <root> # as the client deletes a login: its personas go too
|
||||
ASPNETCORE_ENVIRONMENT=Production ./PrivaPub admin reset-totp <root> # a login that lost its authenticator and codes
|
||||
```
|
||||
|
||||
The deploy runs them as `build-runner`, which owns the published files, reads `appsettings.Production.json` through
|
||||
@@ -420,6 +421,13 @@ group www-data and reaches the private mongod; `sudo -u www-data` works too.
|
||||
- Sign-up still says a login name is taken: uniqueness has to be told, and sign-up is reachable only while setup
|
||||
waits or with an invitation, rate-limited.
|
||||
- `AppConfigurationService` reads appsettings as they are now; the copy older builds saved in Mongo is not read.
|
||||
- **Two-factor sign-in** (R3, `Services/TwoFactor.cs`, `RootUser.Totp`): TOTP (30 s, 6 digits, SHA-1, one step of
|
||||
slack, Otp.NET), set up with the password (`/clientapi/user/totp/setup`: the secret, its `otpauth://` URI and a QR
|
||||
code as an SVG data URI, QRCoder), turned on by the app's first code (`/totp/enable`: ten recovery codes shown
|
||||
once, every other session ended) and off with the password and a code (`/totp/disable`). Each code is taken once
|
||||
(`LastStep`, moved by a conditional update); a recovery code is kept as its SHA-256 and pulled when used. Sign-in
|
||||
(`/clientapi/user/login`, the invitation login and `/oauth/login`) asks for `OneTimeCode` with a 428, and only after a
|
||||
correct password. Recovery by email leaves two-factor on; `PrivaPub admin reset-totp <root>` turns it off.
|
||||
- **Deleting a root deletes everything public it had** (`RootRemoval`, from the admin route or `/clientapi/user/delete`
|
||||
with the password): its sessions end, each persona and each group it owns sends `Delete{Actor}` to followers, members
|
||||
and the accounts it follows, the personas' posts are emptied, and `/peasants/{name}`, its inbox and WebFinger answer
|
||||
|
||||
Reference in new issue
Block a user