diff --git a/packages/auth/src/Bootstrap/Container.ts b/packages/auth/src/Bootstrap/Container.ts index 8adba523e..ada21f1ee 100644 --- a/packages/auth/src/Bootstrap/Container.ts +++ b/packages/auth/src/Bootstrap/Container.ts @@ -665,6 +665,7 @@ export class ContainerConfigLoader { container.get(TYPES.IncreaseLoginAttempts), container.get(TYPES.ClearLoginAttempts), container.get(TYPES.DeleteSetting), + container.get(TYPES.AuthenticatorRepository), ), ) container.bind(TYPES.DeleteAccount).to(DeleteAccount) diff --git a/packages/auth/src/Domain/Authenticator/AuthenticatorRepositoryInterface.ts b/packages/auth/src/Domain/Authenticator/AuthenticatorRepositoryInterface.ts index b622f8c13..f40d89570 100644 --- a/packages/auth/src/Domain/Authenticator/AuthenticatorRepositoryInterface.ts +++ b/packages/auth/src/Domain/Authenticator/AuthenticatorRepositoryInterface.ts @@ -8,4 +8,5 @@ export interface AuthenticatorRepositoryInterface { findByUserUuidAndCredentialId(userUuid: Uuid, credentialId: Buffer): Promise save(authenticator: Authenticator): Promise remove(authenticator: Authenticator): Promise + removeByUserUuid(userUuid: Uuid): Promise } diff --git a/packages/auth/src/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.spec.ts b/packages/auth/src/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.spec.ts index 11f3b31d9..cf1a66c57 100644 --- a/packages/auth/src/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.spec.ts +++ b/packages/auth/src/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.spec.ts @@ -2,6 +2,7 @@ import { Result } from '@standardnotes/domain-core' import { AuthResponse20200115 } from '../../Auth/AuthResponse20200115' import { AuthResponseFactory20200115 } from '../../Auth/AuthResponseFactory20200115' +import { AuthenticatorRepositoryInterface } from '../../Authenticator/AuthenticatorRepositoryInterface' import { CrypterInterface } from '../../Encryption/CrypterInterface' import { Setting } from '../../Setting/Setting' import { SettingServiceInterface } from '../../Setting/SettingServiceInterface' @@ -24,6 +25,7 @@ describe('SignInWithRecoveryCodes', () => { let increaseLoginAttempts: IncreaseLoginAttempts let clearLoginAttempts: ClearLoginAttempts let deleteSetting: DeleteSetting + let authenticatorRepository: AuthenticatorRepositoryInterface const createUseCase = () => new SignInWithRecoveryCodes( @@ -36,12 +38,13 @@ describe('SignInWithRecoveryCodes', () => { increaseLoginAttempts, clearLoginAttempts, deleteSetting, + authenticatorRepository, ) beforeEach(() => { userRepository = {} as jest.Mocked userRepository.findOneByEmail = jest.fn().mockReturnValue({ - uuid: '1-2-3', + uuid: '00000000-0000-0000-0000-000000000000', encryptedPassword: '$2a$11$K3g6XoTau8VmLJcai1bB0eD9/YvBSBRtBhMprJOaVZ0U3SgasZH3a', } as jest.Mocked) @@ -69,6 +72,9 @@ describe('SignInWithRecoveryCodes', () => { deleteSetting = {} as jest.Mocked deleteSetting.execute = jest.fn() + + authenticatorRepository = {} as jest.Mocked + authenticatorRepository.removeByUserUuid = jest.fn() }) it('should return error if password is not provided', async () => { @@ -209,6 +215,24 @@ describe('SignInWithRecoveryCodes', () => { expect(result.getError()).toBe('Could not sign in with recovery codes: Oops') }) + it('should return error if user has an invalid uuid', async () => { + userRepository.findOneByEmail = jest.fn().mockReturnValue({ + uuid: '1-2-3', + encryptedPassword: '$2a$11$K3g6XoTau8VmLJcai1bB0eD9/YvBSBRtBhMprJOaVZ0U3SgasZH3a', + } as jest.Mocked) + + const result = await createUseCase().execute({ + userAgent: 'user-agent', + username: 'test@test.te', + password: 'qweqwe123123', + codeVerifier: 'code-verifier', + recoveryCodes: 'foo', + }) + + expect(result.isFailed()).toBe(true) + expect(result.getError()).toBe('Invalid user uuid') + }) + it('should return auth response', async () => { const result = await createUseCase().execute({ userAgent: 'user-agent', @@ -220,6 +244,7 @@ describe('SignInWithRecoveryCodes', () => { expect(clearLoginAttempts.execute).toHaveBeenCalled() expect(deleteSetting.execute).toHaveBeenCalled() + expect(authenticatorRepository.removeByUserUuid).toHaveBeenCalled() expect(result.isFailed()).toBe(false) }) }) diff --git a/packages/auth/src/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.ts b/packages/auth/src/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.ts index 6b4c9da85..704e9e1a1 100644 --- a/packages/auth/src/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.ts +++ b/packages/auth/src/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.ts @@ -1,5 +1,5 @@ import * as bcrypt from 'bcryptjs' -import { Result, UseCaseInterface, Username, Validator } from '@standardnotes/domain-core' +import { Result, UseCaseInterface, Username, Uuid, Validator } from '@standardnotes/domain-core' import { SettingName } from '@standardnotes/settings' import { ApiVersion } from '@standardnotes/api' @@ -15,6 +15,7 @@ import { AuthResponseFactory20200115 } from '../../Auth/AuthResponseFactory20200 import { IncreaseLoginAttempts } from '../IncreaseLoginAttempts' import { ClearLoginAttempts } from '../ClearLoginAttempts' import { DeleteSetting } from '../DeleteSetting/DeleteSetting' +import { AuthenticatorRepositoryInterface } from '../../Authenticator/AuthenticatorRepositoryInterface' export class SignInWithRecoveryCodes implements UseCaseInterface { constructor( @@ -27,6 +28,7 @@ export class SignInWithRecoveryCodes implements UseCaseInterface> { @@ -65,6 +67,14 @@ export class SignInWithRecoveryCodes implements UseCaseInterface, ) {} + async removeByUserUuid(userUuid: Uuid): Promise { + await this.ormRepository + .createQueryBuilder() + .delete() + .where('user_uuid = :userUuid', { userUuid: userUuid.value }) + .execute() + } + async findById(id: UniqueEntityId): Promise { const persistence = await this.ormRepository .createQueryBuilder('authenticator')