mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 21:50:15 +00:00
Integration working
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import {
|
||||
Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction
|
||||
} from "@bitwarden/common/platform/abstractions/fido2/fido2-user-interface.service.abstraction";
|
||||
import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-user-interface.service.abstraction";
|
||||
|
||||
import { DesktopFido2UserInterfaceService } from "../../autofill/services/desktop-fido2-user-interface.service";
|
||||
import { DesktopSettingsService } from "../../platform/services/desktop-settings.service";
|
||||
@@ -45,31 +43,35 @@ export class Fido2PlaceholderComponent {
|
||||
) {}
|
||||
|
||||
async confirmPasskey() {
|
||||
// placeholder, actual api arguments needed here should be discussed
|
||||
// just show casing we can call into the session to create the credential or change it.
|
||||
const desktopUiService = this.fido2UserInterfaceService as DesktopFido2UserInterfaceService;
|
||||
console.log("Got desktopService", desktopUiService.guid);
|
||||
|
||||
console.log("checking for session", this.fido2UserInterfaceService);
|
||||
try {
|
||||
console.log("checking for session", this.fido2UserInterfaceService);
|
||||
// Add timeout to avoid infinite hanging
|
||||
const session = desktopUiService.getCurrentSession();
|
||||
if (!session) {
|
||||
// todo: handle error
|
||||
console.error("No session found");
|
||||
return;
|
||||
}
|
||||
console.log("Got session", session.guid);
|
||||
|
||||
const desktopService = this.fido2UserInterfaceService as DesktopFido2UserInterfaceService;
|
||||
// const cipher = await session.createCredential({
|
||||
// userHandle: "userHandle2",
|
||||
// userName: "username2",
|
||||
// credentialName: "zxsd2",
|
||||
// rpId: "webauthn.io",
|
||||
// userVerification: true,
|
||||
// });
|
||||
|
||||
const session = await desktopService.getCurrentSession();
|
||||
|
||||
console.log("Got session", session);
|
||||
|
||||
|
||||
await session.createCredential({
|
||||
userHandle: "userHandle",
|
||||
userName: "",
|
||||
credentialName: "",
|
||||
rpId: "",
|
||||
userVerification: true,
|
||||
});
|
||||
|
||||
console.log("Created credential, will notify complete");
|
||||
session.notifyOperationCompleted();
|
||||
|
||||
await this.router.navigate(["/"]);
|
||||
await this.desktopSettingsService.setInModalMode(false);
|
||||
session.notifyOperationCompleted();
|
||||
await this.router.navigate(["/"]);
|
||||
await this.desktopSettingsService.setInModalMode(false);
|
||||
} catch (error) {
|
||||
console.error("Failed during confirmation:", error);
|
||||
// Handle error appropriately
|
||||
}
|
||||
}
|
||||
|
||||
async closeModal() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { APP_INITIALIZER, NgModule } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { Subject, merge } from "rxjs";
|
||||
|
||||
import { OrganizationUserApiService } from "@bitwarden/admin-console/common";
|
||||
@@ -331,6 +332,8 @@ const safeProviders: SafeProvider[] = [
|
||||
AccountService,
|
||||
LogService,
|
||||
MessagingServiceAbstraction,
|
||||
Router,
|
||||
DesktopSettingsService,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { filter, firstValueFrom, map, shareReplay, Subject } from "rxjs";
|
||||
import { Router } from "@angular/router";
|
||||
import { filter, lastValueFrom, firstValueFrom, map, shareReplay, Subject, tap } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
@@ -13,12 +14,17 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherRepromptType, CipherType, SecureNoteType } from "@bitwarden/common/vault/enums";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
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";
|
||||
import { UsernameAlgorithms } from "@bitwarden/generator-core";
|
||||
import { DesktopSettingsService } from "src/platform/services/desktop-settings.service";
|
||||
|
||||
// import the angular router
|
||||
|
||||
export class DesktopFido2UserInterfaceService
|
||||
implements Fido2UserInterfaceServiceAbstraction<void>
|
||||
@@ -29,22 +35,17 @@ export class DesktopFido2UserInterfaceService
|
||||
private accountService: AccountService,
|
||||
private logService: LogService,
|
||||
private messagingService: MessagingService,
|
||||
) {}
|
||||
|
||||
private currentSessionSubject = new Subject<DesktopFido2UserInterfaceSession | undefined>();
|
||||
private currentSubject$ = this.currentSessionSubject.pipe(
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
filter(c => c !== undefined)
|
||||
);
|
||||
|
||||
setCurrentSession(session: DesktopFido2UserInterfaceSession) {
|
||||
this.currentSessionSubject.next(session);
|
||||
}
|
||||
|
||||
getCurrentSession(): Promise<DesktopFido2UserInterfaceSession> {
|
||||
return firstValueFrom(this.currentSubject$);
|
||||
private router: Router,
|
||||
private desktopSettingsService: DesktopSettingsService,
|
||||
) {
|
||||
this.guid = "PARENT" + Math.random().toString(36).substring(7);
|
||||
}
|
||||
guid: string;
|
||||
private currentSession: any;
|
||||
|
||||
getCurrentSession(): DesktopFido2UserInterfaceSession | undefined {
|
||||
return this.currentSession;
|
||||
}
|
||||
|
||||
async newSession(
|
||||
fallbackSupported: boolean,
|
||||
@@ -52,13 +53,21 @@ export class DesktopFido2UserInterfaceService
|
||||
abortController?: AbortController,
|
||||
): Promise<DesktopFido2UserInterfaceSession> {
|
||||
this.logService.warning("newSession", fallbackSupported, abortController);
|
||||
return new DesktopFido2UserInterfaceSession(
|
||||
const session = new DesktopFido2UserInterfaceSession(
|
||||
this.authService,
|
||||
this.cipherService,
|
||||
this.accountService,
|
||||
this.logService,
|
||||
this.messagingService,
|
||||
this.router,
|
||||
this.desktopSettingsService,
|
||||
);
|
||||
|
||||
console.log("In parent", this.guid);
|
||||
console.log("Setting current session", session.guid);
|
||||
|
||||
this.currentSession = session;
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,17 +78,42 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
|
||||
private accountService: AccountService,
|
||||
private logService: LogService,
|
||||
private messagingService: MessagingService,
|
||||
) {}
|
||||
|
||||
async pickCredential({
|
||||
cipherIds,
|
||||
userVerification,
|
||||
}: PickCredentialParams): Promise<{ cipherId: string; userVerified: boolean }> {
|
||||
this.logService.warning("pickCredential", cipherIds, userVerification);
|
||||
|
||||
return { cipherId: cipherIds[0], userVerified: userVerification };
|
||||
private router: Router,
|
||||
private desktopSettingsService: DesktopSettingsService,
|
||||
) {
|
||||
this.guid = "SESSION" + Math.random().toString(36).substring(7);
|
||||
}
|
||||
|
||||
guid: string;
|
||||
|
||||
pickCredential: (
|
||||
params: PickCredentialParams,
|
||||
) => Promise<{ cipherId: string; userVerified: boolean }>;
|
||||
|
||||
private operationSubject = new Subject<void>();
|
||||
private createdCipher: Cipher;
|
||||
|
||||
/**
|
||||
* Notifies the Fido2UserInterfaceSession that the UI operations has completed and it can return to the OS.
|
||||
*/
|
||||
notifyOperationCompleted() {
|
||||
this.operationSubject.next();
|
||||
this.operationSubject.complete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns once the UI has confirmed and completed the operation
|
||||
* @returns
|
||||
*/
|
||||
private async waitForUICompletion(): Promise<void> {
|
||||
return lastValueFrom(this.operationSubject);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called by the OS. It loads the UI and waits for the user to confirm the new credential. Once the UI has confirmed, it returns to the the OS.
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
async confirmNewCredential({
|
||||
credentialName,
|
||||
userName,
|
||||
@@ -94,8 +128,40 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
|
||||
rpId,
|
||||
);
|
||||
|
||||
this.messagingService.send("loadurl", { url: "/passkeys", modal: true });
|
||||
try {
|
||||
// Load the UI:
|
||||
// maybe this modal mode thing shouldn't be done here?
|
||||
await this.desktopSettingsService.setInModalMode(true);
|
||||
await this.router.navigate(["/passkeys"]);
|
||||
|
||||
// Wait for the UI to wrap up
|
||||
await this.waitForUICompletion();
|
||||
await this.createCredential({
|
||||
credentialName,
|
||||
userName,
|
||||
rpId,
|
||||
userHandle: "",
|
||||
userVerification,
|
||||
});
|
||||
console.log("Returning from confirmNewCredential, created cipher:", this.createdCipher);
|
||||
|
||||
// wait for 10ms to help RXJS catch up(?)
|
||||
// We sometimes get a race condition from this.createCredential not updating cipherService in time
|
||||
console.log("waiting 10ms..");
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
console.log("Just waited 10ms");
|
||||
// Return the new cipher (this.createdCipher)
|
||||
return { cipherId: this.createdCipher.id, userVerified: userVerification };
|
||||
} finally {
|
||||
await this.desktopSettingsService.setInModalMode(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be called by the UI to create a new credential with user input etc.
|
||||
* @param param0
|
||||
*/
|
||||
async createCredential({ credentialName, userName, rpId }: NewCredentialParams): Promise<Cipher> {
|
||||
// Store the passkey on a new cipher to avoid replacing something important
|
||||
const cipher = new CipherView();
|
||||
cipher.name = credentialName;
|
||||
@@ -118,7 +184,9 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi
|
||||
const encCipher = await this.cipherService.encrypt(cipher, activeUserId);
|
||||
const createdCipher = await this.cipherService.createWithServer(encCipher);
|
||||
|
||||
return { cipherId: createdCipher.id, userVerified: userVerification };
|
||||
this.createdCipher = createdCipher;
|
||||
|
||||
return createdCipher;
|
||||
}
|
||||
|
||||
async informExcludedCredential(existingCipherIds: string[]): Promise<void> {
|
||||
|
||||
@@ -132,6 +132,7 @@ export class Fido2AuthenticatorService<ParentWindowReference>
|
||||
userVerification: params.requireUserVerification,
|
||||
rpId: params.rpEntity.id,
|
||||
});
|
||||
console.log("rpid", params.rpEntity.id, response.cipherId);
|
||||
const cipherId = response.cipherId;
|
||||
userVerified = response.userVerified;
|
||||
|
||||
@@ -146,6 +147,7 @@ export class Fido2AuthenticatorService<ParentWindowReference>
|
||||
keyPair = await createKeyPair();
|
||||
pubKeyDer = await crypto.subtle.exportKey("spki", keyPair.publicKey);
|
||||
const encrypted = await this.cipherService.get(cipherId);
|
||||
console.log("Encrypted", encrypted);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
@@ -153,6 +155,7 @@ export class Fido2AuthenticatorService<ParentWindowReference>
|
||||
cipher = await encrypted.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(encrypted, activeUserId),
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
!userVerified &&
|
||||
@@ -174,13 +177,15 @@ export class Fido2AuthenticatorService<ParentWindowReference>
|
||||
await this.cipherService.updateWithServer(reencrypted);
|
||||
await this.cipherService.clearCache(activeUserId);
|
||||
credentialId = fido2Credential.credentialId;
|
||||
console.log("rpid", params.rpEntity.id);
|
||||
|
||||
} catch (error) {
|
||||
this.logService?.error(
|
||||
`[Fido2Authenticator] Aborting because of unknown error when creating credential: ${error}`,
|
||||
);
|
||||
throw new Fido2AuthenticatorError(Fido2AuthenticatorErrorCode.Unknown);
|
||||
}
|
||||
|
||||
console.log("authdata rpid", params.rpEntity.id);
|
||||
const authData = await generateAuthData({
|
||||
rpId: params.rpEntity.id,
|
||||
credentialId: parseCredentialId(credentialId),
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
shareReplay,
|
||||
Subject,
|
||||
switchMap,
|
||||
tap,
|
||||
} from "rxjs";
|
||||
import { SemVer } from "semver";
|
||||
|
||||
@@ -141,6 +142,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
this.cipherViews$ = combineLatest([this.encryptedCiphersState.state$, this.localData$]).pipe(
|
||||
filter(([ciphers]) => ciphers != null), // Skip if ciphers haven't been loaded yor synced yet
|
||||
switchMap(() => merge(this.forceCipherViews$, this.getAllDecrypted())),
|
||||
tap((v) => console.log("---- cipherViews$", v)),
|
||||
shareReplay({ bufferSize: 1, refCount: true }),
|
||||
);
|
||||
this.addEditCipherInfo$ = this.addEditCipherInfoState.state$;
|
||||
|
||||
Reference in New Issue
Block a user