Add an Account page for sign-in methods and signed-in devices
/web/account, offered to everyone, lists every browser and client signed in as you, with when each was last used, and signs any of them out, or all but the current one. It sets up, replaces and removes the second factor, and holds the password form, moved from the vault page. Administrators can sign a user out everywhere from the People page. The API gains GET and DELETE /api/v1/auth/sessions and DELETE /api/v1/auth/totp. Sessions are named by a digest of their token, never the token, and record when they were last used. Replacing or removing an active second factor now needs the password and a current code. Server version 0.10.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
318bd68908
commit
f8f634fbf7
+33
-5
@@ -436,11 +436,12 @@ browsing, by design.
|
||||
|
||||
| Page | What it does |
|
||||
| --- | --- |
|
||||
| `/web/vaults` | the vaults you can reach, the create-a-vault form, your own account password |
|
||||
| `/web/vaults` | the vaults you can reach, the create-a-vault form |
|
||||
| `/web/account` | everyone's own settings: account details, every signed-in browser and client with a button to sign each out (or all but this one), two-factor setup, replacement and removal, the password |
|
||||
| `/web/vaults/{id}/delete` | what deleting a vault destroys, and the password that confirms it |
|
||||
| `/web/groups` | the group tree you can see, and a form to add a group inside one you administer |
|
||||
| `/web/groups/{id}` | one group: members and their roles, the vaults it reaches and where that access comes from, grants given and revoked, deletion |
|
||||
| `/web/users` | the accounts you may see; for a server administrator, disable, enable, clear the second factor, promote, demote, delete |
|
||||
| `/web/users` | the accounts you may see; for a server administrator, disable, enable, clear the second factor, sign out everywhere, promote, demote, delete |
|
||||
| `/web/invites` | outstanding invite links with their use counts and expiry, a form to make one, a button to revoke one |
|
||||
|
||||
The navigation offers the last three only to somebody who administers a
|
||||
@@ -478,6 +479,29 @@ No page carries any JavaScript. Each control is its own small form with the
|
||||
CSRF token and one action in it, which is what makes the interface work in a
|
||||
locked-down browser and keeps its attack surface down to that token.
|
||||
|
||||
**The account page.** `/web/account`, offered to everyone, is where a
|
||||
person manages how they sign in. The rules are in `account.go`, shared with
|
||||
the `/api/v1/auth/sessions` and `/api/v1/auth/totp` endpoints:
|
||||
|
||||
- *Signed-in devices* lists every live session — browsers, named from their
|
||||
User-Agent at sign-in ("Firefox on macOS"), and clients, named by the
|
||||
label they sent — with when each was last used. Any of them can be signed
|
||||
out, or all but the current one. Pages and responses refer to a session by
|
||||
`sessionID`, a digest of its token, so the list never carries a token.
|
||||
`LastSeenAt` is refreshed by `markSeen` at most every five minutes, because
|
||||
each refresh rewrites the state document.
|
||||
- *Two-factor authentication* turns on with nothing more than the session,
|
||||
since it only adds a check. Replacing or removing an active factor asks
|
||||
for the password and a current code: before this, `totp/enroll` reset the
|
||||
factor on any live session, so a borrowed laptop could quietly disarm it.
|
||||
- *Password* moved here from the vault page; changing it still signs out the
|
||||
other sessions. An account that signs in only through single sign-on sees
|
||||
neither form, and is told that its provider manages both.
|
||||
|
||||
A server administrator also has *Sign out everywhere* on `/web/users`, for a
|
||||
colleague's lost phone: every session of that account goes, and the account
|
||||
stays usable.
|
||||
|
||||
**Deleting a vault asks for the password again.** Every other control on
|
||||
these pages is undoable or additive; this one erases every stored version of
|
||||
a file. A live session is what an unlocked laptop leaves lying around, so the
|
||||
@@ -874,9 +898,13 @@ what an arm64 Linux build host needs.
|
||||
`targetSdk` is its own change.
|
||||
- **No key-file or Yubikey handling**, and none needed: those live in the
|
||||
KeePass application.
|
||||
- **Sessions are not listable or revocable individually** beyond
|
||||
`keeporg logout` on the device itself. Adding `GET`/`DELETE
|
||||
/api/v1/auth/sessions` is a small, well-bounded job.
|
||||
- **No QR code for two-factor setup.** The account page shows the key and
|
||||
an `otpauth:` link, which a phone opens straight into its authenticator;
|
||||
scanning from a desktop screen needs a QR encoder, which the standard
|
||||
library does not have.
|
||||
- **The CLI has no `sessions` command yet.** The API is there
|
||||
(`/api/v1/auth/sessions`); the web page is the way to see and revoke
|
||||
devices meanwhile.
|
||||
- **Android does not restart its watcher at boot.** Add a
|
||||
`RECEIVE_BOOT_COMPLETED` receiver if that matters more than the permission
|
||||
it costs.
|
||||
|
||||
+11
-1
@@ -21,13 +21,23 @@ https, and every form carries a per-session CSRF token.
|
||||
| POST | `/api/v1/auth/logout` | – | revokes the calling token |
|
||||
| GET | `/api/v1/auth/whoami` | – | the account and its group memberships |
|
||||
| POST | `/api/v1/auth/password` | `{current_password, new_password, totp}` | changes the account password; the current one is required, and the second factor when enrolled. Every other session of that account is revoked |
|
||||
| POST | `/api/v1/auth/totp/enroll` | – | `{secret, uri}`; not active until confirmed |
|
||||
| POST | `/api/v1/auth/totp/enroll` | `{password, totp}`, only when a factor is already on | `{secret, uri}`; not active until confirmed. Replacing an active factor without the password and current code is `403 reauth_required` |
|
||||
| POST | `/api/v1/auth/totp/confirm` | `{code}` | `{totp_enabled: true}` |
|
||||
| DELETE | `/api/v1/auth/totp` | `{password, totp}` | `{totp_enabled: false}`; `403 reauth_required`, `403 bad_credentials`, `403 totp_invalid` |
|
||||
| GET | `/api/v1/auth/sessions` | – | `{sessions: [{id, label, browser, current, created_at, last_seen_at, expires_at}]}`, the caller's own |
|
||||
| DELETE | `/api/v1/auth/sessions/{id}` | – | signs that session out; `404` when it is not the caller's |
|
||||
| DELETE | `/api/v1/auth/sessions` | – | `{revoked: n}`: every session of the caller but this one |
|
||||
| POST | `/api/v1/auth/device/start` | `{label, return_url}` | `{device_code, user_code, verification_url, interval, expires_in}`; `400` when `return_url` is not a `keeporg:` URL |
|
||||
| POST | `/api/v1/auth/device/poll` | `{device_code}` | `202 {status: "pending"}`, then `200 {token, ...}` once; `410 expired` |
|
||||
|
||||
A device code is single use: the first successful poll consumes it.
|
||||
|
||||
A session `id` is a one-way digest of its token, never the token itself, so
|
||||
a list of sessions cannot be used to sign in as any of them. Failed
|
||||
re-authentication answers `403`, not `401`, so a client that drops its
|
||||
token on `401` does not sign itself out over a mistyped password.
|
||||
`last_seen_at` is refreshed at most every five minutes.
|
||||
|
||||
`return_url` is optional (server 0.9 and later). When it is given, the page
|
||||
that ends the approval, after a password or single sign-on, carries a
|
||||
"Return to KeepOrg" link to it. Only the `keeporg:` scheme is accepted, so
|
||||
|
||||
Reference in New Issue
Block a user