# Relationship authorization

Use a trusted backend credential to call Relationship on behalf of your
application. API caller identity, project access and the business principals
stored in a relationship are separate boundaries.

## Credential and transport

| Credential | Contract |
| --- | --- |
| M7 access token | Signed compact JWT; treat its value as opaque. Send `Authorization: Bearer ACCESS_TOKEN`. |
| API audience | Exact audience `https://api.relationship.service.m7.org`; a frontend token is not automatically an API credential. |
| Caller | A user or confidential machine admitted by the receiving service; current roster status must be active. |
| Sender binding | Use an unbound Bearer token. Sender-constrained tokens are rejected with `sender_proof_required`; this API does not implement a DPoP protected-resource flow. |

The service validates token signature, the permitted `RS512` algorithm,
accepted issuer (`https://sso.user.m7.org` or `id.m7.org`), trusted signing
certificate source, audience and time constraints. It then uses
receiver-authenticated M7 admission checks. Decoding a JWT or possessing a
signed token does not establish project authority. Never authorize your users
from decoded token claims alone.

ID tokens, refresh tokens, client secrets, authorization codes and API keys are
not substitutes for this access token. Send the access token only in the
Authorization header, over HTTPS; keep it out of URLs, request bodies, logs
and analytics. Keep confidential client credentials and refresh packages in
server-side secure storage.

## Obtain, replace and revoke tokens

Use [M7 SSO](https://m7.org/docs/api/sso.user.m7.org/) with the OAuth client and
audience registered for your integration. Its discovery document is at
`https://sso.user.m7.org/.well-known/openid-configuration`.

| Caller | Token acquisition |
| --- | --- |
| Application acting for a user | Authorization code with PKCE; device authorization for an input-constrained client. |
| Confidential backend acting as itself | `client_credentials`, with the client's registered authentication method. |

For a confidential client configured for `client_secret_basic`:

```bash
curl --fail-with-body --silent --show-error \
  --request POST 'https://sso.user.m7.org/token' \
  --user 'CLIENT_ID:CLIENT_SECRET' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode 'aud=https://api.relationship.service.m7.org'
```

Request `scope=orgs` as well when using an organization workspace. The OAuth
client policy must permit that audience, grant and scope. Select an issuance
policy that returns an unbound Bearer token for this API.

Observe SSO's returned `expires_in`; do not assume a fixed lifetime. A machine
token has no refresh token: authenticate the client again to obtain a
replacement. User-authorized refresh credentials and their binding package go
to SSO's token endpoint under the applicable SSO lifecycle. Relationship
does not refresh tokens. Revoke credentials through SSO or the owning
application's credential controls; revoked, expired or denied admission is not
overridden by project membership. After a 401, obtain a valid replacement;
repeated 403 responses require an access decision rather than token retries.

## Project authority and application principals

The effective workspace principal must have an active membership in the
project's pinned Management group. Receiving-service Root or Admin status does
not supply another project's membership. Use the returned project context to
inspect current capabilities.

| Operation | Required project capability | Additional role boundary |
| --- | --- | --- |
| Read project context | `project.view` | Active project membership. |
| Read spaces/subspaces | `space.list` | Current optional read grant. |
| Read current relationships, requests or activity | `relationship.list` | Current optional read grant. |
| Create/save/remove relationships; create/decide requests | `relationship.manage` | Project Owner or Admin. |
| Read or acknowledge mailbox events | `mailbox.notifications` | Current mailbox grant; acknowledgement rechecks admission. |

Read grants can be changed independently of membership. Current role,
capabilities and admission are checked again during mutations. A business
reference such as `{"namespace":"example.app","principal_id":"PERSON_UUID"}`
does not enroll that person as an API caller or project operator. The consuming
application must verify that reference and decide which user can create,
accept, cancel or remove a relationship before forwarding a command.

## Personal and organization workspaces

Omit organization selection for the caller's personal workspace. Select an
organization with header `Org: ORGANIZATION_UUID` or body field `org_id`.
The value must be a non-nil dashed UUID. If both are supplied they must match
case-insensitively; null or malformed selections are rejected. Do not send
organization names, membership objects or capability lists as authority.

Organization operations require an access token with `orgs` scope and a current
organization disclosure to Relationship. Every selected-organization request,
including reads, obtains current organization facts. For a machine, the
validated OAuth client must be the actual machine caller. The organization's
stable principal and own project membership supply workspace access; the
machine's personal memberships are not inherited. The actual actor remains
separately attributed in relationship events.

Current direct Management tag **names** resolve through the Relationship
catalogue. Explicit selections replace role fallback; a nonempty selection
of unknown names grants nothing. Only a verified empty selection can use its
configured role fallback. Resolved organization grants are intersected with
project capabilities. A tag never promotes the project membership role.

For an unbound Bearer token, no `Org-DPoP` companion header is required. That
header concerns an organization disclosure proof; it does not add DPoP
protected-resource support or make a sender-constrained token acceptable here.

```bash
curl --fail-with-body --silent --show-error \
  --request POST 'https://api.relationship.service.m7.org/v1/projects/context' \
  --header 'Authorization: Bearer ACCESS_TOKEN' \
  --header 'Org: ORGANIZATION_UUID' \
  --header 'Content-Type: application/json' \
  --data '{"project_id":"PROJECT_UUID"}'
```

## Authorization failures

| Failure | Action |
| --- | --- |
| HTTP 401; `missing_token`, `invalid_token` or `token_required` | Supply an unexpired M7 token for the exact API audience. |
| HTTP 401; `sender_proof_required` | Use an authorized unbound Bearer issuance profile. |
| Admission or operation code 403 | Check active receiving admission, workspace disclosure, project membership and the required capability. |
| Admission HTTP 503 or operation body code 5xx | Authorization verification is unavailable; fail closed and retry with backoff. |
| Code 400 for organization selection | Correct its UUID and conflicting selections. |

Inspect the HTTP status, `status` and `data.code`, as described in the
[service response rules](https://m7.org/docs/api/api.relationship.service.m7.org/README.md#requests-and-responses). Credentials
must never be resent to an arbitrary endpoint named in an error message.
