Compare commits

...
Author SHA1 Message Date
StandardNotes CI d2a66556e0 chore(release): publish
- @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].6
2023-01-09 16:54:54 +00:00
Karol SójkoandMo 8deeae5a16 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 <[email protected]>
2023-01-09 17:25:52 +01:00
StandardNotes CI 0d8a05b805 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].5
2023-01-09 07:40:10 +00:00
Karol Sójko 10751ea2ed feat(snjs): add getting recovery codes (#2132) 2023-01-09 08:13:12 +01:00
29 changed files with 334 additions and 28 deletions
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.23.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
### Features
* **snjs:** add account recovery e2e test suite ([#2134](https://github.com/standardnotes/app/issues/2134)) ([8deeae5](https://github.com/standardnotes/app/commit/8deeae5a16191da7b4aad2ddc568e714fbeef8b8))
# [1.22.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.22.0",
"version": "1.23.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -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 -3
View File
@@ -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 = {
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.104.78](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.77](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.76](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/desktop
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.104.76",
"version": "3.104.78",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.50.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/mobile
## [3.50.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/mobile
## [3.50.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/mobile
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.50.12",
"version": "3.50.14",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0-or-later",
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.101](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.100](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.99](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.4.99",
"version": "1.4.101",
"license": "AGPL-3.0-or-later",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.53.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/services
# [1.53.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.53.0",
"version": "1.53.1",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.161.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
### Features
* **snjs:** add account recovery e2e test suite ([#2134](https://github.com/standardnotes/app/issues/2134)) ([8deeae5](https://github.com/standardnotes/app/commit/8deeae5a16191da7b4aad2ddc568e714fbeef8b8))
# [2.160.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
### Features
* **snjs:** add getting recovery codes ([#2132](https://github.com/standardnotes/app/issues/2132)) ([10751ea](https://github.com/standardnotes/app/commit/10751ea2edb2ca48e4493b87053383f27460ef75))
# [2.159.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
### Features
@@ -97,6 +97,7 @@ import { SessionStorageMapper } from '@Lib/Services/Mapping/SessionStorageMapper
import { LegacySessionStorageMapper } from '@Lib/Services/Mapping/LegacySessionStorageMapper'
import { SignInWithRecoveryCodes } from '@Lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes'
import { UseCaseContainerInterface } from '@Lib/Domain/UseCase/UseCaseContainerInterface'
import { GetRecoveryCodes } from '@Lib/Domain/UseCase/GetRecoveryCodes/GetRecoveryCodes'
/** How often to automatically sync, in milliseconds */
const DEFAULT_AUTO_SYNC_INTERVAL = 30_000
@@ -177,6 +178,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
private declare authManager: AuthClientInterface
private declare _signInWithRecoveryCodes: SignInWithRecoveryCodes
private declare _getRecoveryCodes: GetRecoveryCodes
private internalEventBus!: ExternalServices.InternalEventBusInterface
@@ -263,6 +265,10 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this._signInWithRecoveryCodes
}
get getRecoveryCodes(): UseCaseInterface<string> {
return this._getRecoveryCodes
}
public get files(): FilesClientInterface {
return this.fileService
}
@@ -1218,6 +1224,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
;(this.authenticatorManager as unknown) = undefined
;(this.authManager as unknown) = undefined
;(this._signInWithRecoveryCodes as unknown) = undefined
;(this._getRecoveryCodes as unknown) = undefined
this.services = []
}
@@ -1776,5 +1783,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
this.sessionManager,
this.internalEventBus,
)
this._getRecoveryCodes = new GetRecoveryCodes(this.authManager, this.settingsService)
}
}
@@ -0,0 +1,48 @@
import { AuthClientInterface } from '@standardnotes/services'
import { SettingsClientInterface } from '@Lib/Services/Settings/SettingsClientInterface'
import { GetRecoveryCodes } from './GetRecoveryCodes'
describe('GetRecoveryCodes', () => {
let authClient: AuthClientInterface
let settingsClient: SettingsClientInterface
const createUseCase = () => new GetRecoveryCodes(authClient, settingsClient)
beforeEach(() => {
authClient = {} as jest.Mocked<AuthClientInterface>
authClient.generateRecoveryCodes = jest.fn().mockResolvedValue('recovery-codes')
settingsClient = {} as jest.Mocked<SettingsClientInterface>
settingsClient.getSetting = jest.fn().mockResolvedValue('existing-recovery-codes')
})
it('should return existing recovery codes if they exist', async () => {
const useCase = createUseCase()
const result = await useCase.execute()
expect(result.getValue()).toBe('existing-recovery-codes')
})
it('should generate recovery codes if they do not exist', async () => {
settingsClient.getSetting = jest.fn().mockResolvedValue(undefined)
const useCase = createUseCase()
const result = await useCase.execute()
expect(result.getValue()).toBe('recovery-codes')
})
it('should return error if recovery codes could not be generated', async () => {
settingsClient.getSetting = jest.fn().mockResolvedValue(undefined)
authClient.generateRecoveryCodes = jest.fn().mockResolvedValue(false)
const useCase = createUseCase()
const result = await useCase.execute()
expect(result.isFailed()).toBe(true)
})
})
@@ -0,0 +1,23 @@
import { AuthClientInterface } from '@standardnotes/services'
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
import { SettingName } from '@standardnotes/settings'
import { SettingsClientInterface } from '@Lib/Services/Settings/SettingsClientInterface'
export class GetRecoveryCodes implements UseCaseInterface<string> {
constructor(private authClient: AuthClientInterface, private settingsClient: SettingsClientInterface) {}
async execute(): Promise<Result<string>> {
const existingRecoveryCodes = await this.settingsClient.getSetting(SettingName.RecoveryCodes)
if (existingRecoveryCodes !== undefined) {
return Result.ok(existingRecoveryCodes)
}
const generatedRecoveryCodes = await this.authClient.generateRecoveryCodes()
if (generatedRecoveryCodes === false) {
return Result.fail('Could not generate recovery codes')
}
return Result.ok(generatedRecoveryCodes)
}
}
@@ -2,4 +2,5 @@ import { UseCaseInterface } from '@standardnotes/domain-core'
export interface UseCaseContainerInterface {
get signInWithRecoveryCodes(): UseCaseInterface<void>
get getRecoveryCodes(): UseCaseInterface<string>
}
+139
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
})
})
+2 -1
View File
@@ -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>
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.159.0",
"version": "2.161.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -45,7 +45,7 @@
"@standardnotes/responses": "workspace:*",
"@standardnotes/security": "^1.7.0",
"@standardnotes/services": "workspace:*",
"@standardnotes/settings": "^1.18.4",
"@standardnotes/settings": "^1.19.0",
"@standardnotes/sncrypto-common": "workspace:*",
"@standardnotes/sncrypto-web": "workspace:*",
"@standardnotes/utils": "workspace:*",
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.23.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.23.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/ui-services
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/ui-services",
"version": "1.23.4",
"version": "1.23.5",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.136.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/web
## [3.136.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/web
## [3.136.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)
**Note:** Version bump only for package @standardnotes/web
+22
View File
@@ -1,5 +1,27 @@
{
"versions": [
{
"version": "3.136.6",
"title": "[3.136.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.136.5",
"title": "[3.136.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.136.4",
"title": "[3.136.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-09)",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/web",
"version": "3.136.4",
"version": "3.136.6",
"license": "AGPL-3.0-or-later",
"main": "dist/app.js",
"author": "Standard Notes.",
+5 -5
View File
@@ -6025,12 +6025,12 @@ __metadata:
languageName: unknown
linkType: soft
"@standardnotes/settings@npm:^1.18.4":
version: 1.18.4
resolution: "@standardnotes/settings@npm:1.18.4"
"@standardnotes/settings@npm:^1.19.0":
version: 1.19.0
resolution: "@standardnotes/settings@npm:1.19.0"
dependencies:
reflect-metadata: ^0.1.13
checksum: 73017d19517c5719a3611385b76a5a3273f03c570c42da79656d0df04cb539f6a5eeb7a5d7db30a8ffc7fd0ef4c801924ff27093f84b2c40ad1542b0397745ac
checksum: 85816e5d25e2a4cfe014dbe371bc30902b1bb194f5d3c021fe754678ff9bd693255f1d209a66c4375334b1edd40a8f4133480a20a85b88859b3cfbff1a570cc9
languageName: node
linkType: hard
@@ -6111,7 +6111,7 @@ __metadata:
"@standardnotes/responses": "workspace:*"
"@standardnotes/security": ^1.7.0
"@standardnotes/services": "workspace:*"
"@standardnotes/settings": ^1.18.4
"@standardnotes/settings": ^1.19.0
"@standardnotes/sncrypto-common": "workspace:*"
"@standardnotes/sncrypto-web": "workspace:*"
"@standardnotes/utils": "workspace:*"