v4: an instance can be named by the address of its pages — sign-in and the trust list took only an instance's issuer, its API's origin, so where the pages live elsewhere the address people see in their browser named no instance: in development, localhost:3000 answered its /.well-known with index.html, and the sign-in failed as "doesn't answer as a gerbeur". resolveIssuer (remote/gerbeur-instance.ts) now reads the metadata at the address given and, when it names another issuer, takes it only if that issuer's own metadata names those pages back as where its /authorize is, so an impostor can't claim one; sign-in, the trust list and the check against this instance go through it. The Vite dev server passes /.well-known on to its API, as whatever serves the pages must in production (README). A handle still carries the API's host.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,8 @@ import {
|
||||
parseInstance,
|
||||
type PeerStatus,
|
||||
} from "../../shared/federation.ts";
|
||||
import { fetchMetadata } from "../remote/gerbeur-instance.ts";
|
||||
import { fetchMetadata, resolveIssuer } from "../remote/gerbeur-instance.ts";
|
||||
import { isSelfHost } from "../lib/self-url.ts";
|
||||
import { closeUserSockets } from "./ws-service.ts";
|
||||
|
||||
interface PeerRow {
|
||||
@@ -64,8 +65,8 @@ export function listPeers(trustedOnly: boolean): FederationPeer[] {
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the instance `instance` names on the list of `status`, for `/manage`,
|
||||
* and returns it. A trusted one must answer as a gerbeur instance, whose name
|
||||
* Puts the instance `instance` names, by its API's address or its pages'
|
||||
* (`resolveIssuer`), on the list of `status`, for `/manage`, and returns it. A trusted one must answer as a gerbeur instance, whose name
|
||||
* is kept; a blocked one is blocked whether it answers or not, keeping the
|
||||
* name it had. Blocking one ends the sessions of its users here at once:
|
||||
* their tokens stop counting by `getSessionState`, and this closes their
|
||||
@@ -78,20 +79,27 @@ export async function setPeer(
|
||||
instance: string,
|
||||
status: PeerStatus,
|
||||
): Promise<FederationPeer> {
|
||||
const issuer = parseInstance(instance);
|
||||
if (!issuer) throw new APIException(400, "Not an instance's address");
|
||||
if (issuer === PUBLIC_URL) {
|
||||
const origin = parseInstance(instance);
|
||||
if (!origin) throw new APIException(400, "Not an instance's address");
|
||||
if (isSelfHost(new URL(origin).host)) {
|
||||
throw new APIException(400, "That is this instance");
|
||||
}
|
||||
// An instance is listed by its issuer, which its pages' address leads to;
|
||||
// one that doesn't answer may still be blocked, by the address given.
|
||||
let issuer = origin;
|
||||
let name: string | null = null;
|
||||
try {
|
||||
issuer = await resolveIssuer(origin);
|
||||
name = (await fetchMetadata(issuer)).gerbeur_site_name;
|
||||
} catch (err) {
|
||||
console.error(`[federation] ${issuer} doesn't answer:`, err);
|
||||
console.error(`[federation] ${origin} doesn't answer:`, err);
|
||||
if (status === "trusted") {
|
||||
throw new APIException(502, "That address doesn't answer as a gerbeur");
|
||||
}
|
||||
}
|
||||
if (issuer === PUBLIC_URL) {
|
||||
throw new APIException(400, "That is this instance");
|
||||
}
|
||||
db.prepare(
|
||||
`INSERT INTO federation_peers (issuer, status, name, created_at)
|
||||
VALUES (?, ?, ?, ?)
|
||||
|
||||
Reference in New Issue
Block a user