Skip to content

Commit 81e8cba

Browse files
authored
Merge pull request #18922 from mozilla/310_12-backport
bug(session): Ensure that the session matches account on destroy
2 parents 076c489 + d30ab52 commit 81e8cba

2 files changed

Lines changed: 33 additions & 2 deletions

File tree

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1815,12 +1815,22 @@ export class AccountHandler {
18151815
this.log.begin('Account.destroy', request);
18161816

18171817
const { authPW, email: emailAddress } = request.payload as any;
1818+
const sessionToken = request.auth && request.auth.credentials as unknown as {
1819+
uid: string;
1820+
email: string;
1821+
tokenVerified: boolean;
1822+
tokenVerificationId?: string;
1823+
authenticatorAssuranceLevel?: number;
1824+
}
18181825

18191826
await this.customs.check(request, emailAddress, 'accountDestroy');
18201827

18211828
let accountRecord: Account;
18221829
try {
18231830
accountRecord = await this.db.accountRecord(emailAddress);
1831+
if (accountRecord.uid !== sessionToken.uid) {
1832+
throw error.unknownAccount(emailAddress);
1833+
}
18241834
} catch (err) {
18251835
if (err.errno === error.ERRNO.ACCOUNT_UNKNOWN) {
18261836
await this.customs.flag(request.app.clientAddress, {
@@ -1832,7 +1842,6 @@ export class AccountHandler {
18321842
throw err;
18331843
}
18341844

1835-
const sessionToken = request.auth && request.auth.credentials;
18361845
const hasTotpToken = await this.otpUtils.hasTotpToken(accountRecord);
18371846

18381847
// Someone tried to delete an account with TOTP but did not specify a session.

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3847,13 +3847,14 @@ describe('/account/destroy', () => {
38473847
const tokenVerified = true;
38483848
const uid = uuid.v4({}, Buffer.alloc(16)).toString('hex');
38493849

3850-
let mockDB, mockLog, mockRequest, mockPush, mockPushbox;
3850+
let mockDB, mockLog, mockRequest, mockPush, mockPushbox, mockCustoms;
38513851

38523852
beforeEach(async () => {
38533853
mockDB = {
38543854
...mocks.mockDB({ email: email, uid: uid }),
38553855
};
38563856
mockLog = mocks.mockLog();
3857+
mockCustoms = mocks.mockCustoms();
38573858
mockRequest = mocks.mockRequest({
38583859
credentials: { uid, email, tokenVerified },
38593860
log: mockLog,
@@ -3889,6 +3890,7 @@ describe('/account/destroy', () => {
38893890
log: mockLog,
38903891
push: mockPush,
38913892
pushbox: mockPushbox,
3893+
customs: mockCustoms
38923894
});
38933895
return getRoute(accountRoutes, '/account/destroy');
38943896
}
@@ -3969,6 +3971,26 @@ describe('/account/destroy', () => {
39693971
});
39703972
});
39713973
});
3974+
3975+
it('should fail for mismatch session and account ui', async () => {
3976+
mockDB = { ...mocks.mockDB({ email, uid }) };
3977+
mockRequest = mocks.mockRequest({
3978+
credentials: { uid: 'anotherone', email: `[email protected]`, tokenVerified },
3979+
log: mockLog,
3980+
payload: {
3981+
email,
3982+
},
3983+
});
3984+
const route = buildRoute();
3985+
3986+
try {
3987+
await runTest(route, mockRequest);
3988+
sinon.assert.fail('should have errored');
3989+
} catch (error) {
3990+
sinon.assert.calledOnceWithExactly(mockCustoms.flag, "63.245.221.32", { email, errno: 102 });
3991+
assert.equal(error.errno, 102, 'unknown account');
3992+
}
3993+
});
39723994
});
39733995

39743996
describe('/account', () => {

0 commit comments

Comments
 (0)