From 2dbc30b957dd7a934d35e4627ba61c4bc74a4616 Mon Sep 17 00:00:00 2001 From: Zwifi Date: Wed, 26 Jan 2022 22:20:00 +0100 Subject: [PATCH] Remove the URL query parameters from the HTU field This fixes issue #1842. The HTU field is now build by addition rather than by substraction: the origin and path from the target URL are concatenated to build the DPoP header `htu` claim, rather than stripping elements from the target URL. Co-authored-by: Diego Albuquerque --- CHANGELOG.md | 5 ++++- .../core/src/authenticatedFetch/dpopUtils.spec.ts | 13 +++++++++++++ packages/core/src/authenticatedFetch/dpopUtils.ts | 11 ++++------- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8591e101e..2da8741e75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,10 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html The following changes have been implemented but not released yet: -- +### Bugfixes + +- The HTU field of the DPoP header is now normalized to remove the query parameters. +Thanks to @diegoaraujo for his first contribution to the project! ## 1.11.3 - 2021-08-24 diff --git a/packages/core/src/authenticatedFetch/dpopUtils.spec.ts b/packages/core/src/authenticatedFetch/dpopUtils.spec.ts index 700015f772..529bc2fea0 100644 --- a/packages/core/src/authenticatedFetch/dpopUtils.spec.ts +++ b/packages/core/src/authenticatedFetch/dpopUtils.spec.ts @@ -66,6 +66,19 @@ describe("createDpopHeader", () => { expect(payload.htu).toBe("https://some.resource/"); }); + it("creates a JWT with 'htu' that needs to be normalized", async () => { + const header = await createDpopHeader( + "https://user:pass@some.resource/?query#hash", + "GET", + await mockKeyPair() + ); + const { payload } = await jwtVerify(header, (await mockJwk()).publicKey); + expect(payload.htm).toBe("GET"); + expect(payload.jti).toBeDefined(); + // The IRI is normalized, hence the trailing '/' + expect(payload.htu).toBe("https://some.resource/"); + }); + it("creates a JWT with the appropriate protected header", async () => { const header = await createDpopHeader( "https://some.resource", diff --git a/packages/core/src/authenticatedFetch/dpopUtils.ts b/packages/core/src/authenticatedFetch/dpopUtils.ts index 4374d9a6fb..10177b5c0f 100644 --- a/packages/core/src/authenticatedFetch/dpopUtils.ts +++ b/packages/core/src/authenticatedFetch/dpopUtils.ts @@ -30,12 +30,9 @@ import { PREFERRED_SIGNING_ALG } from "../constant"; * @returns The normalized URL as a string. * @hidden */ -function removeHashUsernameAndPassword(audience: string): string { - const cleanedAudience = new URL(audience); - cleanedAudience.hash = ""; - cleanedAudience.username = ""; - cleanedAudience.password = ""; - return cleanedAudience.toString(); +function normalizeHTU(audience: string): string { + const audienceUrl = new URL(audience); + return new URL(audienceUrl.pathname, audienceUrl.origin).toString(); } export type KeyPair = { @@ -58,7 +55,7 @@ export async function createDpopHeader( dpopKey: KeyPair ): Promise { return new SignJWT({ - htu: removeHashUsernameAndPassword(audience), + htu: normalizeHTU(audience), htm: method.toUpperCase(), jti: v4(), })