Organization memberships and applications
GET or POST https://sso.user.m7.org/userinfo/orgs
This M7 extension is separate from standard /userinfo. Request the registered
OAuth scope orgs explicitly. Consumer authorization requires hosted consent;
client-credentials grants use the application's registered scope policy. An
orgs-only token can call this endpoint; standard UserInfo still requires openid. Omission of scope does not
implicitly grant orgs. Active consumer users and ordinary client-credentials
applications can discover their own Management memberships across organizations.
Tenant member tokens and personal machine-owner proxies are ineligible. Tenant
identity/groups remain fenced to their own directory. This is a
caller-requested lookup of existing organization facts. SSO does not push
organizations into a consuming server or grant that server access to its
resources.
Device authorization currently rejects orgs until its approval flow supports
the permission. See the normative provider profile.
Request and trust
Resolve m7_orgs_endpoint from trusted exact-issuer discovery. For an unbound
token:
curl --fail-with-body --silent --show-error \
--request POST 'https://sso.user.m7.org/userinfo/orgs' \
--header 'Authorization: Bearer ACCESS_TOKEN'
Use the UserInfo request contract for owning client_id
assertions, token transport, fingerprint and sender bindings. The provider derives
the client from the token and checks live token/session/account state, registered
scope/audience policy, and active organization ownership of a client where relevant.
No separate SSO resource audience is added by this endpoint. The credential
must be an access token; an ID token, refresh token, authorization code or API key
does not authorize this lookup. Verify the response sub exactly against the
already validated user subject or, for an ordinary app, its public Client ID.
For DPoP use Authorization: DPoP ACCESS_TOKEN and a fresh DPoP proof binding
this endpoint's exact URL, actual GET/POST method and access token ath; a proof
for /userinfo or API BigFS cannot be reused. For certificate-bound tokens,
present the bound certificate at the top-level m7_mtls_orgs_endpoint from
discovery. It is an M7 extension outside the standard mtls_endpoint_aliases
object. When both bindings apply, both are required. See
discovery.
Current projection
{
"sub": "22222222-2222-4222-8222-222222222222",
"client": null,
"orgs": [
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "Example",
"slug": "example",
"access_level": "member",
"consumer": [
{
"id": "66666666-6666-4666-8666-666666666666",
"client_id": "docs-client",
"name": "Docs"
}
],
"tenant": [],
"management_groups": [
{
"id": "33333333-3333-4333-8333-333333333333",
"name": "Management",
"membership_id": "44444444-4444-4444-8444-444444444444",
"role": "member",
"pub": {
"quota": 7
},
"tags": [
{
"id": "55555555-5555-4555-8555-555555555555",
"name": "BigFS read",
"slug": "bigfs-read",
"data": {
"actions": [
"read"
]
}
}
]
}
]
}
]
}
These UUIDs are valid illustrative identifiers; replace them with actual records.
Each org currently has one implicit Management group. This response does not
introduce arbitrary management-group creation. access_level and membership
role are the caller's live stored owner, admin or member role. The list
is sorted by org ID and can be empty (orgs: []). Only eligible active,
non-archived orgs, active Management groups/principals and matching current
user or application memberships survive the live lookup. A departure or removed
membership is omitted on the next lookup.
client describes the OAuth registration that issued this token, independently
of the user's orgs list. A personal consumer registration returns
{"org":null,"tenant":{"id":"00000000-0000-0000-0000-000000000000","slug":"m7-identity-consumer"}}.
For an org-owned registration, the owning org's disclosure setting controls
whether client contains org: {id, slug} and tenant: null (consumer app) or
tenant: {id, slug} (tenant app). A policy-hidden org-owned registration returns
client: null; it does not change which Management memberships appear in orgs.
The org owner also controls disclosure to the requesting OAuth registration.
SSO resolves its row id and current owning org from the validated token's
client_id; request-supplied identities and token tenant cannot select the
policy identity. Requesting apps may be personal or org-owned. The caller's
owner/admin/member role does not bypass disclosure policy.
For each eligible membership, SSO evaluates the org's ordered app/org ACL; the
first matching rule wins, without app-specific precedence. App rules match the
registration row UUID, and org rules match its non-nil owning org. With no
match, a requester owned by the disclosed org uses same_org_discoverable;
other requesters use discoverable. Both switches default off, including on
existing orgs with no saved policy. An explicit deny overrides the switches.
Only stored org owners can manage these settings through API User and the
User M7 Access → Policy tab.
Denied orgs are omitted entirely before loading public membership data, tags or app inventories. An allow rule permits disclosure of existing membership facts; it cannot create membership or grant product access. Invalid policy or failed storage fails the response. SDK and consuming applications receive the same projection shape, filtered by this live policy; a successful empty list may mean no memberships are discoverable to this app.
For a consumer token, sub is the user UUID. For an application token, sub is
its public client_id; the internal Management principal source is the OAuth
registration's row UUID. The provider authenticates the app as itself and checks
its owning org remains active, when it has one. App access additionally requires
at least one live human Management member in each returned org, matching API
User's application-access rule. The creator uid and owning org do not grant
Management membership. Add the app explicitly to Management to grant access;
its own membership supplies its role, pub and tags. Ordinary apps may back
either directory and may hold memberships in orgs other than their owning org;
tenant identity binding is validated independently of those memberships.
| Check | What it establishes |
|---|---|
Registration org |
Who owns/manages the app; not the caller's memberships |
| Caller Management membership | The live role, membership pub and assigned tags in this org |
| Owner discovery policy | Whether those facts may be disclosed to this requesting app |
| Consuming-service policy | Whether the authenticated caller may operate on a bucket or other resource |
Both consumer and tenant are always arrays, including when empty. They list
live app registrations owned by each returned org, not users, tenant-account
memberships or the caller's memberships in those apps:
consumer:oauth_clients.org = org.idand niltenant.tenant:oauth_clients.org = org.idand non-niltenant.
Only active, unarchived, unexpired registrations appear, sorted by row UUID.
Each descriptor contains id (row UUID), client_id (public OAuth identifier)
and name. OAuth registrations have no slug; client_id need not be a UUID.
Personal apps (org nil) are excluded even when created by the caller. Secrets,
creator IDs, configuration and notes are excluded. Inventory shows which apps
share ownership; it does not grant those apps access to each other or substitute
for a caller's Management membership.
| Data | Meaning |
|---|---|
Membership pub |
organization_manage_group_meta.pub, with asset equal to the caller's Management membership UUID. |
Tag data |
JSON on this individual tag definition, shared by assignments in its org's separate tag pool. |
Organization data |
Separate organization-level data; read through authorized API User org properties, not this projection. |
Tags use scope organization_management / org UUID and assignment asset
organization_management_member / membership UUID, not the global principal.
pub and tag data accept object/array/null. Missing public data is null;
no assignments gives tags: []. Current associative PHP decoding serializes
empty objects as []. The SDK preserves service-defined JSON contents without
giving them universal product permission semantics.
Other principals' memberships, pri, internal owner/principal/creator fields,
timestamps, assignment metadata and expiry fields are excluded. Membership expiry
was not added. Public means exportable through this authorized call, not anonymous
org access. Storage failures return an error, never a successful empty list.
Response policy and failures
HTTP 200 normally uses application/json. Registered UserInfo signing policy
returns application/jwt with RS256/RS512 signature, exact provider issuer,
client-ID audience and subject; registered encryption applies to that signed
response. Decrypt/authenticate first, then verify signature, issuer, audience,
time and exact subject. Policy, signer or encryption failures never fall back to
plaintext. See response encryption. Responses are
non-cacheable (Cache-Control: no-store).
Missing orgs returns HTTP 403 insufficient_scope and a required-scope
challenge. Invalid/inactive tokens, ineligible subjects or sender-binding
failures follow the UserInfo error contract, including
invalid_token and invalid_dpop_proof. Operational lookup/signing failures use
a safe server_error; do not turn them into empty authorization facts. The
current SSO profile does not issue DPoP nonces, while SDK reports preserve a nonce
challenge if an upstream deployment supplies one for explicit caller recovery.
Integration and rollout
Use current-source SDK acquisition or the website integration. The SDK performs strict nested validation and exact subject binding. Seeing an org or a tag does not grant bucket, blog or other product access: consuming services define scope semantics and authorize resources separately. Reacquire facts when needed instead of persisting a membership snapshot as authority.
Both public discovery documents advertised orgs, m7_orgs_endpoint and
m7_mtls_orgs_endpoint with identical metadata at 2026-09-27 02:11 UTC.
Current-source projection and validation checks passed locally; live acceptance
of the membership/application projection remains unverified here.
Install API User's existing membership/tag storage before enabling this reader;
SSO installs no org tables. See
upgrade order.