1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-03 09:03:32 +00:00
Files
browser/libs/common/src/auth/services/webauthn-login/webauthn-login.service.ts
Jake Fink 816bcf4f39 [PM-5255] Create login strategy service (#7750)
* refactor login strategies into own service

* create login service factory

* replaces instances of authService with loginStrategyService

* replace more instances of authService

* move logout back to auth service

* add browser dependencies

* fix desktop dependencies

* fix cli dependencies

* fix lint and test files

* fix anonymous hub deps

* fix webauthn-login service deps

* add loginstrategyservice to bg

* move login strategy service and models to auth folder

* revert changes to tsconfig

* use alias for imports

* fix path

---------

Co-authored-by: rr-bw <102181210+rr-bw@users.noreply.github.com>
2024-02-05 14:26:41 -05:00

93 lines
4.1 KiB
TypeScript

import { Observable } from "rxjs";
import { LoginStrategyServiceAbstraction, WebAuthnLoginCredentials } from "@bitwarden/auth/common";
import { FeatureFlag } from "../../../enums/feature-flag.enum";
import { ConfigServiceAbstraction } from "../../../platform/abstractions/config/config.service.abstraction";
import { LogService } from "../../../platform/abstractions/log.service";
import { PrfKey } from "../../../types/key";
import { WebAuthnLoginApiServiceAbstraction } from "../../abstractions/webauthn/webauthn-login-api.service.abstraction";
import { WebAuthnLoginPrfCryptoServiceAbstraction } from "../../abstractions/webauthn/webauthn-login-prf-crypto.service.abstraction";
import { WebAuthnLoginServiceAbstraction } from "../../abstractions/webauthn/webauthn-login.service.abstraction";
import { AuthResult } from "../../models/domain/auth-result";
import { WebAuthnLoginCredentialAssertionOptionsView } from "../../models/view/webauthn-login/webauthn-login-credential-assertion-options.view";
import { WebAuthnLoginCredentialAssertionView } from "../../models/view/webauthn-login/webauthn-login-credential-assertion.view";
import { WebAuthnLoginAssertionResponseRequest } from "./request/webauthn-login-assertion-response.request";
export class WebAuthnLoginService implements WebAuthnLoginServiceAbstraction {
readonly enabled$: Observable<boolean>;
private navigatorCredentials: CredentialsContainer;
constructor(
private webAuthnLoginApiService: WebAuthnLoginApiServiceAbstraction,
private loginStrategyService: LoginStrategyServiceAbstraction,
private configService: ConfigServiceAbstraction,
private webAuthnLoginPrfCryptoService: WebAuthnLoginPrfCryptoServiceAbstraction,
private window: Window,
private logService?: LogService,
) {
this.enabled$ = this.configService.getFeatureFlag$(FeatureFlag.PasswordlessLogin, false);
this.navigatorCredentials = this.window.navigator.credentials;
}
async getCredentialAssertionOptions(): Promise<WebAuthnLoginCredentialAssertionOptionsView> {
const response = await this.webAuthnLoginApiService.getCredentialAssertionOptions();
return new WebAuthnLoginCredentialAssertionOptionsView(response.options, response.token);
}
async assertCredential(
credentialAssertionOptions: WebAuthnLoginCredentialAssertionOptionsView,
): Promise<WebAuthnLoginCredentialAssertionView> {
const nativeOptions: CredentialRequestOptions = {
publicKey: credentialAssertionOptions.options,
};
// TODO: Remove `any` when typescript typings add support for PRF
nativeOptions.publicKey.extensions = {
prf: { eval: { first: await this.webAuthnLoginPrfCryptoService.getLoginWithPrfSalt() } },
} as any;
try {
const response = await this.navigatorCredentials.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;
let symmetricPrfKey: PrfKey | undefined;
if (prfResult != undefined) {
symmetricPrfKey =
await this.webAuthnLoginPrfCryptoService.createSymmetricKeyFromPrf(prfResult);
}
const deviceResponse = new WebAuthnLoginAssertionResponseRequest(response);
// Verify that we aren't going to send PRF information to the server in any case.
// Note: this will only happen if a dev has done something wrong.
if ("prf" in deviceResponse.extensions) {
throw new Error("PRF information is not allowed to be sent to the server.");
}
return new WebAuthnLoginCredentialAssertionView(
credentialAssertionOptions.token,
deviceResponse,
symmetricPrfKey,
);
} catch (error) {
this.logService?.error(error);
return undefined;
}
}
async logIn(assertion: WebAuthnLoginCredentialAssertionView): Promise<AuthResult> {
const credential = new WebAuthnLoginCredentials(
assertion.token,
assertion.deviceResponse,
assertion.prfKey,
);
const result = await this.loginStrategyService.logIn(credential);
return result;
}
}