mirror of
https://github.com/bitwarden/browser
synced 2025-12-23 11:43:46 +00:00
116 lines
3.8 KiB
TypeScript
116 lines
3.8 KiB
TypeScript
import { BehaviorSubject } from "rxjs";
|
|
import { Jsonify } from "type-fest";
|
|
|
|
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
|
import { WebAuthnLoginTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/webauthn-login-token.request";
|
|
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
|
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
|
import { UserId } from "@bitwarden/common/types/guid";
|
|
import { UserKey } from "@bitwarden/common/types/key";
|
|
|
|
import { WebAuthnLoginCredentials } from "../models/domain/login-credentials";
|
|
import { CacheData } from "../services/login-strategies/login-strategy.state";
|
|
|
|
import { LoginStrategy, LoginStrategyData } from "./login.strategy";
|
|
|
|
export class WebAuthnLoginStrategyData implements LoginStrategyData {
|
|
tokenRequest: WebAuthnLoginTokenRequest;
|
|
captchaBypassToken?: string;
|
|
credentials: WebAuthnLoginCredentials;
|
|
|
|
static fromJSON(obj: Jsonify<WebAuthnLoginStrategyData>): WebAuthnLoginStrategyData {
|
|
return Object.assign(new WebAuthnLoginStrategyData(), obj, {
|
|
tokenRequest: WebAuthnLoginTokenRequest.fromJSON(obj.tokenRequest),
|
|
credentials: WebAuthnLoginCredentials.fromJSON(obj.credentials),
|
|
});
|
|
}
|
|
}
|
|
|
|
export class WebAuthnLoginStrategy extends LoginStrategy {
|
|
protected cache: BehaviorSubject<WebAuthnLoginStrategyData>;
|
|
|
|
constructor(
|
|
data: WebAuthnLoginStrategyData,
|
|
...sharedDeps: ConstructorParameters<typeof LoginStrategy>
|
|
) {
|
|
super(...sharedDeps);
|
|
|
|
this.cache = new BehaviorSubject(data);
|
|
}
|
|
|
|
async logIn(credentials: WebAuthnLoginCredentials) {
|
|
const data = new WebAuthnLoginStrategyData();
|
|
data.credentials = credentials;
|
|
data.tokenRequest = new WebAuthnLoginTokenRequest(
|
|
credentials.token,
|
|
credentials.deviceResponse,
|
|
await this.buildDeviceRequest(),
|
|
);
|
|
this.cache.next(data);
|
|
|
|
const [authResult] = await this.startLogIn();
|
|
return authResult;
|
|
}
|
|
|
|
async logInTwoFactor(): Promise<AuthResult> {
|
|
throw new Error("2FA not supported yet for WebAuthn Login.");
|
|
}
|
|
|
|
protected override async setMasterKey(response: IdentityTokenResponse, userId: UserId) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
protected override async setUserKey(idTokenResponse: IdentityTokenResponse, userId: UserId) {
|
|
const masterKeyEncryptedUserKey = idTokenResponse.key;
|
|
|
|
if (masterKeyEncryptedUserKey) {
|
|
// set the master key encrypted user key if it exists
|
|
await this.cryptoService.setMasterKeyEncryptedUserKey(masterKeyEncryptedUserKey, userId);
|
|
}
|
|
|
|
const userDecryptionOptions = idTokenResponse?.userDecryptionOptions;
|
|
|
|
if (userDecryptionOptions?.webAuthnPrfOption) {
|
|
const webAuthnPrfOption = idTokenResponse.userDecryptionOptions?.webAuthnPrfOption;
|
|
|
|
const credentials = this.cache.value.credentials;
|
|
// confirm we still have the prf key
|
|
if (!credentials.prfKey) {
|
|
return;
|
|
}
|
|
|
|
// decrypt prf encrypted private key
|
|
const privateKey = await this.cryptoService.decryptToBytes(
|
|
webAuthnPrfOption.encryptedPrivateKey,
|
|
credentials.prfKey,
|
|
);
|
|
|
|
// decrypt user key with private key
|
|
const userKey = await this.cryptoService.rsaDecrypt(
|
|
webAuthnPrfOption.encryptedUserKey.encryptedString,
|
|
privateKey,
|
|
);
|
|
|
|
if (userKey) {
|
|
await this.cryptoService.setUserKey(new SymmetricCryptoKey(userKey) as UserKey, userId);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override async setPrivateKey(
|
|
response: IdentityTokenResponse,
|
|
userId: UserId,
|
|
): Promise<void> {
|
|
await this.cryptoService.setPrivateKey(
|
|
response.privateKey ?? (await this.createKeyPairForOldAccount(userId)),
|
|
userId,
|
|
);
|
|
}
|
|
|
|
exportCache(): CacheData {
|
|
return {
|
|
webAuthn: this.cache.value,
|
|
};
|
|
}
|
|
}
|