mirror of
https://github.com/bitwarden/browser
synced 2026-01-03 09:03:32 +00:00
* 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>
93 lines
4.1 KiB
TypeScript
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;
|
|
}
|
|
}
|