The Access Token
The one short token, for every audience: a JWT the account service signs, naming whose it is, for whom, for what, and until when.
- Proves: an account's identity, and who's acting for it (the machine, or the person in the browser), to one audience, with what it may do there.
- Made by: the account service, from a machine assertion, or from a person's sign-in in the browser for a visitor's token. It signs with a key of its own, a secret it holds alone.
- Held by: whatever asked for it, in memory only, never written to
disk. On a machine the accounts plugin gets one and hands it to the
plugin that asked, through a pipe, never to a terminal:
lnk tunnelfor the relay,lnk bucketfor the share API. - Travels: as
Authorization: Bearer, overhttps://, or to a relay on this machine.lnk tunnelsends the relay its token in the connection'sHellotoo. - Lasts: as long as its signer says, and no longer than its
checker's longest life. Local Link's account service signs each for
5 minutes. The relay takes one of at most its spec's
[tokens] max_life, 15 minutes unless its operator says otherwise (Relay Spec). - Revoked: never one by one: its short life is its end. A machine
signed out gets no more, and
link-relay admin user block <user>refuses a user's at once on that relay. - May: what its grants say, at its audience, and nothing else.
Its form
A compact JWS: <header>.<claims>.<signature>, each base64url.
The header and iss
The header holds alg EdDSA, typ at+jwt, kid the signing key's
id, and nothing else. iss is the account service's origin, as
https://accounts.example.com.
sub
Who's acting, as RFC 7519 means it, <kind>:<id>: machine:<its id>
for a machine's, agent:<its id> for a machine's agent's, as itself,
and person:<uuid> for a visitor's, today their account's own uuid. The kind is all before the first :; the id after it is
opaque and may hold : and /. The kinds, the spec's PrincipalKind,
are machine, person, agent, service and api-key.
acc and aud
acc is the account it acts in, acct-<32 hex>: whose resources its
grants reach. aud is one audience: a relay's domain, as
local-link.xyz, or a service's origin.
act and scope
act, optional, is the service acting for sub, as {"sub": "service:<its name>"} (RFC 8693's actor); no token has one yet.
scope is its grants, separated by spaces; none for a token
that only says who it is.
usr and email
usr is the account's username when it was made, for the relay's
addresses alone. email is the account's email, only in a visitor's
token and only when the account service has verified it.
nonce, iat and exp
nonce is only in a visitor's token: the relay's state for the
sign-in it answers, so it can't be carried to another. iat and exp
are when it was made, and when it ends, in Unix seconds.
How it's checked
What a checker refuses, and the keys it checks with.
The header first
A checker reads the header before anything else, and refuses a token
whose alg isn't EdDSA (none among them), whose typ isn't
at+jwt, whose header holds anything more, or whose kid it doesn't
hold.
Then the claims
It then checks the signature with that key, iss the account service
it trusts, aud its own, a grant of what it's asked to do
(Scopes), exp in the future and at most its longest life
away, and exp no further than that from iat. It allows no
difference of clocks.
A claim it doesn't know
A claim it doesn't know, or one missing, is refused: a token as they
were made before grants, its account in sub, with mch and one
scope, among them.
The service's keys
Checkers hold the account service's public keys, each with its id, and
never ask the service about a token: a service that's down or slow
costs them nothing. The relay is given them as --accounts-key.
Replacing a key
To replace its key, the account service publishes the new one beside the old, signs with the new once every checker holds it, and drops the old one its longest life later.
Scopes
A machine asks for a token of one scope, in its assertion; the token
holds that scope's grants in scope, and what checks it reads only the
grants.
tunnel
Grants tunnel.open@tunnel:*, for a relay: a machine opens tunnels in
its account's namespace there, serves them and lists them.
share
Grants share.make@share:*, for a relay: a machine makes, lists and
ends its account's share links there.
visit
Grants tunnel.visit, for a relay: a person, signed in in the browser,
visits that relay's tunnels whose allow names their account or email.
api
Grants machine.read machine.admin agent.admin, for the account
service: a machine calls its API as that machine, to list and sign out
the account's machines, add a box's machine key, and report its agents.
identify
Grants none, for any service, by its origin: a machine says who it is to that service alone, which checks the token itself.
agent
Grants what the machine names for one of its agents, for any service
by its host but the account service and a relay. The assertion names
the agent by its id (agent) and its grants there (grants), at most
32, such as team.member@team:<id> for each team it's on, and the
token's sub is agent:<its id>. lnk auth token --audience <host> --agent-id <id> --grants <grants> asks for one, for the program that adds
it to the agent's calls, never the agent itself.
Whose agent
An agent's token says what its machine asserts, nothing more: its sub
is the agent the asking account names, which the account service holds
only to the machine's own last report (lnk remote), and a machine
writes that report itself. So sub may name an id another account's
agent has, and a checker must hold the agent, and each resource the
token grants, to acc before it trusts either. Its grants are inside
the account alone: none of another's (acct-<id>/..., */...).
Grants
A grant is <action> or <action>@<resource>.
An action
<kind>.<verb> (tunnel.open), <kind>.* for every verb of a kind, or
* for every action.
A resource
<kind>:<id> (tunnel:web), or a pattern ending in * for every one
it begins (tunnel:*, tunnel:ci-*), or *; a grant without one
reaches every resource of the account.
allows(action, resource)
Whatever checks a token asks one question, allows(action, resource):
whether a grant names that action on that resource. It looks for the
grant of the action it serves and skips every other, an action it
doesn't know among them, so an action can be added to tokens without a
relay that's older refusing them.
The kinds it takes
Each checker also takes only the kinds of sub it serves, so a broad
grant to another kind is never read as one of them.
What the relay asks
The relay takes a machine's token, asking tunnel.open on tunnel:*
to open, serve and list tunnels and share.make on share:* for share
links, and a person's for a visitor, asking tunnel.visit on
tunnel:*: every one of the account's, so only a grant of them all
lets it in.
What the account service asks
The account service's API takes a machine's, asking what each route
names in its
spec:
on the token's own machine (machine:<its id>) to say who it is or
sign it out, on the machine a route's path names, its id checked
first, and on every machine (machine:*) or agent (agent:*) of the
account for the others.
What an identify checker asks
A service checking identify tokens takes a machine's and asks no
grant: sub and acc say who.
The cases
The account service's spec holds the cases allows answers (Grants,
x-allows), which its checkers in Rust (link_api::jwt) and the
account service's own run.
Not built yet
The relay takes a token of at most 15 minutes, not its spec's [tokens] max_life.