UserInfo and scopes
GET or POST https://sso.user.m7.org/userinfo
UserInfo returns claims for an active access token. Unlike many generic OIDC
deployments, M7 requires the requesting OAuth client_id as well as the
access token. This prevents one client from reading a token issued to another.
Request
Send a bearer token and the OAuth client that owns the token:
curl --fail-with-body --silent --show-error \
--header 'Authorization: Bearer ACCESS_TOKEN' \
'https://sso.user.m7.org/userinfo?client_id=YOUR_CLIENT_ID'
client_id may be supplied as a query parameter for GET or in the form body
for POST. The access token may be supplied in Authorization: Bearer; M7
also accepts token or access_token request fields for compatibility.
When the access token is fingerprint-bound, send its matching fingerprint
request field. Bearer authorization is the supported public profile. An
already provisioned DPoP-bound credential remains subject to its compatibility
profile; see DPoP status.
An absent, invalid, expired, revoked, or mismatched token returns HTTP 401 with
error: "invalid_token". A valid token issued to a different client_id
returns insufficient_scope with HTTP 403.
Scope-to-claim mapping
Every successful response includes sub, the canonical subject ID. Other
claims depend on the granted access-token scopes.
| Scope | Claims returned |
|---|---|
openid |
Enables OpenID Connect semantics and ID-token issuance at /token; UserInfo always retains sub. |
profile |
Standard profile claims and the M7 profile projection. |
email |
Primary active email address as email and its state as email_verified, when one exists. |
groups |
Tenant-member organization and group claims. It does not expose consumer-account groups. |
offline_access |
Requests durable offline-access behavior; it does not itself add UserInfo claims. |
Consumer-user response
For a consumer-user token with profile and email, the response resembles:
{
"sub": "USER_UUID",
"name": "Alice Example",
"preferred_username": "alice",
"picture": "https://cdn.example.com/alice.png",
"locale": "en-US",
"website": "https://example.com",
"zoneinfo": "UTC",
"updated_at": 1786309200,
"email": "alice@example.com",
"email_verified": true,
"m7": {
"user": {
"id": "USER_UUID",
"handle": "alice"
},
"links": {}
}
}
The exact m7.user profile projection may include publicly available profile
metadata. M7 can also return m7.preferences when the account has opted to
publish preferences. Treat the m7 object as an extension that may gain
additional fields; ignore fields your application does not understand.
Tenant-member response
For a tenant member, sub is the canonical member ID. With profile, M7
returns the same standard profile names where available and adds
m7.client. With groups, it can add m7.org and m7.groups:
{
"sub": "MEMBER_UUID",
"name": "Alice Example",
"preferred_username": "alice",
"m7": {
"client": {
"id": "MEMBER_UUID",
"handle": "alice"
},
"org": {
"id": "ORG_UUID",
"name": "Example Organization"
},
"groups": [
{ "id": "GROUP_UUID", "name": "Engineering", "slug": "engineering" }
]
}
}
Applications must authorize actions using the organization and group state returned at the time of the request, not by assuming cached membership remains valid indefinitely.
Machine-application response
For a client_credentials access token, sub equals the public OAuth
client_id. M7 does not load a human session. With profile, m7.application
contains the application's record ID, client ID, handle, application type, and
tenant ID when applicable.
Privacy and reliability
- Request only scopes your application needs.
- Treat
emailas optional even whenemailscope is granted; an account may have no active primary email. - Use
subas the durable identifier. Do not use a display name or email as a database key. - Do not assume profile or
m7extension fields are always present. - Do not use UserInfo as a substitute for token validation or resource-server authorization. Validate tokens and apply audience/client policy first.