# forbidden

The caller may not do this, or the request looked cross-site (403).

## Meaning

On the hosted sign-in UI API (`/v1/auth/*`) a request is refused when it was not
sent as `application/json`, or its `Origin` or `Sec-Fetch-Site` names another site.
On the admin API (`/v1/admin/*`) the token is valid but:
- it was not minted for one of its organization's admin clients
  (`{slug}-admin-console`, `{slug}-admin-m2m`);
- the caller holds no role that grants the operation's permission (`detail` names
  it), or the token lacks the operation's scope;
- the path names an organization the caller may not manage. An unknown slug and
  another tenant's slug get the same answer, so it does not reveal which exist.

## Impact

Nothing changed. A user at the sign-in screen sees an access-denied error; this means
the page was not served from the issuer's own origin, or a proxy changed the request.
A dashboard user sees that they lack access, which is expected for a role that does
not cover the screen. A machine client hitting it is misconfigured.

## Diagnosis

1. Look the request id up on the Troubleshooting page.
2. A cross-site refusal is recorded on the action the request was for (for example
   `auth.sign_in.completed`) with cause `cross_site_request`.
3. An admin refusal is recorded as `admin.access.denied` with
   `required_permission`, `required_scope`, the target slug and a reason:
   `missing_role`, `missing_scope`, `unauthorized_client` or `cross_org_denied`.
4. `GET /v1/admin/orgs/{slug}/users/{id}/roles` shows what roles the caller holds.

## Mitigation

- As a user: open the sign-in page through the application again, not a bookmark of
  a page on another host.
- As an admin-API caller: request a token from the organization's admin client with
  the operation's scope, and have an owner grant a role with the permission named in
  `detail`. A token acts only in its own organization unless the caller holds a
  platform role in the main organization.
- As an operator: serve the hosted UI from the issuer's origin and make sure the
  proxy passes `Origin` and `Sec-Fetch-Site` through unchanged.

## Escalation

Escalate when a caller holding the documented role and scope is refused, or when
every hosted UI request is refused as cross-site. Open an issue with the request id,
the `admin.access.denied` record's reason and required permission, and the server
version. Never attach the token.

---

[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.
