Access-token formats
M7 issues three access-token profiles: consumer user, tenant member, and OAuth
application. All three are signed JWTs, but their subjects and authorization
boundaries are different. A resource server must select the profile it expects
as part of its own route policy; it must not grant authority merely because an
unverified token contains a recognized principal_type.
This page describes access tokens only. Treat refresh tokens as opaque even though M7 currently signs them as JWTs. An ID token is an OpenID Connect identity assertion for the relying party and must not be used as an API access token.
Three different type fields
An M7 token exchange and signed access token can expose three similarly named values. They are not interchangeable:
| Location | Example | Meaning |
|---|---|---|
| OAuth token response | "token_type": "bearer" |
How the access token is sent in HTTP authorization. |
| JWT protected header | "typ": "JWT" |
The serialization is a signed JSON Web Token. |
| JWT payload | "typ": "session" or "access" |
M7's credential form and lifecycle discriminator. |
Current human access tokens retain payload typ = session while the Identity
service completes its migration to typ = access. M7's shared token
normalizer treats that signed session form as an access token. OAuth-client
tokens issued by client_credentials already carry payload typ = access.
Do not accept an arbitrary session JWT as an access token. Validate the full
profile, including signature, issuer, expected audience and client, time
claims, scope, principal type, and server-side active state where required.
Profile discriminator
| Access-token profile | principal_type |
Current payload typ |
Canonical sub |
|---|---|---|---|
| Consumer user | consumer |
session |
M7 consumer user ID |
| Tenant member | tenant |
session |
Tenant member/client ID |
| OAuth application | oauth_client |
access |
Public OAuth client_id |
principal_type states whose authority the token represents. The sub claim
is the stable canonical subject within that profile. Use sub, rather than a
name, email address, or presentation field, as the principal identifier.
Shared access-token envelope
The three profiles use the following shared claims:
| Claim | Meaning |
|---|---|
iss |
Current signed-token issuer, id.m7.org. This differs from the authorization-server issuer in discovery. |
typ |
Raw credential form: currently session for human profiles and access for OAuth applications. |
principal_type |
consumer, tenant, or oauth_client. |
id |
Identity/profile identifier used by M7. It is not a substitute for the profile's canonical sub. |
sub |
Canonical subject for the selected profile. |
client_id |
OAuth client to which issuance is bound. For human tokens, this is the relying application, not the human or member. |
aud |
Intended resource audience. A resource must require its own audience. |
scope |
Granted space-delimited scopes, when scopes were granted. |
iat, nbf, exp |
Issuance, not-before, and expiry timestamps. |
jti |
Unique token/session identifier used for authoritative state checks. |
A token can also contain sender-constraint compatibility data or profile-specific claims. Do not infer authority from an unfamiliar claim, and ignore extension claims unless the resource has an explicit policy for them.
Who may inspect and depend on claims
An OAuth client that obtains an access token should normally treat it as a credential for the target resource, not as the source of its application session. Use a validated ID token for OpenID Connect sign-in and UserInfo for the current scoped identity projection. Client applications must not derive a user, tenant, role, or group decision merely by decoding an access token.
A resource server may inspect and depend on the documented access-token claims only after it has validated the complete token and selected the expected profile from trusted route policy. It may rely on the documented core and profile-specific claims for that validated profile. It must ignore unknown extension claims unless its own public authorization contract assigns them a meaning.
Refresh tokens are signed JWTs in the current implementation but are opaque to clients. Do not decode them or depend on their claims. Store and send the whole refresh package as documented by the token endpoint.
ID tokens are signed OpenID Connect JWTs intended for the relying party. The
relying party may inspect and depend on their standard claims only after
validating the signature, aud, azp, time claims, and nonce when one was
requested. Never send an ID token to a resource API as an access token.
The examples below show decoded payloads with non-secret placeholders. They are claim-shape examples, not tokens that can be used for authentication.
Consumer-user access token
{
"iss": "id.m7.org",
"typ": "session",
"principal_type": "consumer",
"id": "IDENTITY_PROFILE_UUID",
"sub": "CONSUMER_USER_UUID",
"name": "alice",
"role": "CONTROL_PLANE_ROLE",
"client_id": "OAUTH_CLIENT_ID",
"aud": "https://api.example.com",
"scope": "openid profile email",
"iat": 1787000000,
"nbf": 1786999995,
"exp": 1787000900,
"jti": "TOKEN_SESSION_UUID"
}
Consumer-specific claims are:
id: the consumer's M7 identity/profile ID;sub: the canonical consumer user ID;name: the consumer's stable handle; androle: the authoritative control-plane role, omitted when no role is set.
Consumer access tokens do not carry the legacy user alias, avatar/profile
presentation data, tenant organization or group claims, or arbitrary
relying-party claim decoration. Obtain presentation data through UserInfo.
Tenant-member access token
{
"iss": "id.m7.org",
"typ": "session",
"principal_type": "tenant",
"id": "AUTHENTICATING_IDENTITY_UUID",
"sub": "TENANT_MEMBER_UUID",
"name": "alice",
"org": {
"id": "TENANT_ORG_UUID",
"slug": "example-org"
},
"groups": "engineering:member,owners:admin",
"client_id": "OAUTH_CLIENT_ID",
"aud": "https://api.example.com",
"scope": "openid profile groups",
"iat": 1787000000,
"nbf": 1786999995,
"exp": 1787000900,
"jti": "TOKEN_SESSION_UUID"
}
Tenant-specific claims are:
id: the M7 identity that authenticated the tenant session, which can be a tenant shadow identity or a linked consumer identity;sub: the canonical tenant member/client ID;name: the tenant member's stable handle;org: the single fenced tenant context, containing only its ID and stable slug; andgroups: optional active group memberships encoded as sorted, comma-delimitedgroup-slug:rolepairs.
The groups claim is omitted when the member has no active group membership.
Group slugs and roles cannot contain , or : while this compact format is in
use. Organization and membership status are checked when M7 issues or refreshes
the token; they are not repeated in the payload.
A tenant token must never satisfy consumer/control-plane authorization, even
when its id, name, email, or linked account corresponds to a consumer.
OAuth-application access token
{
"iss": "id.m7.org",
"typ": "access",
"principal_type": "oauth_client",
"id": "OAUTH_CLIENT_ID",
"sub": "OAUTH_CLIENT_ID",
"client_id": "OAUTH_CLIENT_ID",
"org": "TENANT_OR_NIL_UUID",
"org_name": "ORGANIZATION_NAME",
"client_name": "APPLICATION_NAME",
"ogp": "ISSUING_CREDENTIAL_UUID",
"aud": "https://api.example.com",
"scope": "files.read files.write",
"iat": 1787000000,
"nbf": 1786999995,
"exp": 1787000900,
"jti": "TOKEN_SESSION_UUID"
}
OAuth-application tokens are issued by client_credentials and represent the
application itself, never a human login. In the current profile, id, sub,
and client_id all identify the OAuth application. The org claim is a UUID
string, not the object used by tenant-member tokens; a personal application can
carry the nil UUID. org_name and optional client_name are context metadata.
ogp is the M7 issuing-credential reference and must not be interpreted as a
human subject.
Application policy can permit additional configured claims. M7-owned claims
win over requested or configured values. An administrative application can
also receive the explicit machine_owner_proxy = true extension; a resource
must reject that mode unless its route policy specifically supports owner
proxy authority.
Validation requirements
Before using any of these claims, a resource server must:
- validate the JWS signature using the key selected by the protected
header's
kid; - require the current signed-token issuer and the resource's exact audience;
- require the OAuth
client_idauthorized for the request; - require the expected raw token form and
principal_typefor the route; - validate
exp,nbf, granted scopes, and any applicable sender constraint; - enforce profile-specific tenant, role, group, or application policy; and
- perform an online active-state or revocation check when the resource's risk model requires one.
See Discovery and signing keys for M7's per-key lookup model and issuer distinction. Decoding a JWT without completing these checks does not validate it.