feat(snjs): add account recovery e2e test suite (#2134)

* feat(snjs): add account recovery e2e test suite

* fix(snjs): request params in account recovery tests

* fix(snjs): context password passing

* refactor: replace factory functions with context

Co-authored-by: Mo <mo@standardnotes.com>
This commit is contained in:
Karol Sójko
2023-01-09 17:25:52 +01:00
committed by GitHub
parent 0d8a05b805
commit 8deeae5a16
6 changed files with 159 additions and 14 deletions

View File

@@ -49,8 +49,10 @@ export class AuthApiService implements AuthApiServiceInterface {
try { try {
const response = await this.authServer.recoveryKeyParams({ const response = await this.authServer.recoveryKeyParams({
apiVersion: ApiVersion.v0, api_version: ApiVersion.v0,
...dto, code_challenge: dto.codeChallenge,
recovery_codes: dto.recoveryCodes,
username: dto.username,
}) })
return response return response
@@ -75,8 +77,11 @@ export class AuthApiService implements AuthApiServiceInterface {
try { try {
const response = await this.authServer.signInWithRecoveryCodes({ const response = await this.authServer.signInWithRecoveryCodes({
apiVersion: ApiVersion.v0, api_version: ApiVersion.v0,
...dto, code_verifier: dto.codeVerifier,
password: dto.password,
recovery_codes: dto.recoveryCodes,
username: dto.username,
}) })
return response return response

View File

@@ -1,6 +1,6 @@
export interface RecoveryKeyParamsRequestParams { export interface RecoveryKeyParamsRequestParams {
apiVersion: string api_version: string
username: string username: string
codeChallenge: string code_challenge: string
recoveryCodes: string recovery_codes: string
} }

View File

@@ -1,7 +1,7 @@
export interface SignInWithRecoveryCodesRequestParams { export interface SignInWithRecoveryCodesRequestParams {
apiVersion: string api_version: string
username: string username: string
password: string password: string
codeVerifier: string code_verifier: string
recoveryCodes: string recovery_codes: string
} }

View File

@@ -3,9 +3,9 @@ const SessionPaths = {
} }
const RecoveryPaths = { const RecoveryPaths = {
generateRecoveryCodes: '/v1/auth/recovery/codes', generateRecoveryCodes: '/v1/recovery/codes',
recoveryKeyParams: '/v1/auth/recovery/login-params', recoveryKeyParams: '/v1/recovery/login-params',
signInWithRecoveryCodes: '/v1/auth/recovery/login', signInWithRecoveryCodes: '/v1/recovery/login',
} }
export const Paths = { export const Paths = {

View File

@@ -0,0 +1,139 @@
import * as Factory from './lib/factory.js'
chai.use(chaiAsPromised)
const expect = chai.expect
describe('account recovery', function () {
this.timeout(Factory.ThirtySecondTimeout)
let application
let context
beforeEach(async function () {
localStorage.clear()
context = await Factory.createAppContextWithFakeCrypto()
await context.launch()
application = context.application
await context.register()
})
afterEach(async function () {
await context.deinit()
localStorage.clear()
})
it('should get the same recovery codes at each consecutive call', async () => {
let recoveryCodesSetting = await application.settings.getSetting(SettingName.RecoveryCodes)
expect(recoveryCodesSetting).to.equal(undefined)
const generatedRecoveryCodesAfterFirstCall = await application.getRecoveryCodes.execute()
expect(generatedRecoveryCodesAfterFirstCall.getValue().length).to.equal(49)
recoveryCodesSetting = await application.settings.getSetting(SettingName.RecoveryCodes)
expect(recoveryCodesSetting).to.equal(generatedRecoveryCodesAfterFirstCall.getValue())
const fetchedRecoveryCodesOnTheSecondCall = await application.getRecoveryCodes.execute()
expect(generatedRecoveryCodesAfterFirstCall.getValue()).to.equal(fetchedRecoveryCodesOnTheSecondCall.getValue())
})
it('should allow to sign in with recovery codes', async () => {
const generatedRecoveryCodes = await application.getRecoveryCodes.execute()
application = await context.signout()
expect(await application.protocolService.getRootKey()).to.not.be.ok
await application.signInWithRecoveryCodes.execute({
recoveryCodes: generatedRecoveryCodes.getValue(),
username: context.email,
password: context.password,
})
expect(await application.protocolService.getRootKey()).to.be.ok
})
it('should automatically generate new recovery codes after recovery sign in', async () => {
const generatedRecoveryCodes = await application.getRecoveryCodes.execute()
application = await context.signout()
await application.signInWithRecoveryCodes.execute({
recoveryCodes: generatedRecoveryCodes.getValue(),
username: context.email,
password: context.password,
})
const recoveryCodesAfterRecoverySignIn = await application.getRecoveryCodes.execute()
expect(recoveryCodesAfterRecoverySignIn.getValue()).not.to.equal(generatedRecoveryCodes.getValue())
})
it('should disable MFA after recovery sign in', async () => {
const secret = await application.generateMfaSecret()
const token = await application.getOtpToken(secret)
await application.enableMfa(secret, token)
expect(await application.isMfaActivated()).to.equal(true)
const generatedRecoveryCodes = await application.getRecoveryCodes.execute()
application = await context.signout()
await application.signInWithRecoveryCodes.execute({
recoveryCodes: generatedRecoveryCodes.getValue(),
username: context.email,
password: context.password,
})
expect(await application.isMfaActivated()).to.equal(false)
})
it('should not allow to sign in with recovery codes and invalid credentials', async () => {
const generatedRecoveryCodes = await application.getRecoveryCodes.execute()
application = await context.signout()
expect(await application.protocolService.getRootKey()).to.not.be.ok
await application.signInWithRecoveryCodes.execute({
recoveryCodes: generatedRecoveryCodes.getValue(),
username: context.email,
password: 'foobar',
})
expect(await application.protocolService.getRootKey()).to.not.be.ok
})
it('should not allow to sign in with invalid recovery codes', async () => {
await application.getRecoveryCodes.execute()
application = await context.signout()
expect(await application.protocolService.getRootKey()).to.not.be.ok
await application.signInWithRecoveryCodes.execute({
recoveryCodes: 'invalid recovery codes',
username: context.email,
password: context.paswword,
})
expect(await application.protocolService.getRootKey()).to.not.be.ok
})
it('should not allow to sign in with recovery codes if user has none', async () => {
application = await context.signout()
expect(await application.protocolService.getRootKey()).to.not.be.ok
await application.signInWithRecoveryCodes.execute({
recoveryCodes: 'foo bar',
username: context.email,
password: context.paswword,
})
expect(await application.protocolService.getRootKey()).to.not.be.ok
})
})

View File

@@ -89,6 +89,7 @@
<script type="module" src="session.test.js"></script> <script type="module" src="session.test.js"></script>
<script type="module" src="subscriptions.test.js"></script> <script type="module" src="subscriptions.test.js"></script>
<script type="module" src="workspaces.test.js"></script> <script type="module" src="workspaces.test.js"></script>
<script type="module" src="recovery.test.js"></script>
<script type="module"> <script type="module">
mocha.run(); mocha.run();
</script> </script>
@@ -98,4 +99,4 @@
<div id="mocha"></div> <div id="mocha"></div>
</body> </body>
</html> </html>