Tenant identity
Access to the calling tenant's own users, teams and sessions. Shipped. The grant is per (tenant, app): an app without it cannot reach the identity APIs at all.
"permissions": [
{ "identity": "users", "access": ["read", "write"] },
{ "identity": "teams", "access": ["read", "write"] },
{ "identity": "sessions", "access": ["write"] }
]
| Resource | What it covers |
|---|---|
users | Tenant user accounts |
teams | Tenant teams and memberships |
sessions | Session management, including revocation |
read looks the resource up; write creates, updates and deletes it. Each entry names exactly one of the three, so an app that only needs to look a user up declares { "identity": "users", "access": ["read"] } and nothing else.
What it is for
The typical consumer is an app that mirrors business records onto platform identities — a customers app turning contacts into users and organisations into teams, so that a person who exists in the commerce data can actually sign in, and a company's staff share the right group.
Written out, that shape is:
- A business record is created or changes in your own tables.
- Your handler reads or writes the matching platform identity through the identity API.
- The two stay linked by an id you store in your own row.
Store the platform identity's id as a plain column on your entity — the same way you store any reference across a boundary. There is no foreign key from your table to a platform identity.
Zero-secret auth
You hold no credential for this. A request reaching your function already carries the caller's identity context, injected by the gateway, and that context is what authorises the identity call:
async function findUser(c, email) {
const res = await fetch(`https://api.revenexx.com/v1/users?email=${encodeURIComponent(email)}`, {
headers: {
'X-Revenexx-Tenant': c.header('x-revenexx-tenant'),
'X-Revenexx-Context': c.header('x-revenexx-context'),
'X-Request-ID': c.header('x-request-id'),
'Accept': 'application/json',
},
});
if (!res.ok) throw new Error(`identity lookup failed: ${res.status}`);
return res.json();
}
There is no service account to create, no client secret to store in a variable, no token to refresh, and nothing to rotate when somebody leaves. You forward the context you were invoked with. That is the same property the runtime adapter gives you over your own tables, and the same forwarding rules as calling another app.
Two things follow from it:
- Never accept an identity from a request body. The tenant and the acting principal come from the context. A user id in a body is a claim; the context is a fact.
- An app cannot act outside the tenant it was invoked for. There is no credential with wider reach for it to borrow, which is why this grant is safe to hold at all.
Handle it as personal data
This grant reaches people, not rows. Two habits that are worth more than they cost:
Grant read unless you truly create identities. An app that looks a user up to attribute an action does not need write.
Do not copy what you can resolve. Storing a linking id and reading the person's details when you need them is better than duplicating a name, an email and a phone number into your own table, where they will go stale and where nobody expects to have to delete them.
Say why on the consent screen. listing.scopes is where a merchant reads the reason, and "create a platform login for each customer contact so they can sign in to the portal" is an answerable sentence. See Consent.
What it is not
It is not authentication for your app. Your app does not sign anybody in; the gateway does. See Auth.
It is not operator permissions. Whether this person may perform an operation is provides_permissions and the Cockpit view gates.
It is not a customer database. Contacts, organisations and their commerce relationships live in the commerce apps. A platform identity is the login, not the customer record.
Next steps
- Permissions — the rest of the register.
- Person permissions — what a person is allowed to do.
- Auth — how a caller is authenticated in the first place.
- Calling another app — the same forwarding pattern.