Skip to content

Commit

Permalink
feat: allow native app callbacks in client post_logout_redirect_uris
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Apr 20, 2022
1 parent 3b2bcc5 commit 3fca22b
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 29 deletions.
28 changes: 11 additions & 17 deletions lib/helpers/client_schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
}

Expand All @@ -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;
Expand All @@ -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;
Expand Down
52 changes: 40 additions & 12 deletions test/configuration/client_metadata.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
[
Expand Down Expand Up @@ -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',
Expand Down

0 comments on commit 3fca22b

Please sign in to comment.