the oidc module expect accountId and sub to be the same
in our case sub is the username exposed to the app, not the userId
internal to Cloudron
Upstream behavior change 9b89153c0e
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
exports = module.exports = {
|
||||
start,
|
||||
stop,
|
||||
revokeByUserId,
|
||||
revokeByUsername,
|
||||
consumeAuthCode,
|
||||
|
||||
cleanupExpired,
|
||||
@@ -96,7 +96,11 @@ class StorageAdapter {
|
||||
if (this.name === 'AccessToken' && (payload.clientId === oidcClients.ID_WEBADMIN || payload.clientId === oidcClients.ID_DEVELOPMENT)) {
|
||||
const expires = Date.now() + constants.DEFAULT_TOKEN_EXPIRATION_MSECS;
|
||||
|
||||
const [error] = await safe(tokens.add({ clientId: payload.clientId, identifier: payload.accountId, expires, accessToken: id, allowedIpRanges: '' }));
|
||||
// oidc uses the username as accountId but accesstoken identifiers are userIds
|
||||
const user = await users.getByUsername(payload.accountId);
|
||||
if (!user) throw new Error(`user for username ${payload.accountId} not found`);
|
||||
|
||||
const [error] = await safe(tokens.add({ clientId: payload.clientId, identifier: user.id, expires, accessToken: id, allowedIpRanges: '' }));
|
||||
if (error) {
|
||||
console.log('Error adding access token', error);
|
||||
throw error;
|
||||
@@ -150,10 +154,14 @@ class StorageAdapter {
|
||||
// dashboard AccessToken are in the db. the app tokens are in the json files
|
||||
const [error, result] = await safe(tokens.getByAccessToken(id));
|
||||
if (!error && result) {
|
||||
return {
|
||||
accountId: result.identifier,
|
||||
clientId: result.clientId
|
||||
};
|
||||
// translate from userId in the token to username for oidc
|
||||
const user = await users.get(result.identifier);
|
||||
if (user) {
|
||||
return {
|
||||
accountId: user.username,
|
||||
clientId: result.clientId
|
||||
};
|
||||
}
|
||||
}
|
||||
} else if (this.name === 'Session') {
|
||||
const data = await StorageAdapter.getData(this.name);
|
||||
@@ -162,7 +170,7 @@ class StorageAdapter {
|
||||
|
||||
if (session.payload.accountId) {
|
||||
// check if the session user still exists and is active
|
||||
const user = await users.get(session.payload.accountId);
|
||||
const user = await users.getByUsername(session.payload.accountId);
|
||||
if (!user || !user.active) return null;
|
||||
}
|
||||
|
||||
@@ -217,14 +225,14 @@ class StorageAdapter {
|
||||
}
|
||||
|
||||
// Session, Grant and Token management. This is based on the same storage as the below CloudronAdapter
|
||||
async function revokeByUserId(userId) {
|
||||
assert.strictEqual(typeof userId, 'string');
|
||||
async function revokeByUsername(username) {
|
||||
assert.strictEqual(typeof username, 'string');
|
||||
|
||||
const types = [ 'Session', 'Grant', 'AuthorizationCode', 'AccessToken' ];
|
||||
for (const type of types) {
|
||||
await StorageAdapter.updateData(type, (data) => {
|
||||
for (const id in data) {
|
||||
if (data[id].payload?.accountId === userId) delete data[id];
|
||||
if (data[id].payload?.accountId === username) delete data[id];
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -321,7 +329,7 @@ async function renderInteractionPage(req, res, next) {
|
||||
|
||||
// check if user has access to the app if client refers to an app
|
||||
if (app) {
|
||||
const user = await users.get(session.accountId);
|
||||
const user = await users.getByUsername(session.accountId);
|
||||
|
||||
options.NAME = app.label || app.fqdn;
|
||||
options.ICON_URL = app.iconUrl;
|
||||
@@ -356,13 +364,13 @@ async function interactionLogin(req, res, next) {
|
||||
const token = await tokens.getByAccessToken(req.body.autoLoginToken);
|
||||
if (!token) return next(new HttpError(401, 'No such token'));
|
||||
|
||||
const user = await users.get(token.identifier);
|
||||
const user = await users.getByUsername(token.identifier);
|
||||
if (!user) return next(new HttpError(401,'User not found'));
|
||||
if (!user.active) return next(new HttpError(401,'User not active'));
|
||||
|
||||
const result = {
|
||||
login: {
|
||||
accountId: user.id,
|
||||
accountId: user.username,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -391,7 +399,7 @@ async function interactionLogin(req, res, next) {
|
||||
// this is saved as part of interaction.lastSubmission
|
||||
const result = {
|
||||
login: {
|
||||
accountId: user.id,
|
||||
accountId: user.username,
|
||||
},
|
||||
ghost: !!user.ghost
|
||||
};
|
||||
@@ -411,7 +419,7 @@ async function interactionConfirm(req, res, next) {
|
||||
const client = await oidcClients.get(params.client_id);
|
||||
if (!client) return next(new Error('Client not found'));
|
||||
|
||||
const user = await users.get(accountId);
|
||||
const user = await users.getByUsername(accountId);
|
||||
if (!user) return next(new Error('User not found'));
|
||||
user.ghost = !!lastSubmission?.ghost; // restore ghost flag. lastSubmission can be empty if login interaction was skipped (already logged in)
|
||||
|
||||
@@ -476,8 +484,8 @@ async function interactionAbort(req, res, next) {
|
||||
if (error) return next(error);
|
||||
}
|
||||
|
||||
async function getClaims(userId/*, use, scope*/) {
|
||||
const [error, user] = await safe(users.get(userId));
|
||||
async function getClaims(username/*, use, scope*/) {
|
||||
const [error, user] = await safe(users.getByUsername(username));
|
||||
if (error) return { error: 'user not found' };
|
||||
|
||||
const [groupsError, allGroups] = await safe(groups.listWithMembers());
|
||||
|
||||
Reference in New Issue
Block a user