Repository navigation
Checklist identity auth
github-actions[bot] edited this page Nov 12, 2026
·
1 revision
Generated by
pnpm gen:checklist-statusfromdocs/qa/platform-checklist/areas/*.json. ⛔ Do not edit this page — edits go to the area JSON and this page follows on the next run. This page isChecklist-identity-auth.
Auth, login, identity
35 active · 0 planned
| id | title | priority | status | personas |
|---|---|---|---|---|
identity-auth.sso-enforced-first-paint |
ssoEnforced login honors SSO on first paint — no password-wall flash | P2 | active | anonymous visitor at the login page, env owner (break-glass password holder) |
identity-auth.phone-signin-surfaces |
A phone-based user's number shows across the identity surfaces, and the phone capability is advertised honestly | P2 | active | admin |
identity-auth.auth-method-matrix |
Every supported auth method signs in when enabled, is absent when disabled, and is advertised exactly as configured | P1 | active | anonymous visitor, provisioned user per method, admin (for env configuration) |
identity-auth.invitation-scope-gates |
Invitation issuance honors role-scope gates: delegated_admin can invite members but cannot mint admins; a plain member cannot invite at all | P1 | active | delegated_admin org member, plain member, tenant admin (setup only) |
identity-auth.admin-lifecycle-operations |
Admin user-lifecycle operations (ban/unban, set-password, impersonate, create/set-role/remove, revoke-sessions) enforce, persist, and stay closed to non-admins | P1 | active | platform admin, target user, non-admin forger |
identity-auth.session-list-revoke |
Active-session list is owner-scoped, and each revoke primitive terminates exactly the sessions it names | P1 | active | user A (two devices), user B (unrelated, second live session), platform admin |
identity-auth.api-key-ui-lifecycle |
Personal API key: minted show-once on Connect-an-Agent, authenticates as its owner, revoke kills it and restore brings it back — mine-view scoped | P1 | active | a signed-in member (key owner), platform admin, the key itself (machine caller via x-api-key) |
identity-auth.self-service-profile-password |
Self-service profile: name + avatar edits persist through real storage, and password change verifies the current password (passwordless users get set-initial) | P1 | active | a signed-in email+password user, a signed-in passwordless (SSO-only) user |
identity-auth.org-membership-team-management |
Setup Organization page resolves the active org and drives member/invitation/team management through the better-auth org endpoints — non-admins refused server-side | P1 | active | tenant admin / org owner, a target org member, a non-admin org member (forger) |
identity-auth.teams-bu-membership |
Teams and the Business Unit tree: memberships land real rows, and a BU placement widens/narrows a scoped persona's read along the tree | P2 | active | org admin, two members to place on a team, a scope-limited persona whose read follows the BU tree |
identity-auth.self-service-password-reset |
Forgot-password: request → token captured at the dev mail transport → reset; old password refused, expired/reused token refused loudly | P1 | active | anonymous visitor (holds no session), the account owner (before and after reset) |
identity-auth.oauth-app-consent-loop |
OAuth provider: register an app (secret shown once), run the authorization-code consent loop — approve mints tokens + a consent record, deny mints none | P2 | active | an org admin registering the OAuth client, the resource-owner user granting/denying consent |
identity-auth.linked-accounts-social |
Linked accounts: link a social/OIDC identity through the redirect round-trip → a sys_account row appears in mine-view; unlink removes it; provider-less boot degrades honestly | P2 | active | a signed-in user linking a second identity, the same user after unlinking |
identity-auth.identity-import-wizard |
Admin CSV identity import: password-policy auto/temporary drive per-row credentials, imported users sign in, non-admins denied | P2 | active | platform admin (running the import), an imported user (signing in afterwards), a non-admin (forger) |
identity-auth.two-factor-enrollment-reveal |
TOTP enrollment reveal: /two-factor/enable returns a parseable otpauth:// URI + backup codes, the declared reveal dialog's paths resolve against that exact shape, and the live re-reveal endpoint's behavior is recorded | P1 | active | a signed-in user with a known password enrolling their own second factor |
identity-auth.two-factor-verify-to-activate |
Enrollment is inert until one correct TOTP verifies on the session lane — and the run records what sys_two_factor.verified actually carries in between | P1 | active | a signed-in user mid-enrollment (session lane), the same user at the sign-in challenge (cookie lane) |
identity-auth.two-factor-backup-codes |
Backup codes: revealed once at enable, spendable exactly once at the sign-in challenge, and regeneration kills the prior set — and the navigable regenerate surface reveals the new set in a one-shot dialog | P1 | active | a user with a VERIFIED enrollment (run identity-auth.two-factor-verify-to-activate's path first), the same user at the sign-in challenge spending a code |
identity-auth.two-factor-disable-lifecycle |
Disable tears the factor down completely — row gone, flag off, next sign-in unchallenged, stale backup codes dead — is password-gated server-side, and under mfa_required re-gates through the grace-clock machinery | P1 | active | a user with an ACTIVE (verified) enrollment disabling it, the same user post-disable at a fresh sign-in, for the mfa_required clause: the same user under an enforced-MFA boot |
identity-auth.first-run-owner-bootstrap |
Zero-user first-run: the /setup owner wizard serves even under OS_DISABLE_SIGNUP, creates the owner + names the personal org through a full-document exit, and every bootstrap bypass closes the moment an owner exists | P1 | active | anonymous visitor on a zero-user boot (the would-be owner), the created owner (post-bootstrap), a second anonymous visitor (post-bootstrap forger) |
identity-auth.self-signup-gate |
Self-signup gate: the default audience posture (invite_only) refuses at the server with a LOUD code, a pending invitation carves through, and OS_DISABLE_SIGNUP bounces the UI AND the endpoint — the console offers the register form only to a visitor the server would admit (an invitation redirect, or a deployment with no owner) and explains the by-invitation rule to everyone else; the form is never the gate | P1 | active | anonymous uninvited registrant, an invited email's registrant, org admin (mints the invitation), env owner (sets OS_DISABLE_SIGNUP) |
identity-auth.email-verification-loop |
Email verification loop: a challenged sign-up lands a captured verification mail, unverified sign-in is refused EMAIL_NOT_VERIFIED server-side, the link admits, resend works — and the default boot never challenges | P2 | active | a fresh registrant (unverified, then verified), admin (reads state), env owner (configures the verification boot) |
identity-auth.workspace-org-switch |
Workspace switcher: org chrome follows posture × membership count (nothing under single; read-only indicator at one membership; switcher at two), and a switch is a full-document reload after which every tenant-scoped read answers from the new org | P2 | active | a member with one org membership, the same member after gaining a second membership, admin, the walled boot's platform operator (OS_PLATFORM_OWNER_EMAIL): mints the member with POST /api/v1/auth/admin/create-user (RUNNER.md 'A walled boot is a fixture of its own'). On a walled boot Create User joins the administrator's CURRENT organization (ADR-0093 D3 amendment) and the admin starts with none, so first create an organization (POST /api/v1/auth/organization/create) and make it active (POST /api/v1/auth/organization/set-active); without it create-user answers 400 NO_ACTIVE_ORGANIZATION and mints nobody. The member lands as member of that organization, its one membership. The admin does not provision the second org: the member creates that itself (fixtures.requires[1]) |
identity-auth.implicit-account-linking-ownership |
Implicit account linking on external sign-in: no implicit link to an unverified local user; after an unlink, implicit sign-in does not re-link; an explicit, signed-in link still works and lifts the refusal; the platform identity provider's exception holds only on its OAuth path | P1 | active | anonymous: the external sign-in caller, before it is anyone, local user U, signed up with email + password and NOT verified. A stock boot never challenges a sign-up, so a fresh sign-up is unverified, the same user U after verifying their email (the verified-row case), U signed in, linking a provider explicitly from account settings, seeded admin: reads sys_account and sys_user to confirm what was and was not written |
identity-auth.auth-gate-held-session-raw-host-families |
A session held by the ADR-0069 credential policy (password expired, forced reset, MFA not enrolled) is refused with the gate's own 403 code on EVERY raw-host route family, and an API key whose owner holds an account-scope posture is refused too | P1 | active | seeded admin (admin@objectos.ai / admin123) — the Default Organization owner and platform admin on a fresh boot, org member — created by the admin with POST /api/v1/auth/admin/create-user (stock showcase self-registration is closed: audience posture invite_only answers sign-up 403 SELF_REGISTRATION_CLOSED); joins the Default Organization as member, not platform admin, and lands mustChangePassword:true, i.e. a HELD session until it changes its password (POST /api/v1/auth/change-password) |
identity-auth.api-key-expiry-lifecycle |
API-key lifecycle policy: a mint with no expiry gets the default (90 days) echoed back, an expiry past the ceiling is refused 400 API_KEY_EXPIRY_TOO_LONG, and an expired / inactive / revoked key is refused with its own 403 code | P1 | active | seeded admin (admin@objectos.ai / admin123) — the Default Organization owner and platform admin on a fresh boot |
identity-auth.cookie-session-renewal-in-process |
A cookie session past its updateAge is renewed ONLY by GET /auth/get-session: in-process reads (data, meta, me/permissions, SSO admin) leave sys_session.expires_at unchanged and set no cookie, while bearer-only calls still renew | P2 | active | seeded admin (admin@objectos.ai / admin123) — the Default Organization owner and platform admin on a fresh boot |
identity-auth.default-organization-boot-invariant |
ADR-0131 single posture: the Default Organization exists from boot BEFORE any sign-up — the first sign-up becomes its owner, later sign-ups its members, every session carries it as active org, and seeded org-columned rows carry it instead of NULL | P0 | active | seeded admin (admin@objectos.ai / admin123) — the Default Organization owner and platform admin on a fresh boot, org member — created by the admin with POST /api/v1/auth/admin/create-user (stock showcase self-registration is closed: audience posture invite_only answers sign-up 403 SELF_REGISTRATION_CLOSED); joins the Default Organization as member, not platform admin, and lands mustChangePassword:true, i.e. a HELD session until it changes its password (POST /api/v1/auth/change-password) |
identity-auth.import-invitation-and-sso-domain-answers |
Identity import sends the dedicated account-invitation mail whose link opens the console set-password page, and the SSO domain-verification doors answer 404 for an unknown provider when enabled and 400 DOMAIN_VERIFICATION_DISABLED when off | P2 | active | seeded admin (admin@objectos.ai / admin123) — the Default Organization owner and platform admin on a fresh boot |
identity-auth.idp-group-permission-set-mapping |
A platform administrator maps an identity-provider group to a permission set: a SCIM group push grants the set to the group's members at once, an SSO sign-in grants it from the groups claim, deleting the mapping removes only the grants it made, and a set outside the organization boundary is refused | P1 | active | platform administrator: the mapping doors, an organization administrator: the 403 leg, an identity provider: the SCIM pushes |
identity-auth.sso-provider-groups-claim |
The SSO Providers record shows and edits the groups claim: get-groups-claim answers only the provider id and its claim, set-groups-claim stores it (blank clears it) and changes nothing else in the provider's mapping | P2 | active | platform administrator, an organization administrator: the 403 leg |
identity-auth.ai-service-account-lifecycle |
An AI service account is suspended, resumed, re-owned, retired, and has a key revoked and restored, each by the right holder: the owner may suspend but not resume, retire ends the membership and revokes every key, and an owner leaving the organization suspends the account | P1 | active | organization administrator (membership grade admin or owner): register, mint and the lifecycle doors, the accountable owner, a plain member, a second plain member: the 403 legs, anonymous (no session): the 401 legs |
identity-auth.ai-service-account-register-and-key |
An organization administrator registers an AI service account and mints its key: the account is an agent row owned by a named member, with one plain membership and no sign-in, and its key admits requests as the account in the account's organization | P1 | active | organization administrator (membership grade admin or owner): register, mint and the lifecycle doors, the accountable owner, a plain member, a second plain member: the 403 legs, anonymous (no session): the 401 legs |
identity-auth.scim-refused-audit-row |
A refused SCIM request writes one bounded scim_refused audit row naming the connection, the presented credential kind, the resource, the method and the status, never the bearer | P2 | active | an identity provider calling SCIM 2.0, admin (the seeded admin): the mint and the audit read |
identity-auth.console-two-factor-prompt-phone-doors |
The Console's phone doors on a two-factor account ask for the code: phone + password and the phone code each move to the two-factor step instead of an error, and a code completes the sign-in | P1 | active | a user of this run with an email, a phone number, a known password and a verified TOTP enrollment: every step |
identity-auth.console-sign-out-and-ended-session |
Sign-out leaves none of the signed-out user's browser state and no return link, while a session that ended on the server sends the next in-app navigation to sign-in with its return link | P1 | active | seeded admin (admin@objectos.ai / admin123): the signed-out user and the revoked session, org member — created by the admin with POST /api/v1/auth/admin/create-user, password changed so the session is not held: the next person to sign in |