chore: auth verification (#2867) [skip e2e]

This commit is contained in:
Mo
2024-04-08 10:52:56 -05:00
committed by GitHub
parent a37e095907
commit b6eda707bd
30 changed files with 516 additions and 205 deletions

View File

@@ -98,6 +98,7 @@ import {
SignInResponse,
ClientDisplayableError,
SessionListEntry,
MetaEndpointResponse,
} from '@standardnotes/responses'
import {
SyncService,
@@ -117,7 +118,7 @@ import {
LoggerInterface,
canBlockDeinit,
} from '@standardnotes/utils'
import { UuidString, ApplicationEventPayload } from '../Types'
import { UuidString } from '../Types'
import { applicationEventForSyncEvent } from '@Lib/Application/Event'
import { BackupServiceInterface, FilesClientInterface } from '@standardnotes/files'
import { ComputePrivateUsername } from '@standardnotes/encryption'
@@ -275,12 +276,12 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
}),
)
const syncEventCallback = async (eventName: SyncEvent) => {
const syncEventCallback = async (eventName: SyncEvent, data?: unknown) => {
const appEvent = applicationEventForSyncEvent(eventName)
if (appEvent) {
await encryptionService.onSyncEvent(eventName)
await this.notifyEvent(appEvent)
await this.notifyEvent(appEvent, data)
if (appEvent === ApplicationEvent.CompletedFullSync) {
if (!this.handledFullSyncStage) {
@@ -535,7 +536,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.addEventObserver(filteredCallback, event)
}
private async notifyEvent(event: ApplicationEvent, data?: ApplicationEventPayload) {
private async notifyEvent(event: ApplicationEvent, data?: unknown) {
if (event === ApplicationEvent.Started) {
this.onStart()
} else if (event === ApplicationEvent.Launched) {
@@ -768,10 +769,11 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
public async register(
email: string,
password: string,
hvmToken: string,
ephemeral = false,
mergeLocal = true,
): Promise<UserRegistrationResponseBody> {
return this.user.register(email, password, ephemeral, mergeLocal)
return this.user.register(email, password, hvmToken, ephemeral, mergeLocal)
}
/**
@@ -785,8 +787,13 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
ephemeral = false,
mergeLocal = true,
awaitSync = false,
hvmToken?: string,
): Promise<HttpResponse<SignInResponse>> {
return this.user.signIn(email, password, strict, ephemeral, mergeLocal, awaitSync)
return this.user.signIn(email, password, strict, ephemeral, mergeLocal, awaitSync, hvmToken)
}
public async getCaptchaUrl(): Promise<HttpResponse<MetaEndpointResponse>> {
return this.legacyApi.getCaptchaUrl()
}
public async changeEmail(

View File

@@ -166,7 +166,9 @@ describe('SignInWithRecoveryCodes', () => {
})
it('should fail if the sign in with recovery code fails', async () => {
authManager.signInWithRecoveryCodes = jest.fn().mockReturnValue(false)
authManager.signInWithRecoveryCodes = jest.fn().mockReturnValue({
success: false,
})
const useCase = createUseCase()
const result = await useCase.execute({

View File

@@ -68,9 +68,18 @@ export class SignInWithRecoveryCodes implements UseCaseInterface<void> {
recoveryCodes: dto.recoveryCodes,
username: dto.username,
password: rootKey.serverPassword as string,
hvmToken: dto.hvmToken,
})
if (signInResult === false) {
if (signInResult.success === false) {
if (signInResult.captchaURL) {
return Result.fail(
JSON.stringify({
captchaURL: signInResult.captchaURL,
}),
)
}
return Result.fail('Could not sign in with recovery code')
}

View File

@@ -2,4 +2,5 @@ export interface SignInWithRecoveryCodesDTO {
recoveryCodes: string
username: string
password: string
hvmToken?: string
}

View File

@@ -68,6 +68,7 @@ import {
isErrorResponse,
MoveFileResponse,
ValetTokenOperation,
MetaEndpointResponse,
} from '@standardnotes/responses'
import { LegacySession, MapperInterface, Session, SessionToken } from '@standardnotes/domain-core'
import { HttpServiceInterface } from '@standardnotes/api'
@@ -290,6 +291,7 @@ export class LegacyApiService
email: string
serverPassword: string
ephemeral: boolean
hvmToken?: string
}): Promise<HttpResponse<SignInResponse>> {
if (this.authenticating) {
return this.createErrorResponse(API_MESSAGE_LOGIN_IN_PROGRESS, HttpStatusCode.BadRequest)
@@ -301,6 +303,7 @@ export class LegacyApiService
password: dto.serverPassword,
ephemeral: dto.ephemeral,
code_verifier: this.inMemoryStore.getValue(StorageKey.CodeVerifier) as string,
hvm_token: dto.hvmToken,
})
const response = await this.request<SignInResponse>({
@@ -958,4 +961,9 @@ export class LegacyApiService
return this.session.accessToken
}
public getCaptchaUrl() {
const response = this.httpService.get<MetaEndpointResponse>(Paths.v1.meta)
return response
}
}

View File

@@ -70,6 +70,7 @@ export const Paths = {
...SettingsPaths,
...SubscriptionPaths,
...UserPaths,
meta: '/v1/meta',
},
v2: {
...UserPathsV2,

View File

@@ -404,7 +404,12 @@ export class SessionManager
return undefined
}
async register(email: string, password: string, ephemeral: boolean): Promise<UserRegistrationResponseBody> {
async register(
email: string,
password: string,
hvmToken: string,
ephemeral: boolean,
): Promise<UserRegistrationResponseBody> {
if (password.length < MINIMUM_PASSWORD_LENGTH) {
throw new ApiCallError(
ErrorMessage.InsufficientPasswordMessage.replace('%LENGTH%', MINIMUM_PASSWORD_LENGTH.toString()),
@@ -429,6 +434,7 @@ export class SessionManager
const registerResponse = await this.userApiService.register({
email,
serverPassword,
hvmToken,
keyParams,
ephemeral,
})
@@ -503,8 +509,9 @@ export class SessionManager
strict = false,
ephemeral = false,
minAllowedVersion?: Common.ProtocolVersion,
hvmToken?: string,
): Promise<SessionManagerResponse> {
const result = await this.performSignIn(email, password, strict, ephemeral, minAllowedVersion)
const result = await this.performSignIn(email, password, strict, ephemeral, minAllowedVersion, hvmToken)
if (
isErrorResponse(result.response) &&
getErrorFromErrorResponse(result.response).tag !== ErrorTag.ClientValidationError &&
@@ -515,7 +522,7 @@ export class SessionManager
/**
* Try signing in with trimmed + lowercase version of email
*/
return this.performSignIn(cleanedEmail, password, strict, ephemeral, minAllowedVersion)
return this.performSignIn(cleanedEmail, password, strict, ephemeral, minAllowedVersion, hvmToken)
} else {
return result
}
@@ -530,6 +537,7 @@ export class SessionManager
strict = false,
ephemeral = false,
minAllowedVersion?: Common.ProtocolVersion,
hvmToken?: string,
): Promise<SessionManagerResponse> {
const paramsResult = await this.retrieveKeyParams({
email,
@@ -593,7 +601,7 @@ export class SessionManager
}
}
const rootKey = await this.encryptionService.computeRootKey(password, keyParams)
const signInResponse = await this.bypassChecksAndSignInWithRootKey(email, rootKey, ephemeral)
const signInResponse = await this.bypassChecksAndSignInWithRootKey(email, rootKey, ephemeral, hvmToken)
return {
response: signInResponse,
@@ -604,6 +612,7 @@ export class SessionManager
email: string,
rootKey: SNRootKey,
ephemeral = false,
hvmToken?: string,
): Promise<HttpResponse<SignInResponse>> {
const { wrappingKey, canceled } = await this.challengeService.getWrappingKeyIfApplicable()
@@ -619,6 +628,7 @@ export class SessionManager
email,
serverPassword: rootKey.serverPassword as string,
ephemeral,
hvmToken,
})
if (!signInResponse.data || isErrorResponse(signInResponse)) {