diff --git a/src/app.ts b/src/app.ts index 0b2f2a6..d27e72f 100644 --- a/src/app.ts +++ b/src/app.ts @@ -508,15 +508,7 @@ function acceptsBackendCallback(config: HeimdallConfig, appSlug: AppSlug, handof return false; } - if ((config.appBackendCallbacks[appSlug] ?? []).includes(handoff.callbackUrl)) { - return true; - } - - if (appSlug === "repixelizer") { - return handoff.callbackUrl === "https://repixelizer.gamecult.org/api/auth/heimdall/callback"; - } - - return false; + return (config.appBackendCallbacks[appSlug] ?? []).includes(handoff.callbackUrl); } async function maybeDeliverBackendHandoff(handoff: OAuthHandoff, payload: BackendHandoffPayload): Promise { diff --git a/tests/app.test.ts b/tests/app.test.ts index 938003b..47196c6 100644 --- a/tests/app.test.ts +++ b/tests/app.test.ts @@ -32,6 +32,7 @@ function createTestConfig(): HeimdallConfig { }, appBackendCallbacks: { bifrost: ["https://bifrost.gamecult.org/auth/heimdall/callback"], + repixelizer: ["https://repixelizer.gamecult.org/api/auth/heimdall/callback"], }, storage: { backend: "memory", @@ -898,6 +899,34 @@ describe("Heimdall service", () => { ); }); + it("no longer trusts Repixelizer's callback URL by slug name alone", async () => { + const baseConfig = createTestConfig(); + const config = { + ...baseConfig, + appBackendCallbacks: {}, + }; + const app = await buildApp({ config }); + apps.push(app); + + const response = await app.inject({ + method: "POST", + url: "/v1/oauth/discord/start", + payload: { + appSlug: "repixelizer", + mode: "sign_in", + returnTo: "https://repixelizer.gamecult.org/app/", + handoff: { + kind: "backend_callback", + attemptId: "attempt-123", + callbackUrl: "https://repixelizer.gamecult.org/api/auth/heimdall/callback", + }, + }, + }); + + expect(response.statusCode).toBe(400); + expect(response.json()).toEqual(expect.objectContaining({ error: "untrusted_backend_callback" })); + }); + it("resolves app-managed provider credentials without exposing refresh custody", async () => { const store = new InMemoryStore(); const app = await buildApp({