V The Edge

Identity and Access

Every request that enters an application raises two questions: who is asking, and may they do this. Applications usually answer both in code, differently in each service. In Toa both are the gateway’s work, driven by declarations on the resource tree. Operations do not check permissions.

§Identity

An identity is the unique identifier of whoever is on the other side: a person, an organization, a device, another application. It is an identifier and nothing more. It has no name, email, or profile; those belong to the application’s own components.

An identity exists through its credentials. Creating the first credential creates the identity, and removing the last one ends it. One identity can hold several credentials of different kinds, and they all resolve to the same identifier.

§Authentication

Authentication resolves the credentials of a request to an identity. It considers only the credentials, not the resource being requested.

The gateway supports several schemes: a username and password, tokens issued by external identity providers, one-time passwords, and passkeys. Whichever a client starts with, the gateway answers with a token of its own, and the client continues with that. The application’s resources see one kind of proof regardless of how the identity was first established.

The components that store credentials, issue tokens, and hold roles are supplied with the gateway. They are ordinary Toa components, configured like the application’s own, but the application does not write them.

§Linking identities to the domain

An application has its own notion of a user: an account, a customer, a member. The usual link is the simplest one, where the entity and the identity share an identifier.

# accounts/manifest.toa.yaml
exposition:
  /:
    POST:
      auth:incept: id
      endpoint: transit

A request to this route creates an account and, in the same step, an identity with the same id, attached to the credentials the request carried. From then on, “the account of the caller” is the account whose identity matches.

§Authorization

Authorization decides whether an identity may use a resource. It is declared on the tree with directives, and a request is admitted when one of the directives that apply to it grants access.

/orders:
  /:
    auth:role: support
    GET: enumerate
  /:customer:
    auth:id: customer
    GET: enumerate
    POST: transit
DirectiveGrants access when
anonymousThe request carries no credentials.
anyoneThe request carries any valid credentials.
idThe identity equals the value of a route variable.
roleThe identity holds a role covering the named scope.
claimsThe token of an external provider carries the named claims.
ruleAll of several directives grant together.

A resource with no directive that grants is closed. Access is something a route states, never something it has by omission.

§Ownership by route

auth:id expresses the most common rule in any application: people may reach what is theirs. A route that carries the owner in its path, /orders/:customer/, is open to the identity that matches that segment. The rule is visible in the URL and requires no lookup.

§Roles

A role is a value assigned to an identity. Roles form a hierarchy written with colons: support:billing lies within support. A role covers every scope within it, so an identity with the role support may use a route that requires support:billing, and not the reverse.

Scopes can be built from route variables, which gives roles a per-resource meaning without a separate permission system:

/:org:
  auth:role: org:{org}:admin

§Why operations do not check

Keeping authorization at the edge is a consequence of product–platform separation, and it has practical effects.

Operations stay about the business. approve approves an order. It is the same operation whether a manager calls it through the gateway, another component calls it in a workflow, or a test calls it directly.

Access is reviewable. Who may do what is read from the manifests, not reconstructed from conditionals spread through code.

An identity is not smuggled into logic. A route can pass the caller’s identifier to an operation where the domain needs it, for example as the owner of a new order. That is a mapping declared on the route, and the operation receives it as ordinary input.

The boundary has a corollary. Calls between components inside the application are not authorized. Components of one application trust each other, and the gateway is where the outside world is held to account.

§Acting on behalf of an identity

Other applications sometimes need to act for a user: a client program, or an assistant that calls the application’s tools. The gateway can act as an authorization server for them. The user consents on a page the application serves, and the client receives a token that carries the user’s identity restricted to the scopes that were granted. To the rest of the system it is one more credential resolving to the same identity.

Search
Search the documentation