oidc: do not notify login of ghost users
This commit is contained in:
@@ -390,7 +390,6 @@ async function interactionLogin(req, res, next) {
|
||||
}
|
||||
|
||||
const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress || null;
|
||||
const userAgent = req.headers['user-agent'] || '';
|
||||
const clientId = details.params.client_id;
|
||||
|
||||
debug(`interactionLogin: for OpenID client ${clientId} from ${ip}`);
|
||||
@@ -414,12 +413,7 @@ async function interactionLogin(req, res, next) {
|
||||
const [interactionFinishError, redirectTo] = await safe(gOidcProvider.interactionResult(req, res, result));
|
||||
if (interactionFinishError) return next(new HttpError(500, interactionFinishError));
|
||||
|
||||
const auditSource = AuditSource.fromOidcRequest(req);
|
||||
await eventlog.add(user.ghost ? eventlog.ACTION_USER_LOGIN_GHOST : eventlog.ACTION_USER_LOGIN, auditSource, { userId: user.id, user: users.removePrivateFields(user), appId: clientId });
|
||||
await safe(users.notifyLoginLocation(user, ip, userAgent, auditSource), { debug });
|
||||
|
||||
// clear token as it is one-time use
|
||||
await tokens.delByAccessToken(req.body.autoLoginToken);
|
||||
await tokens.delByAccessToken(req.body.autoLoginToken); // clear token as it is one-time use
|
||||
|
||||
return res.status(200).send({ redirectTo });
|
||||
}
|
||||
@@ -438,11 +432,12 @@ async function interactionLogin(req, res, next) {
|
||||
if (verifyError) return next(new HttpError(500, verifyError));
|
||||
if (!user) return next(new HttpError(401, 'Username and password does not match'));
|
||||
|
||||
// TODO we may have to check what else the Account class provides, in which case we have to map those things
|
||||
// this is saved as part of interaction.lastSubmission
|
||||
const result = {
|
||||
login: {
|
||||
accountId: user.id,
|
||||
},
|
||||
ghost: !!user.ghost
|
||||
};
|
||||
|
||||
const [interactionFinishError, redirectTo] = await safe(gOidcProvider.interactionResult(req, res, result));
|
||||
@@ -452,81 +447,68 @@ async function interactionLogin(req, res, next) {
|
||||
}
|
||||
|
||||
async function interactionConfirm(req, res, next) {
|
||||
async function raiseLoginEvent(user, clientId) {
|
||||
try {
|
||||
const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress || null;
|
||||
const userAgent = req.headers['user-agent'] || '';
|
||||
const auditSource = AuditSource.fromOidcRequest(req);
|
||||
const interactionDetails = await gOidcProvider.interactionDetails(req, res);
|
||||
const { grantId, uid, prompt: { name, details }, params, session: { accountId }, lastSubmission } = interactionDetails;
|
||||
|
||||
await eventlog.add(user.ghost ? eventlog.ACTION_USER_LOGIN_GHOST : eventlog.ACTION_USER_LOGIN, auditSource, { userId: user.id, user: users.removePrivateFields(user), appId: clientId });
|
||||
await users.notifyLoginLocation(user, ip, userAgent, auditSource);
|
||||
} catch (e) {
|
||||
console.error('oidc: Failed to raise login event.', e);
|
||||
debug(`route interaction confirm post uid:${uid} prompt.name:${name} accountId:${accountId}`);
|
||||
|
||||
const client = await oidcClients.get(params.client_id);
|
||||
if (!client) return next(new Error('Client not found'));
|
||||
|
||||
const user = await users.get(accountId);
|
||||
if (!user) return next(new Error('User not found'));
|
||||
user.ghost = lastSubmission.ghost; // restore ghost flag
|
||||
|
||||
// Check if user has access to the app if client refers to an app
|
||||
if (client.appId) {
|
||||
const app = await apps.get(client.appId);
|
||||
if (!app) return next(new Error('App not found'));
|
||||
|
||||
if (!apps.canAccess(app, user)) {
|
||||
const result = {
|
||||
error: 'access_denied',
|
||||
error_description: 'User has no access to this app',
|
||||
};
|
||||
|
||||
return await gOidcProvider.interactionFinished(req, res, result, { mergeWithLastSubmission: false });
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const interactionDetails = await gOidcProvider.interactionDetails(req, res);
|
||||
const { grantId, uid, prompt: { name, details }, params, session: { accountId } } = interactionDetails;
|
||||
|
||||
debug(`route interaction confirm post uid:${uid} prompt.name:${name} accountId:${accountId}`);
|
||||
|
||||
assert.equal(name, 'consent');
|
||||
|
||||
const client = await oidcClients.get(params.client_id);
|
||||
const user = await users.get(accountId);
|
||||
|
||||
// Check if user has access to the app if client refers to an app
|
||||
// In most cases the user interaction already ends in the consent screen (see above)
|
||||
if (client.appId) {
|
||||
const app = await apps.get(client.appId);
|
||||
|
||||
if (!apps.canAccess(app, user)) {
|
||||
const result = {
|
||||
error: 'access_denied',
|
||||
error_description: 'User has no access to this app',
|
||||
};
|
||||
|
||||
await raiseLoginEvent(user, client.appId);
|
||||
|
||||
return await gOidcProvider.interactionFinished(req, res, result, { mergeWithLastSubmission: false });
|
||||
}
|
||||
}
|
||||
|
||||
let grant;
|
||||
if (grantId) {
|
||||
grant = await gOidcProvider.Grant.find(grantId);
|
||||
} else {
|
||||
grant = new gOidcProvider.Grant({
|
||||
accountId,
|
||||
clientId: params.client_id,
|
||||
});
|
||||
}
|
||||
|
||||
if (details.missingOIDCScope) {
|
||||
grant.addOIDCScope(details.missingOIDCScope.join(' '));
|
||||
}
|
||||
if (details.missingOIDCClaims) {
|
||||
grant.addOIDCClaims(details.missingOIDCClaims);
|
||||
}
|
||||
if (details.missingResourceScopes) {
|
||||
for (const [indicator, scopes] of Object.entries(details.missingResourceScopes)) {
|
||||
grant.addResourceScope(indicator, scopes.join(' '));
|
||||
}
|
||||
}
|
||||
|
||||
const savedGrantId = await grant.save();
|
||||
|
||||
const consent = {};
|
||||
if (!interactionDetails.grantId) consent.grantId = savedGrantId;
|
||||
|
||||
await raiseLoginEvent(user, params.client_id);
|
||||
|
||||
const result = { consent };
|
||||
await gOidcProvider.interactionFinished(req, res, result, { mergeWithLastSubmission: true });
|
||||
} catch (err) {
|
||||
next(err);
|
||||
let grant;
|
||||
if (grantId) {
|
||||
grant = await gOidcProvider.Grant.find(grantId);
|
||||
} else {
|
||||
grant = new gOidcProvider.Grant({
|
||||
accountId,
|
||||
clientId: params.client_id,
|
||||
});
|
||||
}
|
||||
|
||||
// just confirm everything
|
||||
if (details.missingOIDCScope) grant.addOIDCScope(details.missingOIDCScope.join(' '));
|
||||
if (details.missingOIDCClaims) grant.addOIDCClaims(details.missingOIDCClaims);
|
||||
|
||||
if (details.missingResourceScopes) {
|
||||
for (const [indicator, scopes] of Object.entries(details.missingResourceScopes)) {
|
||||
grant.addResourceScope(indicator, scopes.join(' '));
|
||||
}
|
||||
}
|
||||
|
||||
const savedGrantId = await grant.save();
|
||||
|
||||
const consent = {};
|
||||
if (!interactionDetails.grantId) consent.grantId = savedGrantId;
|
||||
|
||||
// create login event
|
||||
const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress || null;
|
||||
const userAgent = req.headers['user-agent'] || '';
|
||||
const auditSource = AuditSource.fromOidcRequest(req);
|
||||
|
||||
await eventlog.add(user.ghost ? eventlog.ACTION_USER_LOGIN_GHOST : eventlog.ACTION_USER_LOGIN, auditSource, { userId: user.id, user: users.removePrivateFields(user), appId: params.client_id });
|
||||
await users.notifyLoginLocation(user, ip, userAgent, auditSource);
|
||||
|
||||
const result = { consent };
|
||||
await gOidcProvider.interactionFinished(req, res, result, { mergeWithLastSubmission: true });
|
||||
}
|
||||
|
||||
async function interactionAbort(req, res, next) {
|
||||
|
||||
Reference in New Issue
Block a user