Types what crosses the wire for federated sign-in, by which a user signs in to one gerbeur instance with their account on another.
The protocol is a subset of OpenID Connect, the authorization code flow with PKCE, with tokens signed by each instance's Ed25519 key. Between the instances, and between each instance and its client, travel:
- each instance's metadata, served at
/.well-known/openid-configuration; - the pages' requests to start and finish a sign-in, here
(
/api/federation/*) and on the home instance (/api/oauth/*); - the claims of an identity, and the events a home instance sends the instances its users signed in to;
- the instances an admin trusts or blocks, and the instances a user let their account sign in to.
In the code, the user's own instance is their home, playing the identity provider; the instance they sign in to is the client.
Returns the host of the instance at issuer, with its port if it has one:
the part after the @ of its users' handles here (alice@music.example).
Checks whether value is a FederationEvent, as the events endpoint
must once the signature is verified.
Checks whether value carries the IdentityClaims, as a client must
before creating or updating an account from them.
Checks whether value is an InstanceMetadata of this protocol's
version, as remote/gerbeur-instance.ts must before reading one.
Checks whether value is a PeerStatus.
Returns the origin of the instance input names, or null when it names
none. Someone may type a bare host (music.example, taken as https), a URL,
or a handle's host with its port (localhost:8001); a URL with a path names
no instance. Whether plain http is allowed is the server's call.
Sent to POST /api/oauth/codes by the /authorize page once the user
consents, from the query the client instance sent them with.
Returned by POST /api/oauth/codes: the client's callback page with the
code, where the /authorize page sends the user.
Returned by GET /api/me/grants to the settings page: an instance the
user let their account sign in to.
Sent to POST /api/federation/logins by the login page, or by the register
page with the invite it was opened with.
-
instance: string
The user's home instance, read with parseInstance.
- inviteToken: string
Returned by POST /api/federation/logins: the home instance's /authorize
page with the request in its query, which the client navigates to.
Returned by GET /api/federation/peers: the login page offers each trusted
one, and /manage lists them all.
- createdAt: Date
-
host: string
instanceHost of the issuer.
- issuer: string
-
name: string | null
The site's name, as its metadata gave it when it was added.
- status: PeerStatus
Sent to POST /api/federation/sessions by the /federation/callback page,
from the query the home instance sent the user back with. The answer is an
AuthResponse, as a sign-in's.
Carried by an identity token and by an update event: what a home instance
tells another of a user. The names are OpenID Connect's.
- email: string
-
picture: string
Their avatar's URL at home, versioned; absent without one.
-
preferred_username: string
The user's name at home; the client calls them
<name>@<host>.
Served by every instance at /.well-known/openid-configuration
(routes/well-known.ts), and read by another before it trusts it, starts a
sign-in on it or sends it an event. The names are OpenID Connect's, and the
gerbeur_ fields are this protocol's own.
-
authorization_endpoint: string
Its
/authorizepage, where a user lets another instance sign them in. - code_challenge_methods_supported: string[]
-
gerbeur_events_endpoint: string
POST /api/federation/events, where it receives a home's events. - gerbeur_federation: number
-
gerbeur_outgoing: boolean
Whether its users may sign in to other instances, so that a client refuses at once to send them to its
/authorizepage, which would. An instance that doesn't say lets them. -
gerbeur_redirect_uri: string
Where its home instance sends a user back once they consent, as a client: its
/federation/callbackpage, on its frontend. -
gerbeur_site_name: string
The site's name, which a consent page and the peer list show.
- grant_types_supported: string[]
- id_token_signing_alg_values_supported: string[]
-
issuer: string
The instance's public URL, an origin, which names it in every token.
-
jwks_uri: string
GET /.well-known/jwks.json, its public keys. - response_types_supported: string[]
- scopes_supported: string[]
- subject_types_supported: string[]
-
token_endpoint: string
POST /api/oauth/tokens, which trades a code for an identity token.
Returned by GET /api/oauth/clients?client_id= to the /authorize page,
which asks the user whether to sign in there.
- clientId: string
-
granted: boolean
Whether the user already let it sign them in, in which case the page doesn't ask again.
- host: string
- name: string
Sent to PUT /api/federation/peers by /manage.
-
instance: string
As someone typed it, read with parseInstance.
- status: PeerStatus
| { type: "revoke"; }
The payload of a signed event, which a home instance sends each instance a
user signed in to (POST /api/federation/events): update when their name,
email or avatar changed, revoke when their sessions there must end, after
a password change or when they withdraw the grant.
What an admin decided about an instance. Users of a trusted one sign in here directly; those of a blocked one never do, and its users already here are signed out. An instance with neither is invite only: its users sign in here with an invite.
The version of the protocol below, which an instance announces in its InstanceMetadata. An instance refuses one announcing another.
Usage
import * as mod from "shared/federation.ts";