Operator sign in
Enter the dashboard access token. It is exchanged for a private, short-lived session and is never stored on this device.
-diff --git a/README.md b/README.md index 3d6afd4..90aec5c 100644 --- a/README.md +++ b/README.md @@ -92,17 +92,22 @@ that setting, a reverse proxy is safely treated as one shared source. Each signed cookie includes an opaque session identifier whose hash and expiry are kept in the SQLite session registry. Keep that registry on persistent, writable -storage shared by all dashboard workers. If an active session expires or is revoked, -the first authenticated API rejection replaces the dashboard with sign-in and explains -that private drafts remain on the device; signing in again resumes account-bound queued -delivery. This recovery does not clear offline state. **Sign out & clear this device** revokes -only the current session before clearing browser state, so a copied cookie cannot -be replayed afterward; other signed-in devices remain active. **Sign out all -devices** is a separately confirmed lost-device safety action that atomically -revokes every existing operator session before clearing the current browser and -returning to sign-in. Deploying this version invalidates older cookies that do not -contain a registered identifier, so operators must sign in once again. Registry -read or write failures return a sanitized HTTP 503 before Gitea is contacted. +storage shared by all dashboard workers. Operators name a device at sign-in and can +open **Active devices** to review creation/expiry times, identify the current device, +and revoke one remote session without interrupting other trusted devices. The API +exposes only independent management IDs and bounded labels—never cookie values, +session hashes, CSRF proofs, or source addresses. Existing two-column registries are +migrated in place and their live sessions remain valid. + +If an active session expires or is revoked, the first authenticated API rejection +replaces the dashboard with sign-in and explains that private drafts remain on the +device; signing in again resumes account-bound queued delivery. This recovery does +not clear offline state. **Sign out & clear this device** revokes only the current +session before clearing browser state, so a copied cookie cannot be replayed +afterward; other signed-in devices remain active. **Sign out all devices** is a +separately confirmed lost-device safety action that atomically revokes every existing +operator session before clearing the current browser and returning to sign-in. +Registry read or write failures return a sanitized HTTP 503 before Gitea is contacted. Terminate TLS at the trusted reverse proxy: session cookies are deliberately `Secure`, `HttpOnly`, `SameSite=Strict`, and scoped to the deployment subpath. diff --git a/frontend/dashboard.css b/frontend/dashboard.css index 315f17a..3991860 100644 --- a/frontend/dashboard.css +++ b/frontend/dashboard.css @@ -11,6 +11,16 @@ header { position: sticky; top: 0; z-index: 20; padding: 12px 16px; display:flex .app-menu-panel { display:flex; gap:10px; align-items:center; flex-wrap:wrap; } button { background: linear-gradient(180deg,#1f3a5f,#15324d); border:1px solid #2a496e; color:#e5e7eb; padding:8px 12px; border-radius:10px; cursor:pointer; } #sign-out-all { min-height:44px; } +.active-devices-sheet { position:fixed; inset:0; z-index:80; display:flex; justify-content:flex-end; background:rgba(5,12,21,.72); backdrop-filter:blur(4px); } +.active-devices-sheet[hidden] { display:none; } +.active-devices-panel { box-sizing:border-box; width:min(560px,100%); height:100%; overflow:auto; padding:18px; padding-bottom:calc(18px + env(safe-area-inset-bottom)); background:#0b1526; border-left:1px solid #2a496e; } +.active-devices-header { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; } +.active-devices-header h2, .active-devices-header p { margin-top:0; } +.active-devices-header button, .active-device button { min-height:44px; } +.active-devices-list { display:grid; gap:10px; margin-top:16px; } +.active-device { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:8px; align-items:center; padding:12px; border:1px solid #2a496e; border-radius:12px; background:#0f2237; } +.active-device strong, .active-device span { display:block; overflow-wrap:anywhere; } +.active-device-current { color:#55d6be; font-weight:700; } button:hover { filter: brightness(1.15); } .panel { border: 1px solid #1b2d45; border-radius: 14px; padding: 12px; background: rgba(11,21,38,.92); } .panel > summary { cursor: pointer; list-style-position: inside; } diff --git a/frontend/index.html b/frontend/index.html index 8e02503..fc0f420 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -18,6 +18,7 @@
+Review signed-in devices and remove access you no longer trust.
Enter the dashboard access token. It is exchanged for a private, short-lived session and is never stored on this device.
-