# unauthorized

The caller is not signed in, or the sign-in or admin token no longer counts (401).

## Meaning

- Hosted sign-in UI: the request carries no sign-in session, or the pending sign-in
  between password and second factor has lapsed (`auth.pending.ttl`), as on the
  consent, device verification and MFA endpoints ("sign in to continue").
- The MFA endpoints also answer it when the factor or recovery codes they act on do
  not exist, so factor state is never revealed.
- Admin API: the token was absent or did not verify, or a DPoP-bound token failed
  its sender constraint. `WWW-Authenticate` says which; a challenge naming
  `use_dpop_nonce` means retry with the nonce sent in `DPoP-Nonce`.

## Impact

A user whose sign-in step took too long sees "Your sign-in session expired" and
signs in again; this is expected now and then. A dashboard user is sent to sign in
again. An admin-API client getting it on every call has a broken token setup.

## Diagnosis

1. Read the `WWW-Authenticate` header on an admin-API answer.
2. Look the request id up on the Troubleshooting page. Hosted UI refusals name a
   cause such as `pending_expired`, `pending_absent`, `session_absent` or
   `session_expired`. An admin token refusal is recorded as `admin.access.denied`
   with reason `unauthenticated` and a cause such as `credential_missing`,
   `token_expired` or a `dpop_*` cause.
3. For lapsed pending sign-ins across many users, check `auth.pending.ttl` and the
   cache (`cache.*`), which holds pending sign-ins.

## Mitigation

- As a user: choose "Sign in again" and complete each step without long pauses.
- As an admin-API caller: get a new token; for a DPoP-bound token send
  `Authorization: DPoP <token>` and a fresh proof on every call, and on a
  `use_dpop_nonce` challenge repeat the call once with the nonce from `DPoP-Nonce`.
- As an operator: if pending sign-ins lapse for everyone, check that every replica
  reaches the same cache.

## Escalation

Escalate when a freshly issued admin token is refused, or pending sign-ins lapse well
inside `auth.pending.ttl`. Open an issue with the request id, the audit cause, the
`WWW-Authenticate` value and the server version. Never attach tokens or proofs.

---

[Problem code runbooks](https://www.nauthera.io/docs/v1/runbooks/problems) · Generated from [`internal/runbook/problems.yaml`](https://github.com/nauthera/nauthera-server/blob/main/internal/runbook/problems.yaml) in nauthera-server.
