feat(dpop): client now aware if session if bound
this prevent client to try binding and creating - unnecessary call - unnecessary warning in server log
This commit is contained in:
@@ -256,6 +256,20 @@ export interface User {
|
||||
* card).
|
||||
*/
|
||||
has_password?: boolean;
|
||||
/**
|
||||
* TRUE when the caller's current session is DPoP-bound (row's
|
||||
* `dpop_jkt IS NOT NULL`). Populated only by `/api/auth/me`; other
|
||||
* User-emitting endpoints leave it unset.
|
||||
*
|
||||
* The session store reads this to skip a redundant
|
||||
* `POST /api/auth/dpop/bind` call — the endpoint returns 409
|
||||
* `already_bound` on repeated attempts (anti-downgrade invariant)
|
||||
* and each rejection logs at audit INFO, so a naive "bind on
|
||||
* every load" pattern was cluttering the audit stream. We only
|
||||
* fire bind now when there's actual work to do (fresh OIDC /
|
||||
* magic-link session that landed unbound).
|
||||
*/
|
||||
is_dpop_bound?: boolean;
|
||||
}
|
||||
|
||||
/** Fields rendered by the paginated admin table. Full account details remain
|
||||
|
||||
@@ -50,12 +50,14 @@ class SessionStore {
|
||||
// Post-redirect DPoP bind — catches OIDC / magic-link
|
||||
// flows whose server-side callback creates the session
|
||||
// UNBOUND (no way for the redirect to carry the JKT in
|
||||
// the callback body). One-shot per SPA lifetime because
|
||||
// `this.loaded` guard makes `load()` a singleton;
|
||||
// server returns 409 if the session is already bound
|
||||
// (harmless — result is swallowed). Fire-and-forget so
|
||||
// a slow IndexedDB open doesn't stall the app boot.
|
||||
void bindDpopIfPossible();
|
||||
// the callback body). Gate on `is_dpop_bound` so we
|
||||
// don't call the endpoint on every SPA load: password
|
||||
// login already binds at session-mint time, so `/me`
|
||||
// reports `true` on the very first request and skip
|
||||
// avoids the 409 `already_bound` reject that would
|
||||
// otherwise clutter the audit stream. Fire-and-forget
|
||||
// so a slow IndexedDB open doesn't stall app boot.
|
||||
if (me.is_dpop_bound === false) void bindDpopIfPossible();
|
||||
} else this.user = null;
|
||||
} catch {
|
||||
this.user = null;
|
||||
|
||||
Reference in New Issue
Block a user