diff --git a/lib/helpers/client_schema.js b/lib/helpers/client_schema.js index db62dfc11..d6a877b41 100644 --- a/lib/helpers/client_schema.js +++ b/lib/helpers/client_schema.js @@ -545,13 +545,7 @@ module.exports = function getSchema(provider) { postLogoutRedirectUris() { if (this.post_logout_redirect_uris) { - this.post_logout_redirect_uris.forEach((uri) => { - try { - new url.URL(uri); // eslint-disable-line no-new - } catch (err) { - this.invalidate('post_logout_redirect_uris must only contain uris'); - } - }); + this.redirectUris(this.post_logout_redirect_uris, 'post_logout_redirect_uris'); } } @@ -575,35 +569,35 @@ module.exports = function getSchema(provider) { }); } - redirectUris() { - this.redirect_uris.forEach((redirectUri) => { + redirectUris(uris = this.redirect_uris, label = 'redirect_uris') { + uris.forEach((redirectUri) => { let hostname; let protocol; try { ({ hostname, protocol } = new url.URL(redirectUri)); } catch (err) { - this.invalidate('redirect_uris must only contain valid uris'); + this.invalidate(`${label} must only contain valid uris`); } const { hash } = url.parse(redirectUri); if (hash) { - this.invalidate('redirect_uris must not contain fragments'); + this.invalidate(`${label} must not contain fragments`); } switch (this.application_type) { // eslint-disable-line default-case case 'web': { if (!['https:', 'http:'].includes(protocol)) { - this.invalidate('redirect_uris must only contain web uris'); + this.invalidate(`${label} must only contain web uris`); } if (this.grant_types.includes('implicit')) { if (protocol === 'http:') { - this.invalidate('redirect_uris for web clients using implicit flow MUST only register URLs using the https scheme', 'implicit-force-https'); + this.invalidate(`${label} for web clients using implicit flow MUST only register URLs using the https scheme', 'implicit-force-https`); } if (hostname === 'localhost') { - this.invalidate('redirect_uris for web clients using implicit flow must not be using localhost', 'implicit-forbid-localhost'); + this.invalidate(`${label} for web clients using implicit flow must not be using localhost', 'implicit-forbid-localhost`); } } break; @@ -612,17 +606,17 @@ module.exports = function getSchema(provider) { switch (protocol) { case 'http:': // Loopback Interface Redirection if (!LOOPBACKS.has(hostname)) { - this.invalidate('redirect_uris for native clients using http as a protocol can only use loopback addresses as hostnames'); + this.invalidate(`${label} for native clients using http as a protocol can only use loopback addresses as hostnames`); } break; case 'https:': // Claimed HTTPS URI Redirection if (LOOPBACKS.has(hostname)) { - this.invalidate(`redirect_uris for native clients using claimed HTTPS URIs must not be using ${hostname} as hostname`); + this.invalidate(`${label} for native clients using claimed HTTPS URIs must not be using ${hostname} as hostname`); } break; default: // Private-use URI Scheme Redirection if (!protocol.includes('.')) { - this.invalidate('redirect_uris for native clients using Custom URI scheme should use reverse domain name based scheme'); + this.invalidate(`${label} for native clients using Custom URI scheme should use reverse domain name based scheme`); } } break; diff --git a/test/configuration/client_metadata.test.js b/test/configuration/client_metadata.test.js index 1c1f9688f..f0c56f7d6 100644 --- a/test/configuration/client_metadata.test.js +++ b/test/configuration/client_metadata.test.js @@ -372,6 +372,46 @@ describe('Client metadata validation', () => { }); }); + context('post_logout_redirect_uris', function () { + defaultsTo(this.title, [], undefined); + defaultsTo(this.title, [], { post_logout_redirect_uris: undefined }); + mustBeArray(this.title, [{}, 'string', 123, true]); + rejects(this.title, [123], /must only contain strings$/); + + allows(this.title, ['http://some'], { + application_type: 'web', + }); + allows(this.title, ['https://some'], { + application_type: 'web', + }); + rejects(this.title, ['https://rp.example.com#'], /post_logout_redirect_uris must not contain fragments$/); + rejects(this.title, ['https://rp.example.com#whatever'], /post_logout_redirect_uris must not contain fragments$/, { + application_type: 'web', + }); + rejects(this.title, ['no-dot-reverse-notation:/some'], undefined, { + application_type: 'web', + }); + rejects(this.title, ['https://localhost'], undefined, { + application_type: 'web', + grant_types: ['implicit', 'authorization_code'], + response_types: ['code id_token'], + }); + allows(this.title, ['http://localhost'], { + application_type: 'web', + }); + rejects(this.title, ['http://some'], undefined, { + application_type: 'native', + }); + rejects(this.title, ['not-a-uri'], undefined, { + application_type: 'native', + }); + rejects(this.title, ['http://foo/bar'], undefined, { + application_type: 'web', + grant_types: ['implicit'], + response_types: ['id_token'], + }); + }); + context('request_object_signing_alg', function () { mustBeString(this.title); [ @@ -476,18 +516,6 @@ describe('Client metadata validation', () => { rejects(this.title, 'not-a-type'); }); - context('post_logout_redirect_uris', function () { - defaultsTo(this.title, [], undefined); - defaultsTo(this.title, [], { post_logout_redirect_uris: undefined }); - mustBeArray(this.title, undefined); - - rejects(this.title, [123], /must only contain strings$/, undefined); - allows(this.title, ['http://a-web-uri'], undefined); - allows(this.title, ['https://a-web-uri'], undefined); - allows(this.title, ['any-custom-scheme://not-a-web-uri'], undefined); - rejects(this.title, ['not a uri'], /must only contain uris$/, undefined); - }); - [ 'token_endpoint_auth_method', 'introspection_endpoint_auth_method',