Plays the home instance in federated sign-in: it lets this instance's users sign in to other gerbeur instances with their account here.

The flow is OpenID Connect's authorization code with PKCE, in three steps.

  1. Another instance, the client, sends the user to the /authorize page with a request. The page shows who asks (describeClient) and, once the user consents, gets a code (createAuthorizationCode), which it carries back to the client's callback page.
  2. The client trades the code for an identity token, server to server (exchangeCode): this user's name, email and avatar, signed with the instance key.
  3. While the user's grant lasts, the client hears of their changes: a new name, email or avatar, and the end of their sessions after a password change or reset. notifyClients queues the signed events in federation_outbox, and outboxJob delivers them.

Only a local account signs in elsewhere: a user who came from another instance signs in from there. routes/oauth.ts, routes/well-known.ts and the routes that change a user call it.

Functions

f
createAuthorizationCode(
userId: string,
request: AuthorizeRequest
): Promise<string>

Records user userId's consent to sign in to the client of request, and returns where to send them: the client's callback page with a code, which the client trades within a minute (exchangeCode). The grant lasts until they withdraw it, so the page asks no more.

f
deliverOutbox(): Promise<void>

Sends the outbox's events to each instance that has some, every instance at once so that one that doesn't answer delays none of the others.

f
describeClient(
userId: string,
clientId: string
): Promise<OAuthClient>

Returns the instance clientId names, for the /authorize page's question to user userId, and whether they already let it sign them in.

f
exchangeCode(params: URLSearchParams): Promise<{ id_token: string; token_type: string; }>

Trades a code for an identity token, for POST /api/oauth/tokens, which the client calls server to server with the form params. A code works once, within a minute, for the client and redirect it was made for, and with the verifier whose hash the request carried (PKCE). The token, signed with the instance key and addressed to the client, carries identityClaims and the request's nonce.

f
getInstanceMetadata(): InstanceMetadata

Returns the instance's metadata, served at /.well-known/openid-configuration.

f
identityClaims(user: User): IdentityClaims

Returns what this instance tells another of user: their name, email and avatar, whose URL changes with each new one, so that a client fetches it again only then.

f
listGrants(userId: string): FederationGrant[]

Returns the instances user userId let their account sign in to, the most recently used first, for the settings page.

f
notifyClients(
userId: string,
event: FederationEvent | { type: "update"; },
clientIds?: string[]
): Promise<void>

Tells each instance user userId signed in to of event, or only those of clientIds: update when their name, email or avatar changed, revoke when their sessions there must end. The routes that change a user call it, and for an update, event may be just the type: the claims are read now. The events are queued in the outbox and their delivery starts at once, after those queued before for the same instance (deliverTo).

f
revokeGrant(
userId: string,
host: string
): Promise<void>

Withdraws user userId's grant to the instance whose host is host: it stops answering for them there, and is told to end their sessions.

f
withdrawAllGrants(): Promise<number>

Withdraws every grant, as each user would theirs, and drops the codes not yet traded, so that no account here stays signed in elsewhere: main.ts calls it at startup when FEDERATION.outgoing is off. Returns how many grants it withdrew.

Variables

v
ISSUER: string

The instance's name in every token it signs and every token addressed to it: its public URL, which must be an origin for other instances to find its metadata.

v
outboxJob: Job

Retries the outbox's failed events every minute.

v
REDIRECT_URI: string

The client's page a home instance sends the user back to, on the frontend. Its metadata announces it, and a home accepts no other.