Skip to content

Commit

Permalink
fix: registered native loopback redirect_uris do not get normalized
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Dec 2, 2019
1 parent d7b927d commit 96e035f
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 34 deletions.
2 changes: 1 addition & 1 deletion lib/consts/client_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ const HTTPS_URI = [
'sector_identifier_uri',
];

const LOOPBACKS = ['localhost', '127.0.0.1', '[::1]'];
const LOOPBACKS = new Set(['localhost', '127.0.0.1', '[::1]']);

const ENUM = {
application_type: () => ['native', 'web'],
Expand Down
20 changes: 2 additions & 18 deletions lib/helpers/client_schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ module.exports = function getSchema(provider) {
this.postLogoutRedirectUris();
this.redirectUris();
this.webMessageUris();
this.normalizeNativeAppUris();
this.checkContacts();

// max_age and client_secret_expires_at format
Expand Down Expand Up @@ -473,21 +472,6 @@ module.exports = function getSchema(provider) {
this.response_types = this.response_types.map((type) => [...new Set(type.split(' '))].sort().join(' '));
}

normalizeNativeAppUris() {
if (this.application_type === 'web') return;

this.redirect_uris = this.redirect_uris.map((redirectUri) => {
const parsed = new url.URL(redirectUri);
// remove the port component, making dynamic ports allowed for loopback uris
if (parsed.protocol === 'http:' && LOOPBACKS.includes(parsed.hostname)) {
parsed.port = 80; // http + 80 = no port part in the string
return parsed.href;
}

return redirectUri;
});
}

postLogoutRedirectUris() {
this.post_logout_redirect_uris.forEach((uri) => {
try {
Expand Down Expand Up @@ -554,12 +538,12 @@ module.exports = function getSchema(provider) {
case 'native': {
switch (protocol) {
case 'http:': // Loopback Interface Redirection
if (!LOOPBACKS.includes(hostname)) {
if (!LOOPBACKS.has(hostname)) {
this.invalidate('redirect_uris for native clients using http as a protocol can only use loopback addresses as hostnames');
}
break;
case 'https:': // Claimed HTTPS URI Redirection
if (LOOPBACKS.includes(hostname)) {
if (LOOPBACKS.has(hostname)) {
this.invalidate(`redirect_uris for native clients using claimed HTTPS URIs must not be using ${hostname} as hostname`);
}
break;
Expand Down
34 changes: 22 additions & 12 deletions lib/models/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -436,22 +436,32 @@ module.exports = function getClient(provider) {
return this.grantTypes.includes(type);
}

redirectUriAllowed(redirectUri) {
let checkedUri = redirectUri;
redirectUriAllowed(value) {
let parsed;
try {
parsed = new URL(value);
} catch (err) {
return false;
}

const match = this.redirectUris.includes(value);
if (
this.applicationType === 'native'
&& redirectUri.startsWith('http:')
match
|| this.applicationType !== 'native'
|| !parsed.protocol === 'http:'
|| !LOOPBACKS.has(parsed.hostname)
) {
try {
const parsed = new URL(redirectUri);
if (LOOPBACKS.includes(parsed.hostname)) {
parsed.port = 80;
checkedUri = parsed.href;
}
} catch (err) {}
return match;
}

return this.redirectUris.includes(checkedUri);
parsed.port = '';

return !!this.redirectUris
.find((registeredUri) => {
const registered = new URL(registeredUri);
registered.port = '';
return parsed.href === registered.href;
});
}

checkSessionOriginAllowed(origin) {
Expand Down
6 changes: 3 additions & 3 deletions test/oauth_native_apps/oauth_native_apps.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ describe('OAuth 2.0 for Native Apps Best Current Practice features', () => {
token_endpoint_auth_method: 'none',
redirect_uris: ['http://localhost:2355/op/callback'],
}).then((client) => {
expect(client.redirectUris).to.contain('http://localhost/op/callback');
expect(client.redirectUris).to.contain('http://localhost:2355/op/callback');
expect(client.redirectUriAllowed('http://localhost/op/callback')).to.be.true;
expect(client.redirectUriAllowed('http://localhost:80/op/callback')).to.be.true;
expect(client.redirectUriAllowed('http://localhost:443/op/callback')).to.be.true;
Expand Down Expand Up @@ -123,7 +123,7 @@ describe('OAuth 2.0 for Native Apps Best Current Practice features', () => {
token_endpoint_auth_method: 'none',
redirect_uris: ['http://127.0.0.1:2355/op/callback'],
}).then((client) => {
expect(client.redirectUris).to.contain('http://127.0.0.1/op/callback');
expect(client.redirectUris).to.contain('http://127.0.0.1:2355/op/callback');
expect(client.redirectUriAllowed('http://127.0.0.1/op/callback')).to.be.true;
expect(client.redirectUriAllowed('http://127.0.0.1:80/op/callback')).to.be.true;
expect(client.redirectUriAllowed('http://127.0.0.1:443/op/callback')).to.be.true;
Expand Down Expand Up @@ -159,7 +159,7 @@ describe('OAuth 2.0 for Native Apps Best Current Practice features', () => {
token_endpoint_auth_method: 'none',
redirect_uris: ['http://[::1]:2355/op/callback'],
}).then((client) => {
expect(client.redirectUris).to.contain('http://[::1]/op/callback');
expect(client.redirectUris).to.contain('http://[::1]:2355/op/callback');
expect(client.redirectUriAllowed('http://[::1]/op/callback')).to.be.true;
expect(client.redirectUriAllowed('http://[::1]:80/op/callback')).to.be.true;
expect(client.redirectUriAllowed('http://[::1]:443/op/callback')).to.be.true;
Expand Down

0 comments on commit 96e035f

Please sign in to comment.