Skip to content

Commit

Permalink
PM-7673: Reduce syncs when signing in with passkeys (#10817)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
abergs authored Sep 19, 2024
1 parent 7f9c5ce commit 3540797
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TextEncoder } from "util";

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 { UserId } from "../../../types/guid";
Expand Down Expand Up @@ -53,7 +53,9 @@ describe("FidoAuthenticatorService", () => {
userInterface = mock<Fido2UserInterfaceService>();
userInterfaceSession = mock<Fido2UserInterfaceSession>();
userInterface.newSession.mockResolvedValue(userInterfaceSession);
syncService = mock<SyncService>();
syncService = mock<SyncService>({
activeUserLastSync$: () => of(new Date()),
});
accountService = mock<AccountService>();
authenticator = new Fido2AuthenticatorService(
cipherService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,14 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
}

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(
params.excludeCredentialDescriptorList,
Expand Down Expand Up @@ -223,15 +230,17 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
let cipherOptions: CipherView[];

await userInterfaceSession.ensureUnlockedVault();
await this.syncService.fullSync(false);

if (params.allowCredentialDescriptorList?.length > 0) {
cipherOptions = await this.findCredentialsById(
params.allowCredentialDescriptorList,
params.rpId,
);
} else {
cipherOptions = await this.findCredentialsByRp(params.rpId);
// Try to find the passkey locally before causing a sync to speed things up
// only skip syncing if we found credentials AND all of them have a counter = 0
cipherOptions = await this.findCredential(params, cipherOptions);
if (
cipherOptions.length === 0 ||
cipherOptions.some((c) => c.login.fido2Credentials.some((p) => p.counter > 0))
) {
// 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) {
Expand Down Expand Up @@ -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(
params: Fido2AuthenticatorGetAssertionParams,
cipherOptions: CipherView[],
Expand Down

0 comments on commit 3540797

Please sign in to comment.