Skip to main content
Petanque Life

User Directory (cross-tenant)

F21.06 10 features

At a glance

A cross-tenant lookup and recovery surface for every human on the platform. Search by email, name, license number, phone, or user id; drill into a unified detail with sessions, devices, audit, and tickets; recover credentials; lock, unlock, soft-delete, merge duplicates; and inspect both the effective state and a per-user timeline stitched from nine source collections.

How it works

Every operator-facing user task starts here. `GET /sys/user-directory?q=…` searches across all tenants by email, name, license, phone, or user id and returns hits enriched with tenant context and role. The detail view at `sys/(dashboard)/user-directory/[id]` aggregates profile, tenant memberships, sessions, devices, audit timeline, support history, open tickets, and linked OAuth identities into a single page. Recovery actions — reset password, reset MFA, reset WebAuthn, resend verification — are POSTed individually, each requiring a reason and fresh-auth, and each producing an audit row with the reason embedded.

Lock/unlock toggles a temporary block that is shown to the user as `contact support` at login without deleting any data. Soft-delete marks the user disabled and anonymises PII; the account is recoverable for 30 days, then hard-deleted. Impersonation is a one-click button that hands off into the F21.02 consent flow rather than starting impersonation directly, so the user always sees a grant request before any session opens.

The session panel lists active sessions per device and supports per-session or all-but-current revoke. Merge consolidates two accounts that turn out to be the same human across tenants: the preview endpoint shows the diff, the operator confirms, and the merge writes a full audit trail. Two power tools sit at the end.

The effective-state inspector returns one 60-second-cached payload that aggregates identity, sessions, tenant context, capabilities, feature flags, experiment assignments, pending operations, open support grants, open tickets, billing, and anomalies; an embedded rule engine flags failed-login spikes (≥3 in 24h), logins from new IPs (30-day lookback), long inactivity (≥90d), and the locked-with-active-session edge case. The per-user timeline aggregates nine source collections (`user_sessions`, `sys_failed_login_samples`, `sys_audit_entries`, `support_access_grants`, `impersonation_sessions`, `invoices`, `notifications`, `sys_incidents`, `legal_document_acceptances`) into a chronological stream with filter chips per kind, jump-to-date, and row-level drill-in — no new event-of-record collection, just a query-time merge with paging.

Key capabilities

  • Cross-tenant search by email, name, license, phone, user id with tenant + role context
  • Unified detail view: profile, sessions, devices, audit, tickets, OAuth identities
  • Reason + fresh-auth recovery actions: reset password / MFA / WebAuthn / resend verification
  • Lock/unlock without delete; soft-delete with 30-day recovery window
  • Impersonate from detail view via F21.02 grant flow
  • Per-device session panel with individual or bulk revoke
  • Merge duplicates with preview, confirm, and full audit trail
  • Effective-state inspector with anomaly rules and Copy-as-text/JSON for tickets
  • Per-user timeline stitched at query time from nine collections, paginated

In practice

A support operator picks up a ticket from a player who cannot log in. She searches by email, opens the detail view, and the effective-state inspector flags a failed-login spike: nine attempts in two hours from a new IP. She clicks `Copy as text`, pastes the snapshot into the ticket, and asks the player to confirm the IP.

The player confirms it is not theirs. She clicks `Reset MFA`, enters reason `suspected credential stuffing`, completes the fresh-auth passkey, and revokes all sessions but the most recent verified device. She then opens the timeline view, filters to `sys_failed_login_samples`, and forwards the IP to security operations.

Features in this subsystem

10
ID Status Features
F21.06.01 Shipped Search all users across all tenants by email, name, license number, phone, user id. Results include tenant context + role. ✅ PL-T126
F21.06.02 Shipped User detail view — profile, tenant memberships, sessions, devices, audit timeline, support history, open tickets, linked OAuth identities. UI: sys/app/(dashboard)/user-directory/[id]/index.tsx. API: GET /sys/user-directory/{user_id}. Implemented (PL-T126)
F21.06.03 Shipped Account recovery — reset password, reset MFA, reset WebAuthn, resend verification email. Each requires reason + fresh-auth. UI: user-directory/[id]/recovery.tsx. API: POST /sys/user-directory/{user_id}/{reset-password,reset-mfa,reset-webauthn,resend-verification}. Implemented (PL-T126)
F21.06.04 Shipped Lock/unlock account — temporary block without delete. Shown to user at login as "contact support". API: POST /sys/user-directory/{user_id}/{lock,unlock}. Implemented (PL-T126)
F21.06.05 Shipped Soft-delete — marks user disabled + anonymized PII. Recoverable 30 d. Hard delete after grace period. API: POST /sys/user-directory/{user_id}/{soft-delete,restore}. Implemented (PL-T126)
F21.06.06 Shipped Impersonate from detail view — inherits the reason prompt + audit from F21.02. Grant-flow-knapp i user-directory/[id]/index.tsx triggar /support-access/grants + F21.02-flödet. Implemented (PL-T126 + PL-T122)
F21.06.07 Shipped Session panel — list active sessions for a user, per-device. Revoke individually or all-but-current. UI: user-directory/[id]/sessions.tsx. API: GET /sys/user-directory/{user_id}/sessions, DELETE /sys/user-directory/{user_id}/sessions[/{session_id}]. Implemented (PL-T126)
F21.06.08 Shipped Merge duplicate users — consolidate two accounts (same person across tenants) into a single identity with linked tenant profiles. Audit-trail of the merge. UI: user-directory/merge.tsx. API: POST /sys/user-directory/merge/preview, POST /sys/user-directory/merge/{operation_id}/{confirm,cancel}. Implemented (PL-T126)
F21.06.09 Shipped Effective-state inspector — GET /sys/users/{id}/effective-state?tenant_id=… aggregates identity + sessions + tenant context + capabilities + feature flags + experiment assignments + pending ops + open support-grants + open tickets + billing + anomalies in a single 60 s-cached payload. Sys-UI sys/app/(dashboard)/user-directory/[id]/effective-state.tsx renders a three-column layout with a red-bordered anomaly banner and "Copy as text / JSON" for ticket replies. sys_support / sys_security; every read audited as sys.user.effective-state.view. Anomaly rule engine detects failed-login spikes (≥3/24 h), logins from new IPs (30 d lookback), long inactivity (≥90 d), and locked-with-active-session. ✅ PL-T145
F21.06.10 Shipped Per-user timeline — GET /sys/users/{id}/timeline?from&to&kinds&limit&offset aggregates a kronologisk stream from nine source collections (user_sessions, sys_failed_login_samples, sys_audit_entries, support_access_grants, impersonation_sessions, invoices, notifications, sys_incidents, legal_document_acceptances) — no new event-of-record collection. Sys-UI sys/app/(dashboard)/user-directory/[id]/timeline.tsx with filter chips per kind, jump-to-date picker, and row-level drill-in to audit / incident / invoice / grant detail. Paginated (TIMELINE_MAX_PAGE=500, default 100). sys.user.timeline.view audited. ✅ PL-T145