mirror of
https://github.com/bitwarden/browser
synced 2025-12-18 09:13:33 +00:00
[PM-9473] Add messaging for macOS passkey extension and desktop (#10768)
* Add messaging for macos passkey provider * fix: credential id conversion * Make build.sh executable Co-authored-by: Colton Hurst <colton@coltonhurst.com> * chore: add TODO --------- Co-authored-by: Andreas Coroiu <andreas.coroiu@gmail.com> Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> Co-authored-by: Colton Hurst <colton@coltonhurst.com>
This commit is contained in:
@@ -56,6 +56,8 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
|
||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { Fido2AuthenticatorService as Fido2AuthenticatorServiceAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-authenticator.service.abstraction";
|
||||
import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-user-interface.service.abstraction";
|
||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
|
||||
@@ -73,6 +75,7 @@ import { Message, MessageListener, MessageSender } from "@bitwarden/common/platf
|
||||
// eslint-disable-next-line no-restricted-imports -- Used for dependency injection
|
||||
import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal";
|
||||
import { TaskSchedulerService } from "@bitwarden/common/platform/scheduling";
|
||||
import { Fido2AuthenticatorService } from "@bitwarden/common/platform/services/fido2/fido2-authenticator.service";
|
||||
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
|
||||
import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory";
|
||||
import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory";
|
||||
@@ -80,6 +83,7 @@ import { SystemService } from "@bitwarden/common/platform/services/system.servic
|
||||
import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state";
|
||||
// eslint-disable-next-line import/no-restricted-paths -- Implementation for memory storage
|
||||
import { MemoryStorageService as MemoryStorageServiceForStateProviders } from "@bitwarden/common/platform/state/storage/memory-storage.service";
|
||||
import { SyncService } from "@bitwarden/common/platform/sync";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { VaultTimeoutStringType } from "@bitwarden/common/types/vault-timeout.type";
|
||||
import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
@@ -99,6 +103,7 @@ import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-
|
||||
import { DesktopAutofillService } from "../../autofill/services/desktop-autofill.service";
|
||||
import { ElectronBiometricsService } from "../../key-management/biometrics/electron-biometrics.service";
|
||||
import { flagEnabled } from "../../platform/flags";
|
||||
import { DesktopFido2UserInterfaceService } from "../../platform/services/desktop-fido2-user-interface.service";
|
||||
import { DesktopSettingsService } from "../../platform/services/desktop-settings.service";
|
||||
import { ElectronKeyService } from "../../platform/services/electron-key.service";
|
||||
import { ElectronLogRendererService } from "../../platform/services/electron-log.renderer.service";
|
||||
@@ -309,7 +314,29 @@ const safeProviders: SafeProvider[] = [
|
||||
}),
|
||||
safeProvider({
|
||||
provide: DesktopAutofillService,
|
||||
deps: [LogService, CipherServiceAbstraction, ConfigService],
|
||||
deps: [
|
||||
LogService,
|
||||
CipherServiceAbstraction,
|
||||
ConfigService,
|
||||
Fido2AuthenticatorServiceAbstraction,
|
||||
AccountService,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: Fido2UserInterfaceServiceAbstraction,
|
||||
useClass: DesktopFido2UserInterfaceService,
|
||||
deps: [AuthServiceAbstraction, CipherServiceAbstraction, AccountService, LogService],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: Fido2AuthenticatorServiceAbstraction,
|
||||
useClass: Fido2AuthenticatorService,
|
||||
deps: [
|
||||
CipherServiceAbstraction,
|
||||
Fido2UserInterfaceServiceAbstraction,
|
||||
SyncService,
|
||||
AccountService,
|
||||
LogService,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: NativeMessagingManifestService,
|
||||
|
||||
@@ -1,9 +1,92 @@
|
||||
import { ipcRenderer } from "electron";
|
||||
|
||||
import type { autofill } from "@bitwarden/desktop-napi";
|
||||
|
||||
import { Command } from "../platform/main/autofill/command";
|
||||
import { RunCommandParams, RunCommandResult } from "../platform/main/autofill/native-autofill.main";
|
||||
|
||||
export default {
|
||||
runCommand: <C extends Command>(params: RunCommandParams<C>): Promise<RunCommandResult<C>> =>
|
||||
ipcRenderer.invoke("autofill.runCommand", params),
|
||||
|
||||
listenPasskeyRegistration: (
|
||||
fn: (
|
||||
clientId: number,
|
||||
sequenceNumber: number,
|
||||
request: autofill.PasskeyRegistrationRequest,
|
||||
completeCallback: (
|
||||
error: Error | null,
|
||||
response: autofill.PasskeyRegistrationResponse,
|
||||
) => void,
|
||||
) => void,
|
||||
) => {
|
||||
ipcRenderer.on(
|
||||
"autofill.passkeyRegistration",
|
||||
(
|
||||
event,
|
||||
data: {
|
||||
clientId: number;
|
||||
sequenceNumber: number;
|
||||
request: autofill.PasskeyRegistrationRequest;
|
||||
},
|
||||
) => {
|
||||
const { clientId, sequenceNumber, request } = data;
|
||||
fn(clientId, sequenceNumber, request, (error, response) => {
|
||||
if (error) {
|
||||
ipcRenderer.send("autofill.completeError", {
|
||||
clientId,
|
||||
sequenceNumber,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ipcRenderer.send("autofill.completePasskeyRegistration", {
|
||||
clientId,
|
||||
sequenceNumber,
|
||||
response,
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
listenPasskeyAssertion: (
|
||||
fn: (
|
||||
clientId: number,
|
||||
sequenceNumber: number,
|
||||
request: autofill.PasskeyAssertionRequest,
|
||||
completeCallback: (error: Error | null, response: autofill.PasskeyAssertionResponse) => void,
|
||||
) => void,
|
||||
) => {
|
||||
ipcRenderer.on(
|
||||
"autofill.passkeyAssertion",
|
||||
(
|
||||
event,
|
||||
data: {
|
||||
clientId: number;
|
||||
sequenceNumber: number;
|
||||
request: autofill.PasskeyAssertionRequest;
|
||||
},
|
||||
) => {
|
||||
const { clientId, sequenceNumber, request } = data;
|
||||
fn(clientId, sequenceNumber, request, (error, response) => {
|
||||
if (error) {
|
||||
ipcRenderer.send("autofill.completeError", {
|
||||
clientId,
|
||||
sequenceNumber,
|
||||
error: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ipcRenderer.send("autofill.completePasskeyAssertion", {
|
||||
clientId,
|
||||
sequenceNumber,
|
||||
response,
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,12 +1,32 @@
|
||||
import { Injectable, OnDestroy } from "@angular/core";
|
||||
import { EMPTY, Subject, distinctUntilChanged, mergeMap, switchMap, takeUntil } from "rxjs";
|
||||
import { autofill } from "desktop_native/napi";
|
||||
import {
|
||||
EMPTY,
|
||||
Subject,
|
||||
distinctUntilChanged,
|
||||
firstValueFrom,
|
||||
map,
|
||||
mergeMap,
|
||||
switchMap,
|
||||
takeUntil,
|
||||
} from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import {
|
||||
Fido2AuthenticatorGetAssertionParams,
|
||||
Fido2AuthenticatorGetAssertionResult,
|
||||
Fido2AuthenticatorMakeCredentialResult,
|
||||
Fido2AuthenticatorMakeCredentialsParams,
|
||||
Fido2AuthenticatorService as Fido2AuthenticatorServiceAbstraction,
|
||||
} from "@bitwarden/common/platform/abstractions/fido2/fido2-authenticator.service.abstraction";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { getCredentialsForAutofill } from "@bitwarden/common/platform/services/fido2/fido2-autofill-utils";
|
||||
import { Fido2Utils } from "@bitwarden/common/platform/services/fido2/fido2-utils";
|
||||
import { guidToRawFormat } from "@bitwarden/common/platform/services/fido2/guid-utils";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@@ -26,6 +46,8 @@ export class DesktopAutofillService implements OnDestroy {
|
||||
private logService: LogService,
|
||||
private cipherService: CipherService,
|
||||
private configService: ConfigService,
|
||||
private fido2AuthenticatorService: Fido2AuthenticatorServiceAbstraction<void>,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
@@ -47,6 +69,8 @@ export class DesktopAutofillService implements OnDestroy {
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.listenIpc();
|
||||
}
|
||||
|
||||
/** Give metadata about all available credentials in the users vault */
|
||||
@@ -114,6 +138,146 @@ export class DesktopAutofillService implements OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
listenIpc() {
|
||||
ipc.autofill.listenPasskeyRegistration((clientId, sequenceNumber, request, callback) => {
|
||||
this.logService.warning("listenPasskeyRegistration", clientId, sequenceNumber, request);
|
||||
this.logService.warning(
|
||||
"listenPasskeyRegistration2",
|
||||
this.convertRegistrationRequest(request),
|
||||
);
|
||||
|
||||
const controller = new AbortController();
|
||||
void this.fido2AuthenticatorService
|
||||
.makeCredential(this.convertRegistrationRequest(request), null, controller)
|
||||
.then((response) => {
|
||||
callback(null, this.convertRegistrationResponse(request, response));
|
||||
})
|
||||
.catch((error) => {
|
||||
this.logService.error("listenPasskeyRegistration error", error);
|
||||
callback(error, null);
|
||||
});
|
||||
});
|
||||
|
||||
ipc.autofill.listenPasskeyAssertion(async (clientId, sequenceNumber, request, callback) => {
|
||||
this.logService.warning("listenPasskeyAssertion", clientId, sequenceNumber, request);
|
||||
|
||||
// TODO: For some reason the credentialId is passed as an empty array in the request, so we need to
|
||||
// get it from the cipher. For that we use the recordIdentifier, which is the cipherId.
|
||||
if (request.recordIdentifier && request.credentialId.length === 0) {
|
||||
const cipher = await this.cipherService.get(request.recordIdentifier);
|
||||
if (!cipher) {
|
||||
this.logService.error("listenPasskeyAssertion error", "Cipher not found");
|
||||
callback(new Error("Cipher not found"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
|
||||
const decrypted = await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
|
||||
const fido2Credential = decrypted.login.fido2Credentials?.[0];
|
||||
if (!fido2Credential) {
|
||||
this.logService.error("listenPasskeyAssertion error", "Fido2Credential not found");
|
||||
callback(new Error("Fido2Credential not found"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
request.credentialId = Array.from(
|
||||
guidToRawFormat(decrypted.login.fido2Credentials?.[0].credentialId),
|
||||
);
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
void this.fido2AuthenticatorService
|
||||
.getAssertion(this.convertAssertionRequest(request), null, controller)
|
||||
.then((response) => {
|
||||
callback(null, this.convertAssertionResponse(request, response));
|
||||
})
|
||||
.catch((error) => {
|
||||
this.logService.error("listenPasskeyAssertion error", error);
|
||||
callback(error, null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private convertRegistrationRequest(
|
||||
request: autofill.PasskeyRegistrationRequest,
|
||||
): Fido2AuthenticatorMakeCredentialsParams {
|
||||
return {
|
||||
hash: new Uint8Array(request.clientDataHash),
|
||||
rpEntity: {
|
||||
name: request.rpId,
|
||||
id: request.rpId,
|
||||
},
|
||||
userEntity: {
|
||||
id: new Uint8Array(request.userHandle),
|
||||
name: request.userName,
|
||||
displayName: undefined,
|
||||
icon: undefined,
|
||||
},
|
||||
credTypesAndPubKeyAlgs: request.supportedAlgorithms.map((alg) => ({
|
||||
alg,
|
||||
type: "public-key",
|
||||
})),
|
||||
excludeCredentialDescriptorList: [],
|
||||
requireResidentKey: true,
|
||||
requireUserVerification:
|
||||
request.userVerification === "required" || request.userVerification === "preferred",
|
||||
fallbackSupported: false,
|
||||
};
|
||||
}
|
||||
|
||||
private convertRegistrationResponse(
|
||||
request: autofill.PasskeyRegistrationRequest,
|
||||
response: Fido2AuthenticatorMakeCredentialResult,
|
||||
): autofill.PasskeyRegistrationResponse {
|
||||
return {
|
||||
rpId: request.rpId,
|
||||
clientDataHash: request.clientDataHash,
|
||||
credentialId: Array.from(Fido2Utils.bufferSourceToUint8Array(response.credentialId)),
|
||||
attestationObject: Array.from(
|
||||
Fido2Utils.bufferSourceToUint8Array(response.attestationObject),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
private convertAssertionRequest(
|
||||
request: autofill.PasskeyAssertionRequest,
|
||||
): Fido2AuthenticatorGetAssertionParams {
|
||||
return {
|
||||
rpId: request.rpId,
|
||||
hash: new Uint8Array(request.clientDataHash),
|
||||
allowCredentialDescriptorList: [
|
||||
{
|
||||
id: new Uint8Array(request.credentialId),
|
||||
type: "public-key",
|
||||
},
|
||||
],
|
||||
extensions: {},
|
||||
requireUserVerification:
|
||||
request.userVerification === "required" || request.userVerification === "preferred",
|
||||
fallbackSupported: false,
|
||||
};
|
||||
}
|
||||
|
||||
private convertAssertionResponse(
|
||||
request: autofill.PasskeyAssertionRequest,
|
||||
response: Fido2AuthenticatorGetAssertionResult,
|
||||
): autofill.PasskeyAssertionResponse {
|
||||
return {
|
||||
userHandle: Array.from(response.selectedCredential.userHandle),
|
||||
rpId: request.rpId,
|
||||
signature: Array.from(response.signature),
|
||||
clientDataHash: request.clientDataHash,
|
||||
authenticatorData: Array.from(response.authenticatorData),
|
||||
credentialId: Array.from(response.selectedCredential.id),
|
||||
};
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
|
||||
@@ -261,7 +261,7 @@ export class Main {
|
||||
new EphemeralValueStorageService();
|
||||
new SSOLocalhostCallbackService(this.environmentService, this.messagingService);
|
||||
|
||||
this.nativeAutofillMain = new NativeAutofillMain(this.logService);
|
||||
this.nativeAutofillMain = new NativeAutofillMain(this.logService, this.windowMain);
|
||||
void this.nativeAutofillMain.init();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ import { ipcMain } from "electron";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { autofill } from "@bitwarden/desktop-napi";
|
||||
|
||||
import { WindowMain } from "../../../main/window.main";
|
||||
|
||||
import { CommandDefinition } from "./command";
|
||||
|
||||
export type RunCommandParams<C extends CommandDefinition> = {
|
||||
@@ -14,7 +16,12 @@ export type RunCommandParams<C extends CommandDefinition> = {
|
||||
export type RunCommandResult<C extends CommandDefinition> = C["output"];
|
||||
|
||||
export class NativeAutofillMain {
|
||||
constructor(private logService: LogService) {}
|
||||
private ipcServer: autofill.IpcServer | null;
|
||||
|
||||
constructor(
|
||||
private logService: LogService,
|
||||
private windowMain: WindowMain,
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
ipcMain.handle(
|
||||
@@ -26,6 +33,52 @@ export class NativeAutofillMain {
|
||||
return this.runCommand(params);
|
||||
},
|
||||
);
|
||||
|
||||
this.ipcServer = await autofill.IpcServer.listen(
|
||||
"autofill",
|
||||
// RegistrationCallback
|
||||
(error, clientId, sequenceNumber, request) => {
|
||||
if (error) {
|
||||
this.logService.error("autofill.IpcServer.registration", error);
|
||||
return;
|
||||
}
|
||||
this.windowMain.win.webContents.send("autofill.passkeyRegistration", {
|
||||
clientId,
|
||||
sequenceNumber,
|
||||
request,
|
||||
});
|
||||
},
|
||||
// AssertionCallback
|
||||
(error, clientId, sequenceNumber, request) => {
|
||||
if (error) {
|
||||
this.logService.error("autofill.IpcServer.assertion", error);
|
||||
return;
|
||||
}
|
||||
this.windowMain.win.webContents.send("autofill.passkeyAssertion", {
|
||||
clientId,
|
||||
sequenceNumber,
|
||||
request,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.on("autofill.completePasskeyRegistration", (event, data) => {
|
||||
this.logService.warning("autofill.completePasskeyRegistration", data);
|
||||
const { clientId, sequenceNumber, response } = data;
|
||||
this.ipcServer.completeRegistration(clientId, sequenceNumber, response);
|
||||
});
|
||||
|
||||
ipcMain.on("autofill.completePasskeyAssertion", (event, data) => {
|
||||
this.logService.warning("autofill.completePasskeyAssertion", data);
|
||||
const { clientId, sequenceNumber, response } = data;
|
||||
this.ipcServer.completeAssertion(clientId, sequenceNumber, response);
|
||||
});
|
||||
|
||||
ipcMain.on("autofill.completeError", (event, data) => {
|
||||
this.logService.warning("autofill.completeError", data);
|
||||
const { clientId, sequenceNumber, error } = data;
|
||||
this.ipcServer.completeAssertion(clientId, sequenceNumber, error);
|
||||
});
|
||||
}
|
||||
|
||||
private async runCommand<C extends CommandDefinition>(
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import {
|
||||
Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction,
|
||||
Fido2UserInterfaceSession,
|
||||
NewCredentialParams,
|
||||
PickCredentialParams,
|
||||
} from "@bitwarden/common/platform/abstractions/fido2/fido2-user-interface.service.abstraction";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherRepromptType, CipherType, SecureNoteType } from "@bitwarden/common/vault/enums";
|
||||
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view";
|
||||
import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view";
|
||||
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
|
||||
import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.view";
|
||||
|
||||
// TODO: This should be moved to the directory of whatever team takes this on
|
||||
|
||||
export class DesktopFido2UserInterfaceService
|
||||
implements Fido2UserInterfaceServiceAbstraction<void>
|
||||
{
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private cipherService: CipherService,
|
||||
private accountService: AccountService,
|
||||
private logService: LogService,
|
||||
) {}
|
||||
|
||||
async newSession(
|
||||
fallbackSupported: boolean,
|
||||
_tab: void,
|
||||
abortController?: AbortController,
|
||||
): Promise<DesktopFido2UserInterfaceSession> {
|
||||
this.logService.warning("newSession", fallbackSupported, abortController);
|
||||
return new DesktopFido2UserInterfaceSession(
|
||||
this.authService,
|
||||
this.cipherService,
|
||||
this.accountService,
|
||||
this.logService,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSession {
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private cipherService: CipherService,
|
||||
private accountService: AccountService,
|
||||
private logService: LogService,
|
||||
) {}
|
||||
|
||||
async pickCredential({
|
||||
cipherIds,
|
||||
userVerification,
|
||||
}: PickCredentialParams): Promise<{ cipherId: string; userVerified: boolean }> {
|
||||
this.logService.warning("pickCredential", cipherIds, userVerification);
|
||||
|
||||
return { cipherId: cipherIds[0], userVerified: userVerification };
|
||||
}
|
||||
|
||||
async confirmNewCredential({
|
||||
credentialName,
|
||||
userName,
|
||||
userVerification,
|
||||
rpId,
|
||||
}: NewCredentialParams): Promise<{ cipherId: string; userVerified: boolean }> {
|
||||
this.logService.warning(
|
||||
"confirmNewCredential",
|
||||
credentialName,
|
||||
userName,
|
||||
userVerification,
|
||||
rpId,
|
||||
);
|
||||
|
||||
// Store the passkey on a new cipher to avoid replacing something important
|
||||
const cipher = new CipherView();
|
||||
cipher.name = credentialName;
|
||||
|
||||
cipher.type = CipherType.Login;
|
||||
cipher.login = new LoginView();
|
||||
cipher.login.username = userName;
|
||||
cipher.login.uris = [new LoginUriView()];
|
||||
cipher.login.uris[0].uri = "https://" + rpId;
|
||||
cipher.card = new CardView();
|
||||
cipher.identity = new IdentityView();
|
||||
cipher.secureNote = new SecureNoteView();
|
||||
cipher.secureNote.type = SecureNoteType.Generic;
|
||||
cipher.reprompt = CipherRepromptType.None;
|
||||
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
|
||||
const encCipher = await this.cipherService.encrypt(cipher, activeUserId);
|
||||
const createdCipher = await this.cipherService.createWithServer(encCipher);
|
||||
|
||||
return { cipherId: createdCipher.id, userVerified: userVerification };
|
||||
}
|
||||
|
||||
async informExcludedCredential(existingCipherIds: string[]): Promise<void> {
|
||||
this.logService.warning("informExcludedCredential", existingCipherIds);
|
||||
}
|
||||
|
||||
async ensureUnlockedVault(): Promise<void> {
|
||||
this.logService.warning("ensureUnlockedVault");
|
||||
|
||||
const status = await firstValueFrom(this.authService.activeAccountStatus$);
|
||||
if (status !== AuthenticationStatus.Unlocked) {
|
||||
throw new Error("Vault is not unlocked");
|
||||
}
|
||||
}
|
||||
|
||||
async informCredentialNotFound(): Promise<void> {
|
||||
this.logService.warning("informCredentialNotFound");
|
||||
}
|
||||
|
||||
async close() {
|
||||
this.logService.warning("close");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user