PM-7673: Reduce syncs when signing in with passkeys (#10817)

* Reduce syncs when signing in with passkeys

* PM-7673: Reduce syncs when creating a passkey (#10824)

* Reduce to syncs when creating a passkey

* Mocked rxjs stream
This commit is contained in:
Anders Åberg 2024-09-19 14:45:45 +02:00 committed by GitHub
parent 7f9c5cedaf
commit 354079725f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 37 additions and 11 deletions

View File

@ -1,7 +1,7 @@
import { TextEncoder } from "util"; import { TextEncoder } from "util";
import { mock, MockProxy } from "jest-mock-extended"; import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject } from "rxjs"; import { BehaviorSubject, of } from "rxjs";
import { AccountInfo, AccountService } from "../../../auth/abstractions/account.service"; import { AccountInfo, AccountService } from "../../../auth/abstractions/account.service";
import { UserId } from "../../../types/guid"; import { UserId } from "../../../types/guid";
@ -53,7 +53,9 @@ describe("FidoAuthenticatorService", () => {
userInterface = mock<Fido2UserInterfaceService>(); userInterface = mock<Fido2UserInterfaceService>();
userInterfaceSession = mock<Fido2UserInterfaceSession>(); userInterfaceSession = mock<Fido2UserInterfaceSession>();
userInterface.newSession.mockResolvedValue(userInterfaceSession); userInterface.newSession.mockResolvedValue(userInterfaceSession);
syncService = mock<SyncService>(); syncService = mock<SyncService>({
activeUserLastSync$: () => of(new Date()),
});
accountService = mock<AccountService>(); accountService = mock<AccountService>();
authenticator = new Fido2AuthenticatorService( authenticator = new Fido2AuthenticatorService(
cipherService, cipherService,

View File

@ -94,7 +94,14 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
} }
await userInterfaceSession.ensureUnlockedVault(); await userInterfaceSession.ensureUnlockedVault();
await this.syncService.fullSync(false);
// Avoid syncing if we did it reasonably soon as the only reason for syncing is to validate excludeCredentials
const lastSync = await firstValueFrom(this.syncService.activeUserLastSync$());
const threshold = new Date().getTime() - 1000 * 60 * 30; // 30 minutes ago
if (!lastSync || lastSync.getTime() < threshold) {
await this.syncService.fullSync(false);
}
const existingCipherIds = await this.findExcludedCredentials( const existingCipherIds = await this.findExcludedCredentials(
params.excludeCredentialDescriptorList, params.excludeCredentialDescriptorList,
@ -223,15 +230,17 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
let cipherOptions: CipherView[]; let cipherOptions: CipherView[];
await userInterfaceSession.ensureUnlockedVault(); await userInterfaceSession.ensureUnlockedVault();
await this.syncService.fullSync(false);
if (params.allowCredentialDescriptorList?.length > 0) { // Try to find the passkey locally before causing a sync to speed things up
cipherOptions = await this.findCredentialsById( // only skip syncing if we found credentials AND all of them have a counter = 0
params.allowCredentialDescriptorList, cipherOptions = await this.findCredential(params, cipherOptions);
params.rpId, if (
); cipherOptions.length === 0 ||
} else { cipherOptions.some((c) => c.login.fido2Credentials.some((p) => p.counter > 0))
cipherOptions = await this.findCredentialsByRp(params.rpId); ) {
// If no passkey is found, or any had a non-zero counter, sync to get the latest data
await this.syncService.fullSync(false);
cipherOptions = await this.findCredential(params, cipherOptions);
} }
if (cipherOptions.length === 0) { if (cipherOptions.length === 0) {
@ -335,6 +344,21 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
} }
} }
private async findCredential(
params: Fido2AuthenticatorGetAssertionParams,
cipherOptions: CipherView[],
) {
if (params.allowCredentialDescriptorList?.length > 0) {
cipherOptions = await this.findCredentialsById(
params.allowCredentialDescriptorList,
params.rpId,
);
} else {
cipherOptions = await this.findCredentialsByRp(params.rpId);
}
return cipherOptions;
}
private requiresUserVerificationPrompt( private requiresUserVerificationPrompt(
params: Fido2AuthenticatorGetAssertionParams, params: Fido2AuthenticatorGetAssertionParams,
cipherOptions: CipherView[], cipherOptions: CipherView[],