1
0
mirror of https://github.com/bitwarden/browser synced 2026-03-02 11:31:44 +00:00

Fix sign in

This commit is contained in:
Bernd Schoolmann
2025-11-17 15:13:08 +01:00
parent 452ef0d774
commit 791397d60d
8 changed files with 86 additions and 24 deletions

View File

@@ -1,4 +1,20 @@
export type AuthenticatorAssertionResponse = {
clientDataJSON: Uint8Array;
authenticatorData: Uint8Array;
signature: Uint8Array;
userHandle: Uint8Array | null;
};
export type PublicKeyCredential = {
authenticatorAttachment: string;
id: string;
rawId: Uint8Array;
response: AuthenticatorAssertionResponse;
type: string;
prf?: Uint8Array;
};
export abstract class NavigatorCredentialsService {
abstract get(options: CredentialRequestOptions): Promise<Credential | null>;
abstract get(options: CredentialRequestOptions): Promise<PublicKeyCredential | null>;
abstract available(): Promise<boolean>;
}

View File

@@ -1,7 +1,10 @@
import { ClientType } from "@bitwarden/client-type";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { NavigatorCredentialsService } from "../../abstractions/webauthn/navigator-credentials.service";
import {
NavigatorCredentialsService,
PublicKeyCredential as CustomPublicKeyCredential,
} from "../../abstractions/webauthn/navigator-credentials.service";
export class DefaultNavigatorCredentialsService implements NavigatorCredentialsService {
private navigatorCredentials: CredentialsContainer;
@@ -13,11 +16,36 @@ export class DefaultNavigatorCredentialsService implements NavigatorCredentialsS
this.navigatorCredentials = this.window.navigator.credentials;
}
async get(options: CredentialRequestOptions): Promise<Credential | null> {
return await this.navigatorCredentials.get(options);
async get(options: CredentialRequestOptions): Promise<CustomPublicKeyCredential | null> {
const result: PublicKeyCredential = (await this.navigatorCredentials.get(
options,
)) as PublicKeyCredential;
const response: AuthenticatorAssertionResponse =
result.response as AuthenticatorAssertionResponse;
return {
authenticatorAttachment: result.authenticatorAttachment,
id: result.id,
rawId: new Uint8Array(result.rawId),
response: {
clientDataJSON: new Uint8Array(response.clientDataJSON),
authenticatorData: new Uint8Array(response.authenticatorData),
signature: new Uint8Array(response.signature),
userHandle: response.userHandle ? new Uint8Array(response.userHandle) : null,
},
type: result.type,
prf: bufferSourceToUint8Array(result.getClientExtensionResults().prf?.results?.first),
};
}
async available(): Promise<boolean> {
return this.platformUtilsService.getClientType() === ClientType.Web;
}
}
function bufferSourceToUint8Array(source: BufferSource): Uint8Array {
if (source instanceof ArrayBuffer) {
return new Uint8Array(source);
} else {
return new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
}
}

View File

@@ -2,6 +2,8 @@
// @ts-strict-ignore
import { Jsonify } from "type-fest";
import { PublicKeyCredential } from "@bitwarden/common/auth/abstractions/webauthn/navigator-credentials.service";
import { Utils } from "../../../../platform/misc/utils";
import { WebAuthnLoginResponseRequest } from "./webauthn-login-response.request";
@@ -20,15 +22,15 @@ export class WebAuthnLoginAssertionResponseRequest extends WebAuthnLoginResponse
constructor(credential: PublicKeyCredential) {
super(credential);
if (!(credential.response instanceof AuthenticatorAssertionResponse)) {
throw new Error("Invalid authenticator response");
}
this.response = {
authenticatorData: Utils.fromBufferToUrlB64(credential.response.authenticatorData),
signature: Utils.fromBufferToUrlB64(credential.response.signature),
clientDataJSON: Utils.fromBufferToUrlB64(credential.response.clientDataJSON),
userHandle: Utils.fromBufferToUrlB64(credential.response.userHandle),
authenticatorData: Utils.fromBufferToUrlB64(
credential.response.authenticatorData.buffer as ArrayBuffer,
),
signature: Utils.fromBufferToUrlB64(credential.response.signature.buffer as ArrayBuffer),
clientDataJSON: Utils.fromBufferToUrlB64(
credential.response.clientDataJSON.buffer as ArrayBuffer,
),
userHandle: Utils.fromBufferToUrlB64(credential.response.userHandle.buffer as ArrayBuffer),
};
}

View File

@@ -1,3 +1,5 @@
import { PublicKeyCredential } from "@bitwarden/common/auth/abstractions/webauthn/navigator-credentials.service";
import { Utils } from "../../../../platform/misc/utils";
export abstract class WebAuthnLoginResponseRequest {
@@ -8,7 +10,7 @@ export abstract class WebAuthnLoginResponseRequest {
constructor(credential: PublicKeyCredential) {
this.id = credential.id;
this.rawId = Utils.fromBufferToUrlB64(credential.rawId);
this.rawId = Utils.fromBufferToUrlB64(credential.rawId.buffer as ArrayBuffer);
this.type = credential.type;
// WARNING: do not add PRF information here by mapping

View File

@@ -43,15 +43,13 @@ export class WebAuthnLoginService implements WebAuthnLoginServiceAbstraction {
try {
const response = await this.navigatorCredentialsService.get(nativeOptions);
if (!(response instanceof PublicKeyCredential)) {
return undefined;
}
// TODO: Remove `any` when typescript typings add support for PRF
const prfResult = (response.getClientExtensionResults() as any).prf?.results?.first;
const prfResult = response.prf;
let symmetricPrfKey: PrfKey | undefined;
if (prfResult != undefined) {
symmetricPrfKey =
await this.webAuthnLoginPrfKeyService.createSymmetricKeyFromPrf(prfResult);
symmetricPrfKey = await this.webAuthnLoginPrfKeyService.createSymmetricKeyFromPrf(
prfResult.buffer as ArrayBuffer,
);
}
const deviceResponse = new WebAuthnLoginAssertionResponseRequest(response);