diff --git a/packages/endpoint-auth/lib/controllers/consent.js b/packages/endpoint-auth/lib/controllers/consent.js index 5cc2b39f3..106fa02bc 100644 --- a/packages/endpoint-auth/lib/controllers/consent.js +++ b/packages/endpoint-auth/lib/controllers/consent.js @@ -1,6 +1,7 @@ import process from "node:process"; import { IndiekitError } from "@indiekit/error"; +import { getCanonicalUrl } from "@indiekit/util"; import { validationResult } from "express-validator"; import { getRequestUriData } from "../pushed-authorization-request.js"; @@ -47,7 +48,7 @@ export const consentController = { * @see {@link https://indieauth.spec.indieweb.org/#authorization-response} */ post(request, response) { - const { application } = request.app.locals; + const { application, publication } = request.app.locals; let scope = request.body?.scope; const { @@ -78,13 +79,16 @@ export const consentController = { scope = scope.join(" "); } + // Always use canonical publication URL, per IndieAuth spec ยง5.2 + const canonicalMe = getCanonicalUrl(publication.me); + // Create authorization code const code = signToken({ client_id, ...(code_challenge && { code_challenge }), ...(code_challenge_method && { code_challenge_method }), jti: crypto.randomUUID(), - me, + me: canonicalMe, redirect_uri, ...(scope && { scope }), }); diff --git a/packages/endpoint-auth/test/integration/302-consent-submit-authenticate-with-me.js b/packages/endpoint-auth/test/integration/302-consent-submit-authenticate-with-me.js index 70c9d5145..08d88c72c 100644 --- a/packages/endpoint-auth/test/integration/302-consent-submit-authenticate-with-me.js +++ b/packages/endpoint-auth/test/integration/302-consent-submit-authenticate-with-me.js @@ -6,6 +6,7 @@ import { testServer } from "@indiekit-test/server"; import supertest from "supertest"; import { createPasswordHash } from "../../lib/password.js"; +import { verifyToken } from "../../lib/token.js"; await mockAgent("endpoint-auth"); const server = await testServer(); @@ -19,7 +20,7 @@ describe("endpoint-auth POST /auth/consent", () => { const response = await request .get("/auth") .query({ client_id: "https://auth-endpoint.example" }) - .query({ me: "https://website.example" }) + .query({ me: "https://another.example" }) .query({ redirect_uri: "https://auth-endpoint.example/redirect" }) .query({ response_type: "code" }) .query({ state: "12345" }); @@ -34,6 +35,8 @@ describe("endpoint-auth POST /auth/consent", () => { .send({ password: "foo" }); const { host, protocol } = new URL(response.request.url); const issuer = encodeURIComponent(`${protocol}//${host}`); + const code = new URL(response.headers.location).searchParams.get("code"); + const decoded = verifyToken(code); assert.equal(response.status, 302); assert.match( @@ -41,6 +44,7 @@ describe("endpoint-auth POST /auth/consent", () => { /code=(.*)&iss=(.*)&state=(.*)&me=(.*)/, ); assert.ok(response.headers.location.includes(`iss=${issuer}`)); + assert.equal(decoded.me, "https://website.example/"); }); after(() => server.close()); diff --git a/packages/endpoint-auth/test/integration/302-consent-submit-authenticate.js b/packages/endpoint-auth/test/integration/302-consent-submit-authenticate.js index f0beb3923..15d276248 100644 --- a/packages/endpoint-auth/test/integration/302-consent-submit-authenticate.js +++ b/packages/endpoint-auth/test/integration/302-consent-submit-authenticate.js @@ -6,6 +6,7 @@ import { testServer } from "@indiekit-test/server"; import supertest from "supertest"; import { createPasswordHash } from "../../lib/password.js"; +import { verifyToken } from "../../lib/token.js"; await mockAgent("endpoint-auth"); const server = await testServer(); @@ -31,9 +32,12 @@ describe("endpoint-auth POST /auth/consent", () => { .type("form") .query({ request_uri: `urn:ietf:params:oauth:request_uri:${reference}` }) .send({ password: "foo" }); + const code = new URL(result.headers.location).searchParams.get("code"); + const decoded = verifyToken(code); assert.equal(result.status, 302); assert.match(result.headers.location, /code=(.*)&iss=(.*)&state=(.*)/); + assert.equal(decoded.me, "https://website.example/"); }); after(() => server.close());