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.id and nil tenant.
  • tenant: oauth_clients.org = org.id and non-nil tenant.

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.