# Browser-direct member passwords

All operations below are SSO POSTs below the [browser-direct base](https://m7.org/docs/api/sso.user.m7.org/browser-direct.md).
They take session handles, JSON and fresh endpoint DPoP; no additional profile
or email scope is required. Selected-member mutations require current active
member/session/token admission. Status receipts have the narrower recovery
semantics described below. Never send a member selector or Identity credential.

## Choose the operation

| Member | Available action |
| --- | --- |
| Native tenant password | Change with current password; remove only after fresh retained-provider sign-in |
| Federated-only tenant member | Set first native password after fresh eligible provider sign-in |
| Consumer-linked member | Manage password through the consumer account; tenant set/change/remove is unavailable |
| Missing, ambiguous or unusable identity binding | Unavailable; do not guess a credential or manufacture one |

| Relative POST path | Exact input | Output |
| --- | --- | --- |
| `/password/options` | Session handles only | Capability projection below |

Options returns `mode`
(`set`, `change`, `external`, `unavailable`), `can_set`,
`reauthentication_required`, `setup_status` (`idle`, `needs_password`,
`indeterminate`, `completed`), `message`, `removable`, `can_remove`,
`password_identity`, `removal_identity` (local UUID or null), `removal_status`
(`idle`, `pending`, `completed`), and `revocation_pending`. Use this current
capability response to build controls. The local identity ID is not a remote
credential and must not be inferred from a handle.

## Change an existing password

| Relative path | Additional input beyond handles | Output |
| --- | --- | --- |
| `/password/change` | `current_password`, `new_password`, `confirm_password` (each ≤4096) | `status`, `next_action: sign_in`; completed includes boolean `sessions_revoked`, indeterminate includes `message` |
| `/password/status` | None | `status: idle`, or the stored completed/indeterminate receipt |

```json
{"bd_carrier":"<carrier>","bd_session":"<selected session>","bd_key":"<carrier secret>","current_password":"<current password>","new_password":"<new password>","confirm_password":"<same new password>"}
```

The new password must match confirmation and differ from the current password.
`current_password_invalid` (400) leaves the old credential valid and clears the
attempt marker. `password_not_configured` and
`consumer_password_managed_elsewhere` (403) identify unavailable account types.

A mutation marker is persisted before the remote write. Unresolved change blocks
refresh, ACK and switching for that session. Confirmed completion closes all
remembered entries for this same member in the current carrier; other members
remain. Native Identity credential revocation can affect that native credential
across apps/devices; `sessions_revoked` reports whether remote revocation was
confirmed, not a count or a guarantee about independent provider credentials.
Same-carrier provider entries for the member are removed too. An indeterminate
result must never be labeled successful or retried as a password write.
An indeterminate change closes only the operation's local session; it does not
claim that all same-member entries were closed. Confirmed changes also attempt
remote family cleanup for the closed same-carrier entries, including provider
entries; that work can remain pending without changing the native revocation
boolean in the receipt.

After a lost response, call `/password/status` with the original handles and key.
This reads the receipt and can work after local child revocation while the
carrier retains that child; it does not refresh or repeat the mutation. Closing
the carrier removes that recovery path. Try the intended password in a fresh
sign-in or use recovery if the outcome remains uncertain. Repeating a change
with its existing receipt does not start another write.

## Set the first native password

| Relative path | Additional input | Output |
| --- | --- | --- |
| `/password/set` | `new_password`, `confirm_password` | Setup receipt: `status`, `next_action` and explanatory `message` as applicable |
| `/password/set-status` | None | Current receipt; may resume safe reconciliation steps |

Both require a real sign-in through an eligible **currently linked provider**
within five minutes. Refresh, switching and email sign-in do not satisfy this
retained-provider proof. No current-password field is accepted. The member must
have no native password and no consumer link. The new native credential uses the
same tenant login name without replacing the provider identity or the current
provider session. Completed `next_action: password_sign_in` offers the new
capability; it does not force logout. Existing tenant 2FA remains intact.

Use status after a lost response. `needs_password` explicitly permits asking for
the password again; no password is stored in the receipt. `indeterminate` means
remote credential creation/update was not confirmed. Do not repeatedly create
credentials or infer failure from transport errors. Some unknown creation
outcomes require operator investigation because no remote identifier was
received. `password_already_configured`, `password_setup_unavailable` and
`password_removal_pending` (409) require reloading options/reconciling first.

## Remove native password sign-in

| Relative path | Additional input | Output |
| --- | --- | --- |
| `/password/remove` | `password_identity` local UUID from reviewed options | `status: pending` or `completed`, `removed: true`, `sessions_revoked`, `revocation_pending`, `message` |
| `/password/remove-status` | Same reviewed `password_identity` | Receipt; **can resume retirement**, not a read-only query |

A remove-status call without a matching removal receipt returns `status: idle`;
the reviewed identity is an input and is not echoed in this receipt.

Removal requires fresh sign-in (five minutes) through a verified provider that
will remain linked and is currently permitted. A native password, email login,
refresh or remembered-account switch alone does not qualify. Ask the member to
confirm the reviewed password identity. A stale identity ID must never target a
later replacement credential.

Local removal immediately disables that binding and closes password-authenticated
and legacy unattributed tenant sessions across applications and hosted SSO.
Known provider/email sessions remain; consumer and other-member sessions are
separate. Remote native-password retirement can remain pending. Retry the exact
remove/status receipt with fresh proof; do not create a replacement until
retirement is confirmed. Completed receipt may still report `revocation_pending`
for extra token-family cleanup; `sessions_revoked` here is a local count, unlike
the change operation's boolean. Pending/confirmed results must remain distinct.

A retired native credential cannot be recovered or reactivated. A later eligible
setup creates a new native credential. Removal does not clear tenant factors or
saved email-2FA preferences. It does not unlink the retained provider.

## Fresh sign-in without duplicate remembered accounts

For `reauthentication_required` (403), keep the carrier and signing key, perform
an actual new sign-in, and include `bd_carrier,bd_key` in authorization-code
pickup. Once the new session is active/ACKed, SSO replaces older entries for the
**same member UUID** in that carrier. Display names are not identity keys.
Failure or pending activation preserves old entries and selection. Other members,
carriers and applications are unaffected. See [selection recovery](https://m7.org/docs/api/sso.user.m7.org/browser-direct-sessions.md).

## Password validation

The endpoints reuse hosted password validation. The default medium policy
requires at least 12 Unicode characters and at least two character groups for
passwords shorter than 20 characters; low requires 8 characters, high requires
16 and three groups below 24 characters. The maximum is 1,024 Unicode characters
and common weak passwords are rejected. Operators can select another configured
policy, including disabled strength checks; request nonempty/type/4096-byte and
NUL/CR/LF rules still apply. Show returned validation feedback without treating
its prose as a stable error code. Do not trim or log passwords.
