Skip to content

Commit a88bb11

Browse files
chenbadschom
authored andcommitted
chore(security events): add the session.destroy security event
Because: - we want to know when a user signs out of a session This commit: - adds the 'session.destroy' security event - updates the inactive account deletion script to query for the event
1 parent 39047a2 commit a88bb11

File tree

9 files changed

+47
-5
lines changed

9 files changed

+47
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
SET NAMES utf8mb4 COLLATE utf8mb4_bin;
2+
3+
CALL assertPatchLevel('161');
4+
5+
INSERT INTO securityEventNames(name) VALUES ('session.destroy');
6+
7+
UPDATE dbMetadata SET value = '162' WHERE name = 'schema-patch-level';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- DELETE FROM securityEventNames WHERE name='session.destroy';
2+
3+
-- UPDATE dbMetadata SET value = '161' WHERE name = 'schema-patch-level';
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"level": 161
2+
"level": 162
33
}

packages/fxa-auth-server/lib/inactive-accounts/active-status.ts

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export const securityEventsQuery = (uid, activeByDateTimestamp) =>
6060
EVENT_NAMES['account.login'],
6161
EVENT_NAMES['account.password_reset_success'],
6262
EVENT_NAMES['account.password_changed'],
63+
EVENT_NAMES['session.destroy'],
6364
])
6465
.limit(1)
6566
.first();

packages/fxa-auth-server/lib/routes/account.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -584,11 +584,11 @@ export class AccountHandler {
584584
});
585585
}
586586

587-
await this.db.securityEvent({
588-
ipAddr: request.app.clientAddress,
587+
await this.accountEventsManager.recordSecurityEvent(this.db, {
589588
name: 'account.create',
590-
tokenId: sessionToken.id,
591589
uid: account.uid,
590+
ipAddr: request.app.clientAddress,
591+
tokenId: sessionToken.id,
592592
});
593593

594594
return this.accountCreateResponse({

packages/fxa-auth-server/lib/routes/session.js

+9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
'use strict';
66

7+
const { Container } = require('typedi');
78
const error = require('../error');
89
const isA = require('joi');
910
const requestHelper = require('../routes/utils/request_helper');
@@ -15,6 +16,7 @@ const NodeRendererBindings =
1516
const SESSION_DOCS = require('../../docs/swagger/session-api').default;
1617
const DESCRIPTION = require('../../docs/swagger/shared/descriptions').default;
1718
const HEX_STRING = validators.HEX_STRING;
19+
const { AccountEventsManager } = require('../account-events');
1820

1921
module.exports = function (
2022
log,
@@ -41,6 +43,7 @@ module.exports = function (
4143
);
4244

4345
const otpOptions = config.otp;
46+
const accountEventsManager = Container.get(AccountEventsManager);
4447

4548
const routes = [
4649
{
@@ -89,6 +92,12 @@ module.exports = function (
8992
}
9093

9194
await db.deleteSessionToken(sessionToken);
95+
await accountEventsManager.recordSecurityEvent(db, {
96+
name: 'session.destroy',
97+
uid,
98+
ipAddr: request.app.clientAddress,
99+
tokenId: sessionToken.id,
100+
});
92101

93102
return {};
94103
},

packages/fxa-auth-server/scripts/delete-inactive-accounts/lib.ts

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export const securityEventUidsQuery = (activeByDateTimestamp) =>
3737
EVENT_NAMES['account.login'],
3838
EVENT_NAMES['account.password_reset_success'],
3939
EVENT_NAMES['account.password_changed'],
40+
EVENT_NAMES['session.destroy'],
4041
])
4142
.as('securityEventUids');
4243

packages/fxa-auth-server/test/local/routes/session.js

+21-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ const sinon = require('sinon');
1313
const otplib = require('otplib');
1414
const assert = require('../../assert');
1515
const gleanMock = mocks.mockGlean();
16+
const { Container } = require('typedi');
17+
const { AccountEventsManager } = require('../../../lib/account-events');
1618

1719
const ROOT_DIR = '../../..';
1820

@@ -65,6 +67,11 @@ function makeRoutes(options = {}) {
6567
const glean = options.glean || gleanMock;
6668
const statsd = options.statsd || mocks.mockStatsd();
6769

70+
Container.set(
71+
AccountEventsManager,
72+
options.accountEventsManager || { recordSecurityEvent: sinon.stub() }
73+
);
74+
6875
const Password =
6976
options.Password || require('../../../lib/crypto/password')(log, config);
7077
const customs = options.customs || {
@@ -715,12 +722,19 @@ describe('/session/destroy', () => {
715722
let request;
716723
let log;
717724
let db;
725+
let securityEventStub;
718726

719727
beforeEach(() => {
720728
db = mocks.mockDB();
721729
log = mocks.mockLog();
722730
const config = {};
723-
const routes = makeRoutes({ log, config, db });
731+
securityEventStub = sinon.stub();
732+
const routes = makeRoutes({
733+
log,
734+
config,
735+
db,
736+
accountEventsManager: { recordSecurityEvent: securityEventStub },
737+
});
724738
route = getRoute(routes, '/session/destroy');
725739
request = mocks.mockRequest({
726740
credentials: {
@@ -734,6 +748,12 @@ describe('/session/destroy', () => {
734748
it('responds correctly when session is destroyed', () => {
735749
return runTest(route, request).then((res) => {
736750
assert.equal(Object.keys(res).length, 0);
751+
sinon.assert.calledOnceWithExactly(securityEventStub, db, {
752+
name: 'session.destroy',
753+
uid: 'foo',
754+
ipAddr: '63.245.221.32',
755+
tokenId: undefined,
756+
});
737757
});
738758
});
739759

packages/fxa-shared/db/models/auth/security-event.ts

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export const EVENT_NAMES = {
3737
'account.primary_secondary_swapped': 23,
3838
'account.password_reset_otp_sent': 24,
3939
'account.password_reset_otp_verified': 25,
40+
'session.destroy': 26,
4041
} as const;
4142

4243
export type SecurityEventNames = keyof typeof EVENT_NAMES;

0 commit comments

Comments
 (0)