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; and
  • role: 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; and
  • groups: optional active group memberships encoded as sorted, comma-delimited group-slug:role pairs.

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:

  1. validate the JWS signature using the key selected by the protected header's kid;
  2. require the current signed-token issuer and the resource's exact audience;
  3. require the OAuth client_id authorized for the request;
  4. require the expected raw token form and principal_type for the route;
  5. validate exp, nbf, granted scopes, and any applicable sender constraint;
  6. enforce profile-specific tenant, role, group, or application policy; and
  7. 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.