Organizations, persons and grants
A person can do something in OktoID only because they hold a grant: a bundle of permissions, for one organization and everything beneath it. Nothing the identity provider says about the person (a group, a role, an attribute) gives them any authority. Signing in proves who someone is; their grants decide what they may do.
This page explains the parts and the rules that connect them. The requests that put them to work, from a fresh stack to a second administrator signing in, are in Install and sign in.
Organizations
Organizations form a tree. Each one has a path made of the slugs above it,
such as /oktoid/acme/branch, and an id (a UUID) that never changes.
- The top of the tree is the anchor. The setup run creates it from the
deployment's answer file; on a local stack it is
/oktoid. - An organization is created with
POST /api/v1/subjects, beneath the organization you act for, or beneath aparentPathinside it. - A slug is lowercase letters, digits and hyphens, 1 to 63 characters, starting and ending with a letter or a digit. It is unique among its siblings.
Organizations listed in the answer file are created by the setup run on every deploy if they are missing.
Persons
A person is someone who signs in. They are registered with
POST /api/v1/persons and sit beneath the organization that registered them:
a person registered for acme has the path /oktoid/acme/<their id>.
- A person is always a leaf. Nothing can be created beneath a person.
- A person's path ends in their id, never their name. Paths appear in logs and audit records, and a name there would be personal data in places that are never erased.
- Names and email address are personal data. They are stored encrypted under a key that belongs to that person alone, are never logged, and disappear from every reply once the person is erased. Erasing a person destroys that key, which leaves the stored values unreadable. This release has no request that erases a person.
"invite": truecreates the person's account in the staff realm of the identity provider and sends them an invitation. Before the account can sign in, its holder must verify the address, set a password, and set up a second factor with an authenticator app. Withoutinvite, the person exists in OktoID but has no account to sign in with.
Registering a person gives them no authority. Until someone grants them a bundle, they can sign in and do nothing.
Bundles
A bundle is a named set of permissions. This release has five, created by the setup run. No request creates or changes a bundle.
| Bundle | Holds | Who holds it |
|---|---|---|
ENTITY_ADMIN | Running an organization: create and read organizations, create and read persons, and create, read and revoke grants. Nothing of the trust tree | An organization's administrators, and the root account at the anchor unless the answer file names another bundle |
ORGANIZATION_CLIENT | Create and read organizations | Machine clients, unless the answer file names another bundle for one |
TRUST_ANCHOR_ADMIN | Governing the trust tree: put an organization forward, admit it, approve publication, and read accreditations, entity statements and organizations | The governance authority's staff who decide admissions |
ACCREDITATION_OFFICER | Put an organization forward, and read accreditations, entity statements and organizations. It cannot admit or approve publication | Staff who prepare admissions for someone else to decide |
SOLE_ADMIN | Everything in ENTITY_ADMIN and TRUST_ANCHOR_ADMIN together | The root account on a local stack, where one person runs and governs. The setup run refuses it for the root account in a staging or production deployment |
Running an organization and governing the tree are kept apart on purpose. A
holder of ENTITY_ADMIN cannot admit anyone, and because delegation only
narrows, cannot grant a bundle that can. What the trust-tree permissions are for
is in The trust tree.
The permission definitions in @oktoid/contracts are the authority on what each
bundle holds.
Grants
A grant says: this person holds this bundle for this organization. It gives the bundle's permissions over the organization and everything beneath it, and nothing above or beside it.
- A grant is made with
POST /api/v1/grants, naming the person (granteeId), the organization (organizationId) and the bundle. - A person holds at most one active grant per organization. To change the bundle, revoke the grant and make a new one.
- A person may hold grants for several organizations, and acts for one of them at a time.
GET /api/v1/grantslists the grants for organizations inside your own, filtered byorganization,granteeorstatus.- Every grant and every revocation is written to the audit record, with who did it.
A grant gives authority, not visibility elsewhere. Anything outside the
organization you act for answers 404, exactly as something that does not exist
would. A refusal never tells you whether the thing exists.
Machine clients hold grants too: one each, for the organization the answer file says it acts for, made by the setup run. A grant made through the API is always for a person.
Delegation only narrows
You can grant only what you hold yourself, and only inside your own organization. A grant is refused unless:
- every permission in the bundle is one your own grant has, and
- the organization is the one you act for, or beneath it.
So an administrator of /oktoid/acme can make another administrator of
/oktoid/acme or of /oktoid/acme/branch, but not of /oktoid, and cannot
hand out a bundle larger than their own. A refused grant answers 404, the same
answer as for an organization or person that does not exist.
The same rule applies to revoking: you cannot revoke a grant larger than your own, so a narrower administrator cannot lock out a broader one.
Acting for an organization
Every request a person makes names the organization they act for, in the header
Oktoid-Acting-For: <the organization's id>
For that request, the person's reach is that organization's subtree, and their permissions are those of the bundle on their grant there.
- Without the header, a person's request is refused with
403. - With an organization they hold no grant for, the request is refused with
the same
403, whether or not the organization exists. - Machine clients send no header. They act for the one organization their grant names.
To learn which organizations you can act for, ask
GET /api/v1/me. It is the one request that acts for no organization: it takes
no Oktoid-Acting-For header, ignores one if sent, and needs no permission. It
answers your own active grants, each with the organization's id (the value for
the header), its path and the bundle:
{"grants":[{"organizationId":"<a UUID>","path":"/oktoid/acme","bundle":"ENTITY_ADMIN"}]}
It never shows anyone else's grants. If you hold none, the list is empty rather
than an error. It reads your grants afresh on every call, so a grant made or
revoked shows there at once, even while a revoked grant can still authorize
requests (see Revoking a grant). On a local stack,
pnpm oktoid login prints the same list when you sign in
(Install and sign in, step 5).
Revoking a grant
POST /api/v1/grants/{id}/revoke
The grant's status becomes revoked, and the change is audited. Revoking a grant
that is already revoked answers the same revoked grant and changes nothing.
A revocation takes effect within one token lifetime: at most five minutes. The gateway remembers what a signed-in person may do for a short time, never longer than five minutes and never past the expiry of their access token. Until that memory expires, a revoked grant can still authorize requests. Plan for that window when you remove someone's access in a hurry: revoke the grant, and expect up to five minutes of residual access.
Who can revoke whom
Revoking needs the same three things as granting: the revoke permission (in this
release only ENTITY_ADMIN holds it), a grant for an organization that is the
one you act for or beneath it, and a bundle no larger than your own. Who made
the grant is not checked. In practice:
- Anyone who holds the revoke permission can revoke their own grant. That includes the last administrator of an organization, who then leaves it with none. The root account is the exception in effect: the next setup run restores its grant (see The root account).
- A peer administrator can revoke the administrator who appointed them. If
an administrator of
/oktoid/acmeappoints a second administrator of/oktoid/acme, each can revoke the other. - An administrator cannot revoke a grant above their own organization. The
administrator of
/oktoid/acmethat the root account appointed cannot revoke the root account's grant at/oktoid.
Revoking a grant does not revoke the grants it was used to make
When a person revokes a grant, the grants its holder made stay active. If an
administrator of /oktoid/acme appointed others and their own grant is then
revoked, every grant they made keeps working. Each grant names who made it
(grantedBy in the reply of GET /api/v1/grants), and every grant and every
revocation is in the audit record with who did it, so the grants a removed
administrator made can be found and revoked one by one.
A revocation the platform triggers itself, rather than a person, cascades. It also revokes the grants made under the revoked grant. The one such revocation in this release is the setup run's, when the answer file gives a machine client a different bundle: its old grant is revoked and a new one made, both audited.
The root account
The root account is the first staff account, created by the setup run for the
mailbox the answer file names. It holds ENTITY_ADMIN at the anchor, and every
other grant descends from it.
The setup run restores the root account's grant. If the grant is revoked or
missing, the next setup run makes it again. On a local stack, that is the next
pnpm stack:up. The setup run then:
- records the new grant in the audit record, as any grant;
- reports a
root-grant-restoredfinding in its output.
Revoking the root account's grant is therefore not a way to remove the root. To stop a compromised root account from being used, change its credentials at the identity provider.