It looks like that is possible to access organization from disabled application which is not enabled for a particular connection (but it is potentially be enabled for a user, but not for particular organization).
It means that organization can be accessed via disabled application which is potential security issue.
Steps to reproduce:
- Create a new application eg. Single Page Web Application with
test-app
name; - Create a new connection eg. Database Connections with
test-basic
name; - Disable all applications for
test-basic
connection includingtest-app
one; - Crate a new Organization with
test-org
name and enabletest-basic
connection; - Try to sign in into
test-org
by usingtest-basic
connection andtest-app
application.
Workaround:
- Create a new Actions to fix that issue.
const https = require('https');
function request(opts, data) {
return new Promise((resolve, reject) => {
const req = https.request(opts, (res) => {
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(new Error(res.statusCode.toString()));
}
const body = [];
res.setEncoding('utf8');
res.on('data', (chunk) => {
body.push(Buffer.from(chunk));
});
res.on('end', () => {
try {
const data = JSON.parse(Buffer.concat(body).toString());
resolve(data);
} catch (e) {
reject(e);
}
});
res.on('error', (err) => {
reject(err);
});
});
if (data) {
req.write(Buffer.from(data));
}
req.end();
});
}
/**
* Handler that will be called during the execution of a PostLogin flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
exports.onExecutePostLogin = async (event, api) => {
const tokenData = JSON.stringify({
grant_type: 'client_credentials',
client_id: event.secrets.AUTH0_CLIENT_ID,
client_secret: event.secrets.AUTH0_CLIENT_SECRET,
audience: `https://${event.secrets.AUTH0_DOMAIN}/api/v2/`,
});
const token = await request(
{
hostname: event.secrets.AUTH0_DOMAIN,
path: '/oauth/token',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(tokenData),
},
},
tokenData
);
const connection = await request({
hostname: event.secrets.AUTH0_DOMAIN,
path: `/api/v2/connections/${event.connection.id}?fields=enabled_clients&include_fields=true`,
method: 'GET',
headers: {
Authorization: `${token.token_type} ${token.access_token}`,
'Content-Type': 'application/json',
},
});
const clientId = event.client.client_id;
const appName = event.client.metadata.name ?? event.client.name;
const orgName =
event.organization?.metadata.name ?? event.organization?.display_name;
const isEnabled = connection.enabled_clients.includes(clientId);
if (!isEnabled) {
api.access.deny(
`"${appName}" application is not allowed for "${orgName}" organization.`
);
}
};
/**
* Handler that will be invoked when this action is resuming after an external redirect. If your
* onExecutePostLogin function does not perform a redirect, this function can be safely ignored.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
exports.onContinuePostLogin = async () => {};