# Acquire organization memberships and apps through API BigFS

The current integration transports caller-bound M7 organization facts:

```text
BigFS -> API BigFS -> platform remote organizations -> Identity SDK -> SSO
```

Use a consumer user or ordinary client-credentials application token with an
accepted API BigFS audience and the existing token/sender-binding/principal
checks. The organization routes permit apps backing either a consumer or tenant
directory; tenant member tokens and personal machine-owner proxies are rejected.
They require the canonical issuer `https://sso.user.m7.org` and verified token
`sub`/`client_id`; the broader legacy issuer policy does not override this check.
For apps, `sub` must match the public `client_id`, and SSO returns the app's own
explicit Management memberships. Creator access is never inherited.

Register and explicitly request `orgs`; consumer sign-in requires hosted consent.
The scope and app inventory do not grant bucket or vault permissions. Device
authorization currently rejects `orgs`. Other resource routes retain their
existing token-mode and resource-access rules.

## Owner-controlled visibility

SSO applies each org owner's discovery policy to the requesting OAuth app before
returning membership public data, tags or app inventories. Requester registration
and owning org are resolved from the validated token's `client_id`; request body
`org`/`tenant` values cannot select the policy identity. The requester can be a
personal or org-owned app.

Only stored owners read/write discovery policy. Ordered app/org rules use the
first match across both types. With no match, apps owned by the disclosed org use
`same_org_discoverable`; all other apps use `discoverable`. Both default off.
An explicit deny remains authoritative even for owner/admin/member callers.
Owners opt in BigFS's public Client ID or owning org through User M7 **Access →
Policy**; the saved app rule binds a registration UUID, so a recreated Client ID
does not inherit the old allow. See [policy controls](https://m7.org/docs/api/api.user.m7.org/org.md#organization-discovery-policy).

An allow grants disclosure of existing membership facts. Registration ownership
does not grant membership, and membership/disclosure does not grant bucket or
vault access. A successful empty list may reflect policy filtering. Failed
policy/storage lookup is an error, not an empty success. Local imported references
cannot bypass a fresh lookup under current discovery policy.

## Resolve and search

Send POST JSON to both routes:

| Path under `https://api.bigfs.m7.org` | Input | Success |
| --- | --- | --- |
| `/v1/organization/endpoint` | `{}` | `status: 1`, `data.endpoint`: trusted discovered SSO org URL. |
| `/v1/organization/search` | `{}` for unbound tokens; `orgs_dpop` for a bound token | `status: 1`, `data.sub`, `data.orgs`: SDK-validated caller projection. |

```bash
curl --fail-with-body --silent --show-error \
  'https://api.bigfs.m7.org/v1/organization/search' \
  --header 'Authorization: Bearer ACCESS_TOKEN' \
  --header 'Content-Type: application/json' --data '{}'
```

For a DPoP-bound token obtain the endpoint first. API BigFS's normal `DPoP`
header binds the API request. The separate JSON `orgs_dpop` proof binds the
discovered **SSO org endpoint**, POST and the same token/`ath`, with original
key and fresh `jti`. It must be a string at most 8,192 bytes, without CR/LF or
comma coalescing. The server forwards it to SSO; it never fabricates a proof.
Each API call still requires its own normal API proof when bound.

Successful rows preserve org ID/name/slug, caller `access_level` and
`management_groups`: group/membership ID/name/role, membership `pub`, and tags
with ID/name/slug/definition `data`. Each org also contains `consumer` and
`tenant` lists of its owned active, unarchived, unexpired registrations, split
by nil/non-nil tenant directory. App descriptors contain only `id`, `client_id`
and `name`, with no app slug. The public `client_id` need not be a UUID;
they show shared ownership and convey no permission to call those
apps. Both lists can be empty. Older responses omitting both retain their
absence; malformed or partial inventories fail the request.

The provider currently has one implicit
Management group per org. See the
[SSO projection](https://m7.org/docs/api/sso.user.m7.org/userinfo-orgs.md) and
[SDK validation](https://m7.org/docs/sdk/m7-identity/cli/organization-access.md).
Other principals, private `pri`, internal metadata and expiry fields are excluded.
Empty lists are valid; original identity-only rows confer no absent access facts.

## Failures and freshness

Endpoint discovery failure is a 502 route failure with safe prose. Missing
required `orgs_dpop` returns `status: 0`, `error: "invalid_dpop_proof"`.
Upstream scope/transport/validation failures return `status: 0`, an OAuth error
or `orgs_upstream_failed`, safe `comment`, and `data.dpop_nonce` when available.
Do not infer success from HTTP status alone or turn failure into an empty list.
Current SSO does not issue nonces; the adapter preserves an upstream challenge
and BigFS can explicitly retry once with a fresh nonce-bearing SSO proof.

The caller's `sub` and `client_id` come from verified claims, not body selectors.
Each search uses a fresh SSO membership lookup and signed/encrypted response
verification when registered. Permission changes and departure affect the next
lookup; a saved selection is not continuing authority. The normal
[API BigFS authorization guide](https://m7.org/docs/api/api.bigfs.m7.org/authorization.md) still governs resources.

## Platform service and local import boundary

Reusable PHP service:
`platform\service\remote\organizations\Organization`.
`endpoint(context)` resolves the trusted URI; `available(token, context)` returns
the full live `ManagedOrganizationsReport` without SQL. Context permits only
verified `client_id`, `expected_sub`, and caller `dpop`; constructor options own
trusted issuer/endpoint/certificate/transport policy. Unknown options fail.
The service does not refresh/retry, create proofs or store credentials.

Optional backend `import(remoteID, token, context)` and
`refresh(localID, token, context)` each reacquire live visibility and pass only
remote org **ID/name/slug** to the application-local import repository. Local
issuer/reference/status/timestamps support tracking, not authorization. Caller
role/scopes/tag data/membership `pub` and consumer/tenant app inventories are
never stored as durable import authority.
Remote failures/lost visibility leave existing references unchanged. Applications
authorize import and resource associations themselves; these routes expose live
lookup, not a new HTTP import API.

## Installation and frontend pickup

Install API User management tags and configured membership metadata before SSO
reads them, then SSO endpoint/scope/discovery, complete updated SDK source,
shared platform/bootstrap, API BigFS and the BigFS client. See
[upgrade order](https://m7.org/docs/api/api.user.m7.org/organization-upgrade.md)
and [PHP root configuration](https://m7.org/docs/sdk/m7-identity/cli/organization-access.md#installation-and-release-boundary).
`M7_LIB_ROOT` needs `namespace.php`; `M7_PLATFORM_ROOT` needs `require/startup`;
platform `M7_IDENTITY_TOKEN_ROOT` needs `packages/token-php/autoload.php`.
Live lookup needs no local import table; optional import storage needs normal
SQL quote/UUID helpers and its repository installer. Existing schemas are not
blanket replaced. Later role/modal/departure slices add no extra schema.

BigFS app sign-in adds `orgs` for the canonical consumer issuer; the client
must register it and a new token must obtain consent. Tenant issuer sign-in is
not converted into consumer org access. The Organizations integration page at
`/members/orgs/` uses the configured API client, endpoint then search, separate
SSO proof and bounded nonce recovery. It displays access/membership IDs and roles,
scopes, expandable tag data and membership public JSON, plus separate consumer
and tenant app sections with names, app IDs and client IDs as literal text. Empty,
legacy and missing facts remain explicit. Changed sessions discard a pending
result; scope failure offers sign-in again and other failures remain retryable.

Current source and synthetic checks verify transport/display/import boundaries.
Live deployment acceptance of this response is not established here;
the current SDK org/email operations are unreleased. Seeing these facts does
not authorize bucket or blog operations. Consumers define scope semantics and
resource policy; Blog adoption remains future work. Admin delegation and
read-only app/key inventory are deferred; the standalone catalogue UI was
cancelled and membership expiry was not added.
