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:
@@ -49,8 +49,10 @@ export class AuthApiService implements AuthApiServiceInterface {
|
||||
|
||||
try {
|
||||
const response = await this.authServer.recoveryKeyParams({
|
||||
apiVersion: ApiVersion.v0,
|
||||
...dto,
|
||||
api_version: ApiVersion.v0,
|
||||
code_challenge: dto.codeChallenge,
|
||||
recovery_codes: dto.recoveryCodes,
|
||||
username: dto.username,
|
||||
})
|
||||
|
||||
return response
|
||||
@@ -75,8 +77,11 @@ export class AuthApiService implements AuthApiServiceInterface {
|
||||
|
||||
try {
|
||||
const response = await this.authServer.signInWithRecoveryCodes({
|
||||
apiVersion: ApiVersion.v0,
|
||||
...dto,
|
||||
api_version: ApiVersion.v0,
|
||||
code_verifier: dto.codeVerifier,
|
||||
password: dto.password,
|
||||
recovery_codes: dto.recoveryCodes,
|
||||
username: dto.username,
|
||||
})
|
||||
|
||||
return response
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export interface RecoveryKeyParamsRequestParams {
|
||||
apiVersion: string
|
||||
api_version: string
|
||||
username: string
|
||||
codeChallenge: string
|
||||
recoveryCodes: string
|
||||
code_challenge: string
|
||||
recovery_codes: string
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export interface SignInWithRecoveryCodesRequestParams {
|
||||
apiVersion: string
|
||||
api_version: string
|
||||
username: string
|
||||
password: string
|
||||
codeVerifier: string
|
||||
recoveryCodes: string
|
||||
code_verifier: string
|
||||
recovery_codes: string
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ const SessionPaths = {
|
||||
}
|
||||
|
||||
const RecoveryPaths = {
|
||||
generateRecoveryCodes: '/v1/auth/recovery/codes',
|
||||
recoveryKeyParams: '/v1/auth/recovery/login-params',
|
||||
signInWithRecoveryCodes: '/v1/auth/recovery/login',
|
||||
generateRecoveryCodes: '/v1/recovery/codes',
|
||||
recoveryKeyParams: '/v1/recovery/login-params',
|
||||
signInWithRecoveryCodes: '/v1/recovery/login',
|
||||
}
|
||||
|
||||
export const Paths = {
|
||||
|
||||
139
packages/snjs/mocha/recovery.test.js
Normal file
139
packages/snjs/mocha/recovery.test.js
Normal 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
|
||||
})
|
||||
})
|
||||
@@ -89,6 +89,7 @@
|
||||
<script type="module" src="session.test.js"></script>
|
||||
<script type="module" src="subscriptions.test.js"></script>
|
||||
<script type="module" src="workspaces.test.js"></script>
|
||||
<script type="module" src="recovery.test.js"></script>
|
||||
<script type="module">
|
||||
mocha.run();
|
||||
</script>
|
||||
@@ -98,4 +99,4 @@
|
||||
<div id="mocha"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user