Return 401s when logged out of more activitypub api endpoints (#750)

Co-authored-by: slothful-vassal <89943360+slothful-vassal@users.noreply.github.com>
This commit is contained in:
Robert Clarke
2026-01-31 22:04:56 +00:00
committed by GitHub
parent 1005aa0a84
commit 029f1f134b
10 changed files with 82 additions and 22 deletions

View File

@@ -0,0 +1,35 @@
import { env } from '$env/dynamic/private';
import type { RequestEvent } from '@sveltejs/kit';
import { ClientResponseError } from 'pocketbase';
import type { Actor } from '$lib/models/activitypub/actor';
import { isRemoteHandle } from '$lib/util/activitypub_util';
type GetActorOptions = {
follows?: boolean;
};
type ActorResponse = {
actor: Actor;
error?: string | null;
};
export async function getActorResponseForHandle(event: RequestEvent, handle: string, options: GetActorOptions = {}): Promise<ActorResponse> {
const origin = env.ORIGIN!;
const remoteHandle = isRemoteHandle(handle, origin);
if (remoteHandle && !event.locals.user) {
throw new ClientResponseError({ status: 401, response: { message: "Unauthorized" } });
}
const searchParams = new URLSearchParams();
if (options.follows) {
searchParams.set("follows", "true");
}
const query = searchParams.toString();
const encodedHandle = encodeURIComponent(handle);
const response: ActorResponse = await event.locals.pb.send(
`/activitypub/actor?resource=acct:${encodedHandle}${query ? `&${query}` : ""}`,
{ method: "GET", fetch: event.fetch },
);
return response;
}

View File

@@ -3,8 +3,13 @@
export function splitUsername(handle: string, localDomain?: string) {
const cleaned = handle.replace(/^@/, "").trim();
let normalizedLocalDomain = localDomain;
if (normalizedLocalDomain && normalizedLocalDomain.includes("://")) {
normalizedLocalDomain = new URL(normalizedLocalDomain).hostname;
}
if (!cleaned.includes("@")) {
return [cleaned, localDomain];
return [cleaned, normalizedLocalDomain];
}
let [user, domain] = cleaned.split("@");
@@ -12,6 +17,22 @@ export function splitUsername(handle: string, localDomain?: string) {
return [user, domain]
}
export function isRemoteHandle(handle: string, origin: string) {
const [, domain] = splitUsername(handle, origin);
if (!domain) {
return false;
}
let normalizedDomain = domain;
try {
normalizedDomain = new URL(`http://${domain}`).hostname;
} catch {
normalizedDomain = domain.split(":")[0];
}
normalizedDomain = normalizedDomain.replace(/^www\./, "");
const localHost = new URL(origin).hostname.replace(/^www\./, "");
return normalizedDomain.toLowerCase() !== localHost.toLowerCase();
}
export function handleFromRecordWithIRI(record: any) {
if (!record.expand?.author) {
throw new Error("object has no author info")
@@ -23,4 +44,4 @@ export function handleFromRecordWithIRI(record: any) {
const url = new URL(record.iri ?? "")
return `@${record.expand.author.preferred_username}@${url.hostname}`
}
}