Skip to content

Commit

Permalink
Auth/PM-2041 - Finish adding FIDO2 Authentication + Decryption to Web…
Browse files Browse the repository at this point in the history
… Client (#6798)

* PM-2041 - (1) Bring over WebauthnApiService + required models from existing #5493 PR (2) Per discussion with Andreas, remove unnecessary methods from WebauthnApiService

* PM-2041 - Rename responses folder to response to match rest of codebase

* PM-2041 - Recreate  BaseLoginViaWebAuthnComponent and then web implementation of it.

* PM-2041 - Web routing module - add LoginViaWebAuthnComponent and associated route "login-with-passkey"

* PM-2041 - InjectionTokens - add new navigator credentials injection token which provides the CredentialsContainer interface of the Credential Management API and exposes methods to request credentials and notify the user agent when events such as successful sign in or sign out happen

* PM-2041 - Rename WebauthnApiService & abstraction to WebAuthnLoginApiService

* PM-2041 - Rename WebauthnLoginApiService to WebAuthnAdminApiService

* PM-2041 - Bring over first draft of webauthn-login.service + abstraction; register on jslib-services.module.

* PM-2041 - Bring over web & base login component changes to add login with passkey button if feature flag enabled.

* PM-2041 - WebAuthnAdminApi - update list of TODOs based on conversation with Andreas

* PM-2041 - Login.module - cleanup todo after conversation w/ Andreas

* PM-2041 - Move utils out of web and into common auth/utils and renamed to webauthn-utils

* PM-2041 - Update userDecryptionOptions to support new webauthn prf decryption option

* PM-2041 - (1) Recreate webauthn-login service with updated logic (2) Move files from webauthn to webauthn-login (3) Recreate webauthn-login.strategy with updated logic

* PM-2041 - Remove completed TODO

* PM-2041 - Fix login-via-webauthn component imports + fix name (missing n)

* PM-2041 - Missed this change when renaming LoginViaWebAuthComponent to LoginViaWebAuthnComponent

* PM-2041 - Add WebAuthnLoginApiService to jslib-services.module

* PM-2041 - Remove unused param from WebAuthnLoginApiServiceAbstraction as we aren't supporting non-discoverable passkeys for MVP

* PM-2041 - WebAuthnLoginApiService - remove email and target correct endpoint for getCredentialAssertionOptions(...) call

* PM-2041 - WebAuthnLoginStrategy - (1) Remove unused dep (2) Add safeguard checks to setUserKey(...) logic similar to SSO login strategy

* PM-2041 - BaseLoginViaWebAuthnComponent - Rewrite authenticate logic to use new methods on webAuthnLoginService

* PM-2041 - UserDecryptionOptionsResponse - update naming of webAuthn options object to match server response

* PM-2041 - WebAuthnLoginAssertionResponseRequest - (1) clean up TODO (2) Fix response property name to match server

* PM-2041 - WebAuthnTokenRequest - must stringify device response b/c sending as form data

* PM-2041 - AuthService - Add WebAuthnLoginCredentials and WebAuthnLoginStrategy support to auth service

* PM-2041 - WIP tests for WebAuthnLoginService

* PM-2041 - UserDecryptionOptions - Rename WebAuthnPrfOptions to singular WebAuthnPrfOption to match server

* PM-2041 - Add TODO in login comp

* PM-2041 - (1) Update WebAuthnLoginService.assertCredential(...) to add a check to ensure we cannot leak PRF credentials to the BW server by mistake (2) Add credential to view names for clarity (3) Add JS doc style comments to WebAuthnLoginServiceAbstraction

* PM-2041 - Login.component.html - (1) Center passkey login button (2) Use correct user passkey icon

* PM-2041 - Utils + tests - (1) Add new hexStringToArrayBuffer(...) method (2) Add tests for existing fromBufferToHex(...) (3) Add tests for new hexStringToArrayBuffer(...) method

* PM-2041 - Fix broken import

* PM-2041 - WebAuthnLoginResponseRequest - Adjust warning to be correct

* PM-2041 - Webauthn-utils - createSymmetricKeyFromPrf(...) - add return type

* PM-2041 - WebAuthnLoginService spec file - good progress on figuring out how to test passkey assertion process. Tests are passing, but need to add more setup logic around the MockAuthenticatorAssertionResponse in order to be able to confirm the output is correct.

* PM-2041 - Utils + Utils Spec file changes - (1) Add new fromB64ToArrayBuffer(...) method (2) Add tests for existing fromBufferToB64(...) (3) Add tests for new fromB64ToArrayBuffer(...) method (4) Add round trip conversion tests in both directions

* PM-2041 - Utils.spec - update round trip conversion tests between hex string and array buffer.

* PM-2041 - WebAuthnLoginService.spec - assertCredential(...) happy path test passing

* PM-2041 - WebAuthnLoginAssertionResponseRequest - Add interface

* PM-2041 - WebAuthnLoginAssertionResponseRequest data should be UrlB64 strings per discussion w/ Andreas

* PM-2041 - WebAuthnLoginService Spec file - Per feedback, reverse approaches to generating test data (go from array buffer to b64 strings vs the reverse) to avoid using math.random which can introduce test inconsistency

* PM-2041 - Finish testing assertCredential(...)

* PM-2041 - WebAuthnLoginService tests completed - tested logIn method

* PM-2041 - Login html - add "or" between standard email login and passkey login

* PM-2041 - WebAuthnLoginStrategy test start

* PM-2041 - After rebase - BaseLoginViaWebAuthnComponent - Must rename ForceResetPasswordReason to ForceSetPasswordReason + refactor post login routing logic to match other auth owned flows.

* PM-2401 - Desktop - login comp - fix desktop build

* PM-2041 - Browser - login comp - fix build issue

* PM-2401 - WIP on webauthn-login.strategy testing

* PM-2401 - Finish testing webauthn login strategy

* PM-2041 - WebAuthnAdminApiService renamed to WebAuthnLoginAdminApiService

* PM-2041 - Remove unnecessary comment

* PM-2041 - Per PR feedback, remove noMargin and just add mb-3

* PM-2041 - Per PR feedback, remove unused 2FA and remember email logic (2FA isn't supported right now and we aren't using non-discoverable credentials so we aren't using a user entered email)

* PM-2401 - BaseLoginViaWebAuthnComponent - improve error handling to allow users to retry w/ another passkey

* PM-2401 - Per PR feedback, provide translated message to cover all invalid passkey scenarios.

* PM-2401 - WebAuthnLoginService - per PR feedback, remove unnecessary from

* PM-2041 - WebAuthnLoginCredentialAssertionView - per PR feedback, use actual key type

* PM-2401 - Per PR feedback, remove WebAuthnLoginStrategy constructor as it is identical to its super class constructor

* PM-2041 - WebAuthnLoginService tests - use first value from to improve tests

* PM-2401 - Fix WebAuthnLoginService build issue after changing SymmetricCryptoKey to PrfKey

* PM-2041 - WebAuthnLoginServiceAbstraction remove incorrect undefined from getCredentialAssertionOptions() abstraction

* PM-2041 - Refacor WebAuthn login service tests based on PR feedback

* PM-2041 - Per PR feedback, remove NAVIGATOR_CREDENTIALS injection token and just use WINDOW directly for WebAuthnLoginService

* PM-2041 - WebAuthnLoginServiceAbstraction - per PR feedback, improve assertCredential jsdocs with return info

* PM-2041 - Per PR feedback, update WebAuthnLoginStrategy logInTwoFactor(...) to return an exception if attempted to be called.

* PM-2041 - WebAuthnLoginResponseRequest - per PR feedback, replace fromBufferToB64(...) with fromBufferToUrlB64(...)

* PM-2041 - AssertionOptionsResponse - use doc comment per PR feedback

* PM-2041 - Per PR feedback, adjust location of helpers and mocks in WebAuthnLoginStrategy test file

* PM-2041 - Adjust WebAuthnLoginService tests to take the WebAuthnLoginResponseRequest change to use fromBufferToUrlB64(...) into account to get tests to pass again

* PM-2041 - WebAuthnLoginStrategy - adjust test name to match convention per PR feedback

* PM-2041 - More test tweaks - (1) Rename method (2) Support strict

* PM-2041 - Per PR feedback, AssertionOptionsResponse constructor should null check allowCredentials b/c it is optional

* PM-2041 - Per PR Feedback, remove duplicated fromB64ToArrayBuffer(...) from utils and update tests.

* PM-2041 - Per PR feedback, rename WebAuthnTokenRequest to WebAuthnLoginTokenRequest

* PM-2041 - Per discussion with product and Andreas, add 2FA transition handling just in case we add server support in the future.

* feat: stretch PRF key (#6927)

* feat: stretch PRF key

includes necessary utils -> service refactors

* feat: add tests

* [PM-2041] feat: assertion-options `POST` -> `GET`

* [PM-2041] chore: remove unused properties

* [PM-2041] fix: set private key

* [PM-2041] feat: remove all 2FA related fields

* [PM-2041] chore: clean up 2FA comments

* [PM-2041] chore: document `webauthn-login-prf-crypto.service.abstraction.ts`

* [PM-2041] chore: document webauthn login services

---------

Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com>
Co-authored-by: Andreas Coroiu <andreas.coroiu@gmail.com>
  • Loading branch information
3 people authored Nov 22, 2023
1 parent 2be9273 commit 3a0603a
Show file tree
Hide file tree
Showing 47 changed files with 1,864 additions and 42 deletions.
7 changes: 5 additions & 2 deletions apps/browser/src/auth/popup/login.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstrac
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login.service.abstraction";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
Expand Down Expand Up @@ -43,7 +44,8 @@ export class LoginComponent extends BaseLoginComponent {
formBuilder: FormBuilder,
formValidationErrorService: FormValidationErrorsService,
route: ActivatedRoute,
loginService: LoginService
loginService: LoginService,
webAuthnLoginService: WebAuthnLoginServiceAbstraction
) {
super(
devicesApiService,
Expand All @@ -61,7 +63,8 @@ export class LoginComponent extends BaseLoginComponent {
formBuilder,
formValidationErrorService,
route,
loginService
loginService,
webAuthnLoginService
);
super.onSuccessfulLogin = async () => {
await syncService.fullSync(true);
Expand Down
7 changes: 5 additions & 2 deletions apps/desktop/src/auth/login/login.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ModalService } from "@bitwarden/angular/services/modal.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login.service.abstraction";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
Expand Down Expand Up @@ -71,7 +72,8 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy {
formBuilder: FormBuilder,
formValidationErrorService: FormValidationErrorsService,
route: ActivatedRoute,
loginService: LoginService
loginService: LoginService,
webAuthnLoginService: WebAuthnLoginServiceAbstraction
) {
super(
devicesApiService,
Expand All @@ -89,7 +91,8 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy {
formBuilder,
formValidationErrorService,
route,
loginService
loginService,
webAuthnLoginService
);
super.onSuccessfulLogin = () => {
return syncService.fullSync(true);
Expand Down
15 changes: 0 additions & 15 deletions apps/web/src/app/auth/core/services/webauthn-login/utils.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { WebauthnLoginCredentialCreateOptionsResponse } from "./response/webauth
import { WebauthnLoginCredentialResponse } from "./response/webauthn-login-credential.response";

@Injectable({ providedIn: "root" })
export class WebauthnLoginApiService {
export class WebAuthnLoginAdminApiService {
constructor(private apiService: ApiService) {}

async getCredentialCreateOptions(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
import { mock, MockProxy } from "jest-mock-extended";

import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { WebAuthnLoginPrfCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login-prf-crypto.service.abstraction";

import { CredentialCreateOptionsView } from "../../views/credential-create-options.view";
import { PendingWebauthnLoginCredentialView } from "../../views/pending-webauthn-login-credential.view";
import { RotateableKeySetService } from "../rotateable-key-set.service";

import { WebAuthnLoginAdminApiService } from "./webauthn-login-admin-api.service";
import { WebauthnLoginAdminService } from "./webauthn-login-admin.service";
import { WebauthnLoginApiService } from "./webauthn-login-api.service";

describe("WebauthnAdminService", () => {
let apiService!: MockProxy<WebauthnLoginApiService>;
let apiService!: MockProxy<WebAuthnLoginAdminApiService>;
let userVerificationService!: MockProxy<UserVerificationService>;
let rotateableKeySetService!: MockProxy<RotateableKeySetService>;
let webAuthnLoginPrfCryptoService!: MockProxy<WebAuthnLoginPrfCryptoServiceAbstraction>;
let credentials: MockProxy<CredentialsContainer>;
let service!: WebauthnLoginAdminService;

beforeAll(() => {
// Polyfill missing class
window.PublicKeyCredential = class {} as any;
window.AuthenticatorAttestationResponse = class {} as any;
apiService = mock<WebauthnLoginApiService>();
apiService = mock<WebAuthnLoginAdminApiService>();
userVerificationService = mock<UserVerificationService>();
rotateableKeySetService = mock<RotateableKeySetService>();
webAuthnLoginPrfCryptoService = mock<WebAuthnLoginPrfCryptoServiceAbstraction>();
credentials = mock<CredentialsContainer>();
service = new WebauthnLoginAdminService(
apiService,
userVerificationService,
rotateableKeySetService,
webAuthnLoginPrfCryptoService,
credentials
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BehaviorSubject, filter, from, map, Observable, shareReplay, switchMap,

import { PrfKeySet } from "@bitwarden/auth";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { WebAuthnLoginPrfCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login-prf-crypto.service.abstraction";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { Verification } from "@bitwarden/common/types/verification";

Expand All @@ -13,10 +14,12 @@ import { RotateableKeySetService } from "../rotateable-key-set.service";

import { SaveCredentialRequest } from "./request/save-credential.request";
import { WebauthnLoginAttestationResponseRequest } from "./request/webauthn-login-attestation-response.request";
import { createSymmetricKeyFromPrf, getLoginWithPrfSalt } from "./utils";
import { WebauthnLoginApiService } from "./webauthn-login-api.service";
import { WebAuthnLoginAdminApiService } from "./webauthn-login-admin-api.service";

@Injectable({ providedIn: "root" })
/**
* Service for managing WebAuthnLogin credentials.
*/
export class WebauthnLoginAdminService {
static readonly MaxCredentialCount = 5;

Expand All @@ -30,19 +33,32 @@ export class WebauthnLoginAdminService {
shareReplay({ bufferSize: 1, refCount: true })
);

/**
* An Observable that emits a boolean indicating whether the service is currently fetching
* WebAuthnLogin credentials from the server.
*/
readonly loading$ = this._loading$.asObservable();

constructor(
private apiService: WebauthnLoginApiService,
private apiService: WebAuthnLoginAdminApiService,
private userVerificationService: UserVerificationService,
private rotateableKeySetService: RotateableKeySetService,
private webAuthnLoginPrfCryptoService: WebAuthnLoginPrfCryptoServiceAbstraction,
@Optional() navigatorCredentials?: CredentialsContainer,
@Optional() private logService?: LogService
) {
// Default parameters don't work when used with Angular DI
this.navigatorCredentials = navigatorCredentials ?? navigator.credentials;
}

/**
* Get the credential attestation options needed for initiating the WebAuthnLogin credentail creation process.
* The options contains a challenge and other data for the authenticator.
* This method requires user verification.
*
* @param verification User verification data to be used for the request.
* @returns The credential attestation options and a token to be used for the credential creation request.
*/
async getCredentialCreateOptions(
verification: Verification
): Promise<CredentialCreateOptionsView> {
Expand All @@ -51,6 +67,12 @@ export class WebauthnLoginAdminService {
return new CredentialCreateOptionsView(response.options, response.token);
}

/**
* Create a credential using the given options. This triggers the browsers WebAuthn API to create a credential.
*
* @param credentialOptions Options received from the server using `getCredentialCreateOptions`.
* @returns A pending credential that can be saved to server directly or be used to create a key set.
*/
async createCredential(
credentialOptions: CredentialCreateOptionsView
): Promise<PendingWebauthnLoginCredentialView | undefined> {
Expand All @@ -76,6 +98,13 @@ export class WebauthnLoginAdminService {
}
}

/**
* Create a key set from the given pending credential. The credential must support PRF.
* This will trigger the browsers WebAuthn API to generate a PRF-output.
*
* @param pendingCredential A credential created using `createCredential`.
* @returns A key set that can be saved to the server. Undefined is returned if the credential doesn't support PRF.
*/
async createKeySet(
pendingCredential: PendingWebauthnLoginCredentialView
): Promise<PrfKeySet | undefined> {
Expand All @@ -88,7 +117,9 @@ export class WebauthnLoginAdminService {
userVerification:
pendingCredential.createOptions.options.authenticatorSelection.userVerification,
// TODO: Remove `any` when typescript typings add support for PRF
extensions: { prf: { eval: { first: await getLoginWithPrfSalt() } } } as any,
extensions: {
prf: { eval: { first: await this.webAuthnLoginPrfCryptoService.getLoginWithPrfSalt() } },
} as any,
},
};

Expand All @@ -105,14 +136,23 @@ export class WebauthnLoginAdminService {
return undefined;
}

const symmetricPrfKey = createSymmetricKeyFromPrf(prfResult);
const symmetricPrfKey = await this.webAuthnLoginPrfCryptoService.createSymmetricKeyFromPrf(
prfResult
);
return await this.rotateableKeySetService.createKeySet(symmetricPrfKey);
} catch (error) {
this.logService?.error(error);
return undefined;
}
}

/**
* Save a pending credential to the server. This will also save the key set if it is provided.
*
* @param name User provided name for the credential.
* @param credential A pending credential created using `createCredential`.
* @param prfKeySet A key set created using `createKeySet`.
*/
async saveCredential(
name: string,
credential: PendingWebauthnLoginCredentialView,
Expand Down Expand Up @@ -144,13 +184,26 @@ export class WebauthnLoginAdminService {
return this.credentials$;
}

/**
* Subscribe to a single credential by id.
*
* @param credentialId The id of the credential to subscribe to.
* @returns An observable that emits the credential with the given id.
*/
getCredential$(credentialId: string): Observable<WebauthnLoginCredentialView> {
return this.credentials$.pipe(
map((credentials) => credentials.find((c) => c.id === credentialId)),
filter((c) => c !== undefined)
);
}

/**
* Delete a credential from the server. This method requires user verification.
*
* @param credentialId The id of the credential to delete.
* @param verification User verification data to be used for the request.
* @returns A promise that resolves when the credential has been deleted.
*/
async deleteCredential(credentialId: string, verification: Verification): Promise<void> {
const request = await this.userVerificationService.buildRequest(verification);
await this.apiService.deleteCredential(credentialId, request);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<div
class="tw-mx-auto tw-mt-5 tw-flex tw-max-w-lg tw-flex-col tw-items-center tw-justify-center tw-p-8"
>
<div>
<img class="logo logo-themed" alt="Bitwarden" />
<h3 bitTypography="h3" class="tw-my-8 tw-mb-3 tw-text-center">
{{ "readingPasskeyLoading" | i18n }}
</h3>

<div
class="tw-rounded-md tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-6"
>
<div class="tw-flex tw-flex-col tw-items-center">
<ng-container *ngIf="currentState === 'assert'">
<bit-icon [icon]="Icons.CreatePasskeyIcon" class="tw-my-10"></bit-icon>
<p bitTypography="body1">{{ "readingPasskeyLoadingInfo" | i18n }}</p>
<button
type="button"
bitButton
block
[loading]="true"
buttonType="primary"
class="tw-mb-4"
>
{{ "loading" | i18n }}
</button>
</ng-container>

<ng-container *ngIf="currentState === 'assertFailed'">
<bit-icon [icon]="Icons.CreatePasskeyFailedIcon" class="tw-my-10"></bit-icon>
<p bitTypography="body1">{{ "readingPasskeyLoadingInfo" | i18n }}</p>
<button
type="button"
bitButton
block
buttonType="primary"
class="tw-mb-4"
(click)="retry()"
>
{{ "tryAgain" | i18n }}
</button>
</ng-container>
</div>
<p bitTypography="body1" class="tw-mb-0">
{{ "troubleLoggingIn" | i18n }}<br />
<a routerLink="/login">{{ "useADifferentLogInMethod" | i18n }}</a>
</p>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Component } from "@angular/core";

import { BaseLoginViaWebAuthnComponent } from "@bitwarden/angular/auth/components/base-login-via-webauthn.component";
import { CreatePasskeyFailedIcon } from "@bitwarden/angular/auth/icons/create-passkey-failed.icon";
import { CreatePasskeyIcon } from "@bitwarden/angular/auth/icons/create-passkey.icon";

@Component({
selector: "app-login-via-webauthn",
templateUrl: "login-via-webauthn.component.html",
})
export class LoginViaWebAuthnComponent extends BaseLoginViaWebAuthnComponent {
protected readonly Icons = { CreatePasskeyIcon, CreatePasskeyFailedIcon };
}
17 changes: 17 additions & 0 deletions apps/web/src/app/auth/login/login.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@
</button>
</div>

<div
class="tw-mb-3 tw-flex tw-flex-col tw-items-center tw-justify-center"
*ngIf="showWebauthnLogin$ | async"
>
<p class="tw-mb-3">{{ "or" | i18n }}</p>

<a
bitLink
block
linkType="primary"
routerLink="/login-with-passkey"
(mousedown)="$event.preventDefault()"
>
<span><i class="bwi bwi-passkey"></i> {{ "loginWithPasskey" | i18n }}</span>
</a>
</div>

<hr />

<p class="tw-m-0 tw-text-sm">
Expand Down
7 changes: 5 additions & 2 deletions apps/web/src/app/auth/login/login.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login.service.abstraction";
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
Expand Down Expand Up @@ -62,7 +63,8 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
private routerService: RouterService,
formBuilder: FormBuilder,
formValidationErrorService: FormValidationErrorsService,
loginService: LoginService
loginService: LoginService,
webAuthnLoginService: WebAuthnLoginServiceAbstraction
) {
super(
devicesApiService,
Expand All @@ -80,7 +82,8 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
formBuilder,
formValidationErrorService,
route,
loginService
loginService,
webAuthnLoginService
);
this.onSuccessfulLogin = async () => {
this.messagingService.send("setFullWidth");
Expand Down
Loading

0 comments on commit 3a0603a

Please sign in to comment.