From b14cc82682b7c80f96acd30ffea2d42c48b7dd6b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Mar 2025 14:47:09 +0000 Subject: [PATCH] OIDC: only pass logo_uri, policy_uri, tos_uri if they conform to "common base" (#4748) * OIDC: only pass logo_uri, policy_uri, tos_uri if they conform to "common base" Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- spec/unit/oidc/register.spec.ts | 26 ++++++++++++++++++++++++-- src/oidc/register.ts | 21 +++++++++++++++++---- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/spec/unit/oidc/register.spec.ts b/spec/unit/oidc/register.spec.ts index 53124e0fd..ef72aa947 100644 --- a/spec/unit/oidc/register.spec.ts +++ b/spec/unit/oidc/register.spec.ts @@ -29,8 +29,8 @@ describe("registerOidcClient()", () => { redirectUris: [baseUrl], clientName, applicationType: "web", - tosUri: "http://tos-uri", - policyUri: "http://policy-uri", + tosUri: "https://just.testing/tos", + policyUri: "https://policy.just.testing", contacts: ["admin@example.com"], }; const dynamicClientId = "xyz789"; @@ -67,6 +67,8 @@ describe("registerOidcClient()", () => { id_token_signed_response_alg: "RS256", token_endpoint_auth_method: "none", application_type: "web", + tos_uri: "https://just.testing/tos", + policy_uri: "https://policy.just.testing", }), ); }); @@ -114,4 +116,24 @@ describe("registerOidcClient()", () => { ), ).rejects.toThrow(OidcError.DynamicRegistrationNotSupported); }); + + it("should filter out invalid URIs", async () => { + fetchMockJest.post(delegatedAuthConfig.registration_endpoint!, { + status: 200, + body: JSON.stringify({ client_id: dynamicClientId }), + }); + expect( + await registerOidcClient(delegatedAuthConfig, { + ...metadata, + tosUri: "http://just.testing/tos", + policyUri: "https://policy-uri/", + }), + ).toEqual(dynamicClientId); + expect(JSON.parse(fetchMockJest.mock.calls[0][1]!.body as string)).not.toEqual( + expect.objectContaining({ + tos_uri: "http://just.testing/tos", + policy_uri: "https://policy-uri/", + }), + ); + }); }); diff --git a/src/oidc/register.ts b/src/oidc/register.ts index eeed5ef07..22b70c0a0 100644 --- a/src/oidc/register.ts +++ b/src/oidc/register.ts @@ -54,8 +54,18 @@ interface OidcRegistrationRequestBody { export const DEVICE_CODE_SCOPE = "urn:ietf:params:oauth:grant-type:device_code"; +// Check that URIs have a common base, as per the MSC2966 definition +const urlHasCommonBase = (base: URL, urlStr?: string): boolean => { + if (!urlStr) return false; + const url = new URL(urlStr); + if (url.protocol !== base.protocol) return false; + if (url.host !== base.host && !url.host.endsWith(`.${base.host}`)) return false; + return true; +}; + /** - * Attempts dynamic registration against the configured registration endpoint + * Attempts dynamic registration against the configured registration endpoint. + * Will ignore any URIs that do not use client_uri as a common base as per the spec. * @param delegatedAuthConfig - Auth config from {@link discoverAndValidateOIDCIssuerWellKnown} * @param clientMetadata - The metadata for the client which to register * @returns Promise resolved with registered clientId @@ -74,6 +84,8 @@ export const registerOidcClient = async ( throw new Error(OidcError.DynamicRegistrationNotSupported); } + const commonBase = new URL(clientMetadata.clientUri); + // https://openid.net/specs/openid-connect-registration-1_0.html const metadata: OidcRegistrationRequestBody = { client_name: clientMetadata.clientName, @@ -84,11 +96,12 @@ export const registerOidcClient = async ( id_token_signed_response_alg: "RS256", token_endpoint_auth_method: "none", application_type: clientMetadata.applicationType, - logo_uri: clientMetadata.logoUri, contacts: clientMetadata.contacts, - policy_uri: clientMetadata.policyUri, - tos_uri: clientMetadata.tosUri, + logo_uri: urlHasCommonBase(commonBase, clientMetadata.logoUri) ? clientMetadata.logoUri : undefined, + policy_uri: urlHasCommonBase(commonBase, clientMetadata.policyUri) ? clientMetadata.policyUri : undefined, + tos_uri: urlHasCommonBase(commonBase, clientMetadata.tosUri) ? clientMetadata.tosUri : undefined, }; + const headers = { "Accept": "application/json", "Content-Type": "application/json",