From 07c5140d963a9f607d7f0d9cb829efd39d60cd41 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Thu, 4 Aug 2022 00:22:59 -0600 Subject: [PATCH 01/35] passwordless login page redesign --- .../web/src/app/accounts/login.component.html | 123 ++++++++++++++++++ apps/web/src/app/accounts/login.component.ts | 7 +- apps/web/src/locales/en/messages.json | 6 + .../angular/src/components/login.component.ts | 11 +- 4 files changed, 144 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/accounts/login.component.html b/apps/web/src/app/accounts/login.component.html index e0c4ef68db79..5fca4ac459be 100644 --- a/apps/web/src/app/accounts/login.component.html +++ b/apps/web/src/app/accounts/login.component.html @@ -100,3 +100,126 @@ + + + +
+
+
+ +

+ {{ "loginOrCreateNewAccount" | i18n }} +

+
+ + {{ "resetPasswordAutoEnrollInviteWarning" | i18n }} + + +
+ + {{ "emailAddress" | i18n }} + + +
+ +
+ + {{ "masterPass" | i18n }} + + + + {{ "getMasterPasswordHint" | i18n }} + + +
+ +
+
+ +
+ + {{ "rememberEmail" | i18n }} + +
+ +
+ +
+ +
+ +
+ + {{ "logIn" | i18n }} + + + + + {{ "createAccount" | i18n }} + +
+ + + + +
+
+
+
+
diff --git a/apps/web/src/app/accounts/login.component.ts b/apps/web/src/app/accounts/login.component.ts index e8af9bc2f7f6..d66d29b2a1bb 100644 --- a/apps/web/src/app/accounts/login.component.ts +++ b/apps/web/src/app/accounts/login.component.ts @@ -1,4 +1,5 @@ import { Component, NgZone } from "@angular/core"; +import { UntypedFormBuilder } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { first } from "rxjs/operators"; @@ -45,7 +46,8 @@ export class LoginComponent extends BaseLoginComponent { ngZone: NgZone, protected stateService: StateService, private messagingService: MessagingService, - private routerService: RouterService + private routerService: RouterService, + formBuilder: UntypedFormBuilder ) { super( authService, @@ -57,7 +59,8 @@ export class LoginComponent extends BaseLoginComponent { passwordGenerationService, cryptoFunctionService, logService, - ngZone + ngZone, + formBuilder ); this.onSuccessfulLogin = async () => { this.messagingService.send("setFullWidth"); diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 71f3bd7b8f65..df939986e54f 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -569,9 +569,15 @@ "loginOrCreateNewAccount": { "message": "Log in or create a new account to access your secure vault." }, + "loginWithDevice" : { + "message": "Log in with device" + }, "createAccount": { "message": "Create Account" }, + "newAroundHere": { + "message": "New around here?" + }, "startTrial": { "message": "Start Trial" }, diff --git a/libs/angular/src/components/login.component.ts b/libs/angular/src/components/login.component.ts index c784a0aa062b..1eabb9cbbd28 100644 --- a/libs/angular/src/components/login.component.ts +++ b/libs/angular/src/components/login.component.ts @@ -1,4 +1,5 @@ import { Directive, Input, NgZone, OnInit } from "@angular/core"; +import { UntypedFormBuilder, Validators } from "@angular/forms"; import { Router } from "@angular/router"; import { take } from "rxjs/operators"; @@ -23,12 +24,19 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit masterPassword = ""; showPassword = false; + showLoginOptions = true; formPromise: Promise; onSuccessfulLogin: () => Promise; onSuccessfulLoginNavigate: () => Promise; onSuccessfulLoginTwoFactorNavigate: () => Promise; onSuccessfulLoginForceResetNavigate: () => Promise; + formGroup = this.formBuilder.group({ + email: ["", [Validators.required, Validators.email]], + masterPassword: ["", [Validators.required, Validators.minLength(8)]], + rememberEmail: [false], + }); + protected twoFactorRoute = "2fa"; protected successRoute = "vault"; protected forcePasswordResetRoute = "update-temp-password"; @@ -44,7 +52,8 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit protected passwordGenerationService: PasswordGenerationService, protected cryptoFunctionService: CryptoFunctionService, protected logService: LogService, - protected ngZone: NgZone + protected ngZone: NgZone, + protected formBuilder: UntypedFormBuilder ) { super(environmentService, i18nService, platformUtilsService); } From 8d7c4b327e860b6a5dda6deea926ae1347c1e6de Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Thu, 4 Aug 2022 00:31:28 -0600 Subject: [PATCH 02/35] passwordless login page redesign --- .../web/src/app/accounts/login.component.html | 196 +++++++++--------- 1 file changed, 96 insertions(+), 100 deletions(-) diff --git a/apps/web/src/app/accounts/login.component.html b/apps/web/src/app/accounts/login.component.html index 5fca4ac459be..1e3ec794ff1f 100644 --- a/apps/web/src/app/accounts/login.component.html +++ b/apps/web/src/app/accounts/login.component.html @@ -108,118 +108,114 @@ --> - -
+
-
-
- -

- {{ "loginOrCreateNewAccount" | i18n }} -

-
+ +

+ {{ "loginOrCreateNewAccount" | i18n }} +

+
+ - - {{ "resetPasswordAutoEnrollInviteWarning" | i18n }} - + {{ "resetPasswordAutoEnrollInviteWarning" | i18n }} + -
- - {{ "emailAddress" | i18n }} - - -
+
+ + {{ "emailAddress" | i18n }} + + +
-
- - {{ "masterPass" | i18n }} - - - - {{ "getMasterPasswordHint" | i18n }} - - -
+
+ + {{ "masterPass" | i18n }} + + + + {{ "getMasterPasswordHint" | i18n }} + + +
-
-
- -
- - {{ "rememberEmail" | i18n }} - +
+
+
+ + {{ "rememberEmail" | i18n }} + +
-
+
-
- -
+
+ +
-
- - {{ "logIn" | i18n }} - +
+ + {{ "logIn" | i18n }} + - - - {{ "createAccount" | i18n }} - -
+ + + {{ "createAccount" | i18n }} + +
- + - +
- - +
+ From 6f24fecb781c40c1ab5b8533b07fdc2c378ee140 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Thu, 4 Aug 2022 15:29:22 -0600 Subject: [PATCH 03/35] restyled login form to use tailwind --- .../web/src/app/accounts/login.component.html | 22 ++-- apps/web/src/app/accounts/login.component.ts | 29 +++-- apps/web/src/locales/en/messages.json | 2 +- .../angular/src/components/login.component.ts | 102 ++++++++++-------- 4 files changed, 90 insertions(+), 65 deletions(-) diff --git a/apps/web/src/app/accounts/login.component.html b/apps/web/src/app/accounts/login.component.html index 1e3ec794ff1f..88c8f16f826a 100644 --- a/apps/web/src/app/accounts/login.component.html +++ b/apps/web/src/app/accounts/login.component.html @@ -1,4 +1,4 @@ -
+ - -
- + {{ "loginWithDevice" | i18n }} diff --git a/apps/web/src/app/modules/loose-components.module.ts b/apps/web/src/app/modules/loose-components.module.ts index 1d6d66b47875..e0b34625bd55 100644 --- a/apps/web/src/app/modules/loose-components.module.ts +++ b/apps/web/src/app/modules/loose-components.module.ts @@ -154,6 +154,7 @@ import { CollectionsComponent } from "../vault/collections.component"; import { FolderAddEditComponent } from "../vault/folder-add-edit.component"; import { ShareComponent } from "../vault/share.component"; +import { LoginWithDeviceComponent } from "./../accounts/login-with-device.component"; import { OrganizationCreateModule } from "./organizations/create/organization-create.module"; import { PipesModule } from "./pipes/pipes.module"; import { RegisterFormModule } from "./register-form/register-form.module"; @@ -225,6 +226,7 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga InactiveTwoFactorReportComponent, LockComponent, LoginComponent, + LoginWithDeviceComponent, MasterPasswordPolicyComponent, NavbarComponent, NestedCheckboxComponent, @@ -378,6 +380,7 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga InactiveTwoFactorReportComponent, LockComponent, LoginComponent, + LoginWithDeviceComponent, MasterPasswordPolicyComponent, NavbarComponent, NestedCheckboxComponent, diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 76501a16f496..6e7078293c3b 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -11,6 +11,7 @@ import { AcceptEmergencyComponent } from "./accounts/accept-emergency.component" import { AcceptOrganizationComponent } from "./accounts/accept-organization.component"; import { HintComponent } from "./accounts/hint.component"; import { LockComponent } from "./accounts/lock.component"; +import { LoginWithDeviceComponent } from "./accounts/login-with-device.component"; import { LoginComponent } from "./accounts/login.component"; import { RecoverDeleteComponent } from "./accounts/recover-delete.component"; import { RecoverTwoFactorComponent } from "./accounts/recover-two-factor.component"; @@ -60,6 +61,12 @@ const routes: Routes = [ canActivate: [HomeGuard], // Redirects either to vault, login or lock page. }, { path: "login", component: LoginComponent, canActivate: [UnauthGuard] }, + { + path: "login-with-device", + component: LoginWithDeviceComponent, + canActivate: [UnauthGuard], + data: { titleId: "loginWithDevice" }, + }, { path: "2fa", component: TwoFactorComponent, canActivate: [UnauthGuard] }, { path: "register", diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 4d346f28ff55..0350a278dad9 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -572,6 +572,9 @@ "loginWithDevice" : { "message": "Log in with device" }, + "loginWithDevciceEnabledInfo": { + "message": "Log in with device must be enabled in the settings of the Biwarden mobile app. Need another option?" + }, "createAccount": { "message": "Create Account" }, @@ -584,6 +587,9 @@ "logIn": { "message": "Log In" }, + "logInInitiated": { + "message": "Log in initiated" + }, "submit": { "message": "Submit" }, @@ -708,6 +714,9 @@ "noOrganizationsList": { "message": "You do not belong to any organizations. Organizations allow you to securely share items with other users." }, + "notificationSentDevice":{ + "message": "A notification has been sent to your device." + }, "versionNumber": { "message": "Version $VERSION_NUMBER$", "placeholders": { @@ -2493,6 +2502,9 @@ } } }, + "viewAllLoginOptions": { + "message": "View all log in options" + }, "viewedItemId": { "message": "Viewed item $ID$.", "placeholders": { @@ -3333,6 +3345,12 @@ "message": "To ensure the integrity of your encryption keys, please verify the user's fingerprint phrase before continuing.", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." }, + "fingerprintMatchInfo": { + "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." + }, + "fingerprintPhraseHeader": { + "message": "Fingerprint phrase" + }, "dontAskFingerprintAgain": { "message": "Never prompt to verify fingerprint phrases for invited users (Not recommended)", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -4327,6 +4345,9 @@ "reinviteSelected": { "message": "Resend Invitations" }, + "resendNotification": { + "message": "Resend notification" + }, "noSelectedUsersApplicable": { "message": "This action is not applicable to any of the selected users." }, From c3eae6b0302072e912686ae03d4059e44932cbf8 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Fri, 5 Aug 2022 16:43:26 -0600 Subject: [PATCH 06/35] made reactive form changes for clients --- .../src/popup/accounts/login.component.html | 16 +++------------- .../src/popup/accounts/login.component.ts | 10 ++++++++-- .../src/app/accounts/login.component.html | 14 +++----------- apps/desktop/src/app/accounts/login.component.ts | 10 ++++++++-- apps/web/src/app/accounts/login.component.ts | 2 +- libs/angular/src/components/login.component.ts | 5 +++++ 6 files changed, 28 insertions(+), 29 deletions(-) diff --git a/apps/browser/src/popup/accounts/login.component.html b/apps/browser/src/popup/accounts/login.component.html index 281df8350936..8951ad219033 100644 --- a/apps/browser/src/popup/accounts/login.component.html +++ b/apps/browser/src/popup/accounts/login.component.html @@ -1,4 +1,4 @@ - +
@@ -18,15 +18,7 @@

- +
@@ -34,10 +26,8 @@

diff --git a/apps/browser/src/popup/accounts/login.component.ts b/apps/browser/src/popup/accounts/login.component.ts index edd197ca1831..81feab758d30 100644 --- a/apps/browser/src/popup/accounts/login.component.ts +++ b/apps/browser/src/popup/accounts/login.component.ts @@ -1,10 +1,12 @@ import { Component, NgZone } from "@angular/core"; +import { UntypedFormBuilder } from "@angular/forms"; import { Router } from "@angular/router"; import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/components/login.component"; import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; +import { FormValidationErrorsService } from "@bitwarden/common/abstractions/formValidationErrors.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; @@ -30,7 +32,9 @@ export class LoginComponent extends BaseLoginComponent { protected cryptoFunctionService: CryptoFunctionService, syncService: SyncService, logService: LogService, - ngZone: NgZone + ngZone: NgZone, + formBuilder: UntypedFormBuilder, + formValidationErrorService: FormValidationErrorsService ) { super( authService, @@ -42,7 +46,9 @@ export class LoginComponent extends BaseLoginComponent { passwordGenerationService, cryptoFunctionService, logService, - ngZone + ngZone, + formBuilder, + formValidationErrorService ); super.onSuccessfulLogin = async () => { await syncService.fullSync(true); diff --git a/apps/desktop/src/app/accounts/login.component.html b/apps/desktop/src/app/accounts/login.component.html index 314b05506219..c11ed881b000 100644 --- a/apps/desktop/src/app/accounts/login.component.html +++ b/apps/desktop/src/app/accounts/login.component.html @@ -16,6 +16,7 @@ #form (ngSubmit)="submit()" [appApiAction]="formPromise" + [formGroup]="formGroup" attr.aria-hidden="{{ showingModal }}" >
@@ -25,14 +26,7 @@
- +
@@ -40,10 +34,8 @@
diff --git a/apps/desktop/src/app/accounts/login.component.ts b/apps/desktop/src/app/accounts/login.component.ts index 22731b21f26a..a3906c2916ac 100644 --- a/apps/desktop/src/app/accounts/login.component.ts +++ b/apps/desktop/src/app/accounts/login.component.ts @@ -1,4 +1,5 @@ import { Component, NgZone, OnDestroy, ViewChild, ViewContainerRef } from "@angular/core"; +import { UntypedFormBuilder } from "@angular/forms"; import { Router } from "@angular/router"; import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/components/login.component"; @@ -7,6 +8,7 @@ import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service"; import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; +import { FormValidationErrorsService } from "@bitwarden/common/abstractions/formValidationErrors.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; @@ -47,7 +49,9 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy { private broadcasterService: BroadcasterService, ngZone: NgZone, private messagingService: MessagingService, - logService: LogService + logService: LogService, + formBuilder: UntypedFormBuilder, + formValidationErrorService: FormValidationErrorsService ) { super( authService, @@ -59,7 +63,9 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy { passwordGenerationService, cryptoFunctionService, logService, - ngZone + ngZone, + formBuilder, + formValidationErrorService ); super.onSuccessfulLogin = () => { return syncService.fullSync(true); diff --git a/apps/web/src/app/accounts/login.component.ts b/apps/web/src/app/accounts/login.component.ts index 473cda75dc6c..6a4428ad398c 100644 --- a/apps/web/src/app/accounts/login.component.ts +++ b/apps/web/src/app/accounts/login.component.ts @@ -169,7 +169,7 @@ export class LoginComponent extends BaseLoginComponent { if (!rememberEmail) { await this.stateService.setRememberedEmail(null); } - await super.submit(); + await super.submit(false); } private getPasswordStrengthUserInput() { diff --git a/libs/angular/src/components/login.component.ts b/libs/angular/src/components/login.component.ts index e429b0e1d2ee..99e05c8855b9 100644 --- a/libs/angular/src/components/login.component.ts +++ b/libs/angular/src/components/login.component.ts @@ -210,4 +210,9 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit const name = error.errorName.charAt(0).toUpperCase() + error.errorName.slice(1); return `${error.controlName}${name}`; } + + protected focusInput() { + const email = this.formGroup.get("email")?.value; + document.getElementById(email == null || email === "" ? "email" : "masterPassword").focus(); + } } From 51b76098544a2b07258f71e8175de02a0c097926 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Tue, 9 Aug 2022 14:02:37 -0600 Subject: [PATCH 07/35] added request model --- libs/common/src/enums/authenticationRequestType.ts | 4 ++++ .../common/src/models/request/passwordlessAuthRequest.ts | 9 +++++++++ 2 files changed, 13 insertions(+) create mode 100644 libs/common/src/enums/authenticationRequestType.ts create mode 100644 libs/common/src/models/request/passwordlessAuthRequest.ts diff --git a/libs/common/src/enums/authenticationRequestType.ts b/libs/common/src/enums/authenticationRequestType.ts new file mode 100644 index 000000000000..f78f893c16c7 --- /dev/null +++ b/libs/common/src/enums/authenticationRequestType.ts @@ -0,0 +1,4 @@ +export enum AuthenticationRequestType { + AuthenticateAndUnlock = 0, + Unlock = 1, +} diff --git a/libs/common/src/models/request/passwordlessAuthRequest.ts b/libs/common/src/models/request/passwordlessAuthRequest.ts new file mode 100644 index 000000000000..d9df1c7d890e --- /dev/null +++ b/libs/common/src/models/request/passwordlessAuthRequest.ts @@ -0,0 +1,9 @@ +import { AuthenticationRequestType } from "@bitwarden/common/enums/authenticationRequestType"; + +export class PasswordlessAuthRequest { + email: string; + requestDeviceIdentifier: string; + publicKey: string; + type: AuthenticationRequestType; + accessCode: string; +} From 49f3149c75a7461c82a022a09aeaf99291f83929 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Thu, 11 Aug 2022 01:52:22 -0600 Subject: [PATCH 08/35] made more changes --- .../accounts/login-with-device.component.html | 2 +- .../accounts/login-with-device.component.ts | 31 +++++++++++++------ .../web/src/app/accounts/login.component.html | 14 +++++++-- apps/web/src/app/accounts/login.component.ts | 12 +++++++ apps/web/src/app/oss-routing.module.ts | 1 - 5 files changed, 47 insertions(+), 13 deletions(-) diff --git a/apps/web/src/app/accounts/login-with-device.component.html b/apps/web/src/app/accounts/login-with-device.component.html index 022ace7f06ab..aebe5d339505 100644 --- a/apps/web/src/app/accounts/login-with-device.component.html +++ b/apps/web/src/app/accounts/login-with-device.component.html @@ -34,7 +34,7 @@

{{ "fingerprintPhraseHeader" | i18n }}

{{ "loginWithDevciceEnabledInfo" | i18n }}

- {{ "View all log in options" | i18n }} + {{ "viewAllLoginOptions" | i18n }}
diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index 58bc3cab247c..76f4958aeaee 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -1,7 +1,8 @@ import { Component, OnInit } from "@angular/core"; +import { Router } from "@angular/router"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; -import { StateService } from "@bitwarden/common/abstractions/state.service"; +import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service"; @Component({ selector: "app-login-with-device", @@ -9,16 +10,28 @@ import { StateService } from "@bitwarden/common/abstractions/state.service"; }) export class LoginWithDeviceComponent implements OnInit { fingerPrint: string; + email: string; - constructor(private cryptoService: CryptoService, private stateService: StateService) {} + constructor( + private router: Router, + private cryptoService: CryptoService, + private cryptoFunctionService: CryptoFunctionService + ) { + const navigation = this.router.getCurrentNavigation(); + this.email = navigation.extras?.state?.email; + } async ngOnInit() { - // const fingerPrint = await this.cryptoService.getFingerprint( - // await this.stateService.getUserId() - // ); - // if (fingerPrint) { - // this.fingerPrint = fingerPrint.join("-"); - // } - this.fingerPrint = "longinus-spear-black-moon-scrolls"; + if (!this.email) { + this.router.navigate(["/login"]); + } + + this.startPasswordlessLogin(); + } + + async startPasswordlessLogin() { + const keypair = await this.cryptoFunctionService.rsaGenerateKeyPair(2048); + const fingerprint = await this.cryptoService.getFingerprint(this.email, keypair[0]); + this.fingerPrint = fingerprint.join("-"); } } diff --git a/apps/web/src/app/accounts/login.component.html b/apps/web/src/app/accounts/login.component.html index b6eb76baa7c9..e32fae3912b5 100644 --- a/apps/web/src/app/accounts/login.component.html +++ b/apps/web/src/app/accounts/login.component.html @@ -97,10 +97,20 @@
diff --git a/apps/web/src/app/accounts/login.component.ts b/apps/web/src/app/accounts/login.component.ts index 6a4428ad398c..aeb2f38de9ae 100644 --- a/apps/web/src/app/accounts/login.component.ts +++ b/apps/web/src/app/accounts/login.component.ts @@ -172,6 +172,18 @@ export class LoginComponent extends BaseLoginComponent { await super.submit(false); } + async startPasswordlessLogin() { + this.formGroup.get("masterPassword")?.clearValidators(); + this.formGroup.get("masterPassword")?.updateValueAndValidity(); + + if (!this.formGroup.valid) { + return; + } + + const email = this.formGroup.get("email").value; + this.router.navigate(["/login-with-device"], { state: { email: email } }); + } + private getPasswordStrengthUserInput() { const email = this.formGroup.get("email")?.value; let userInput: string[] = []; diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 6e7078293c3b..c42cb12e6bcd 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -64,7 +64,6 @@ const routes: Routes = [ { path: "login-with-device", component: LoginWithDeviceComponent, - canActivate: [UnauthGuard], data: { titleId: "loginWithDevice" }, }, { path: "2fa", component: TwoFactorComponent, canActivate: [UnauthGuard] }, From ff7125fc695ca7dc9fe52268e6c5952d5fd55702 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Thu, 11 Aug 2022 16:09:46 -0600 Subject: [PATCH 09/35] added implmentation to auth request api --- .../accounts/login-with-device.component.ts | 37 +++++++++++++++++-- libs/common/src/abstractions/api.service.ts | 5 +++ ...ationRequestType.ts => authRequestType.ts} | 2 +- .../models/request/passwordlessAuthRequest.ts | 9 ----- .../request/passwordlessCreateAuthRequest.ts | 12 ++++++ .../models/response/authRequestResponse.ts | 24 ++++++++++++ libs/common/src/services/api.service.ts | 8 +++- 7 files changed, 82 insertions(+), 15 deletions(-) rename libs/common/src/enums/{authenticationRequestType.ts => authRequestType.ts} (52%) delete mode 100644 libs/common/src/models/request/passwordlessAuthRequest.ts create mode 100644 libs/common/src/models/request/passwordlessCreateAuthRequest.ts create mode 100644 libs/common/src/models/response/authRequestResponse.ts diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index 76f4958aeaee..8e253690ae6f 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -1,8 +1,14 @@ import { Component, OnInit } from "@angular/core"; import { Router } from "@angular/router"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service"; +import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; +import { AuthRequestType } from "@bitwarden/common/enums/authRequestType"; +import { Utils } from "@bitwarden/common/misc/utils"; +import { PasswordlessCreateAuthRequest } from "@bitwarden/common/src/models/request/passwordlessCreateAuthRequest"; +import { AppIdService } from "@bitwarden/common/src/services/appId.service"; @Component({ selector: "app-login-with-device", @@ -15,10 +21,16 @@ export class LoginWithDeviceComponent implements OnInit { constructor( private router: Router, private cryptoService: CryptoService, - private cryptoFunctionService: CryptoFunctionService + private cryptoFunctionService: CryptoFunctionService, + private appIdService: AppIdService, + private PasswordGenerationService: PasswordGenerationService, + private apiService: ApiService ) { const navigation = this.router.getCurrentNavigation(); - this.email = navigation.extras?.state?.email; + + if (navigation) { + this.email = navigation.extras?.state?.email; + } } async ngOnInit() { @@ -31,7 +43,24 @@ export class LoginWithDeviceComponent implements OnInit { async startPasswordlessLogin() { const keypair = await this.cryptoFunctionService.rsaGenerateKeyPair(2048); - const fingerprint = await this.cryptoService.getFingerprint(this.email, keypair[0]); - this.fingerPrint = fingerprint.join("-"); + const fingerprint = await ( + await this.cryptoService.getFingerprint(this.email, keypair[0]) + ).join("-"); + const deviceIdentifier = await this.appIdService.getAppId(); + const publicKey = Utils.fromBufferToB64(keypair[0]); + const accessCode = await this.PasswordGenerationService.generatePassword({ length: 25 }); + + const request = new PasswordlessCreateAuthRequest( + this.email, + deviceIdentifier, + publicKey, + AuthRequestType.AuthenticateAndUnlock, + accessCode, + fingerprint + ); + + this.fingerPrint = fingerprint; + + await this.apiService.postAuthRequest(request); } } diff --git a/libs/common/src/abstractions/api.service.ts b/libs/common/src/abstractions/api.service.ts index d5267fa4259f..27372e7c5925 100644 --- a/libs/common/src/abstractions/api.service.ts +++ b/libs/common/src/abstractions/api.service.ts @@ -96,6 +96,7 @@ import { VerifyEmailRequest } from "../models/request/verifyEmailRequest"; import { ApiKeyResponse } from "../models/response/apiKeyResponse"; import { AttachmentResponse } from "../models/response/attachmentResponse"; import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse"; +import { AuthRequestResponse } from "../models/response/authRequestResponse"; import { BillingHistoryResponse } from "../models/response/billingHistoryResponse"; import { BillingPaymentResponse } from "../models/response/billingPaymentResponse"; import { BillingResponse } from "../models/response/billingResponse"; @@ -178,6 +179,8 @@ import { TwoFactorYubiKeyResponse } from "../models/response/twoFactorYubiKeyRes import { UserKeyResponse } from "../models/response/userKeyResponse"; import { SendAccessView } from "../models/view/sendAccessView"; +import { PasswordlessCreateAuthRequest } from "./../models/request/passwordlessCreateAuthRequest"; + export abstract class ApiService { send: ( method: "GET" | "POST" | "PUT" | "DELETE", @@ -229,6 +232,8 @@ export abstract class ApiService { putUpdateTempPassword: (request: UpdateTempPasswordRequest) => Promise; postConvertToKeyConnector: () => Promise; + postAuthRequest: (request: PasswordlessCreateAuthRequest) => Promise; + getUserBillingHistory: () => Promise; getUserBillingPayment: () => Promise; diff --git a/libs/common/src/enums/authenticationRequestType.ts b/libs/common/src/enums/authRequestType.ts similarity index 52% rename from libs/common/src/enums/authenticationRequestType.ts rename to libs/common/src/enums/authRequestType.ts index f78f893c16c7..4edfa5b8889e 100644 --- a/libs/common/src/enums/authenticationRequestType.ts +++ b/libs/common/src/enums/authRequestType.ts @@ -1,4 +1,4 @@ -export enum AuthenticationRequestType { +export enum AuthRequestType { AuthenticateAndUnlock = 0, Unlock = 1, } diff --git a/libs/common/src/models/request/passwordlessAuthRequest.ts b/libs/common/src/models/request/passwordlessAuthRequest.ts deleted file mode 100644 index d9df1c7d890e..000000000000 --- a/libs/common/src/models/request/passwordlessAuthRequest.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { AuthenticationRequestType } from "@bitwarden/common/enums/authenticationRequestType"; - -export class PasswordlessAuthRequest { - email: string; - requestDeviceIdentifier: string; - publicKey: string; - type: AuthenticationRequestType; - accessCode: string; -} diff --git a/libs/common/src/models/request/passwordlessCreateAuthRequest.ts b/libs/common/src/models/request/passwordlessCreateAuthRequest.ts new file mode 100644 index 000000000000..ef50fdd49cdb --- /dev/null +++ b/libs/common/src/models/request/passwordlessCreateAuthRequest.ts @@ -0,0 +1,12 @@ +import { AuthRequestType } from "@bitwarden/common/enums/authRequestType"; + +export class PasswordlessCreateAuthRequest { + constructor( + readonly email: string, + readonly requestDeviceIdentifier: string, + readonly publicKey: string, + readonly type: AuthRequestType, + readonly accessCode: string, + readonly fingerprintPhrase: string + ) {} +} diff --git a/libs/common/src/models/response/authRequestResponse.ts b/libs/common/src/models/response/authRequestResponse.ts new file mode 100644 index 000000000000..41adc117b98f --- /dev/null +++ b/libs/common/src/models/response/authRequestResponse.ts @@ -0,0 +1,24 @@ +import { DeviceType } from "@bitwarden/common/enums/deviceType"; + +import { BaseResponse } from "./baseResponse"; + +export class AuthRequestResponse extends BaseResponse { + id: string; + publicKey: string; + requestDeviceType: DeviceType; + requestIpAddress: string; + key: string; + masterPasswordHash: string; + creationDate: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.publicKey = this.getResponseProperty("PublicKey"); + this.requestDeviceType = this.getResponseProperty("RequestDeviceType"); + this.requestIpAddress = this.getResponseProperty("RequestIpAddress"); + this.key = this.getResponseProperty("Key"); + this.masterPasswordHash = this.getResponseProperty("MaterPasswordHash"); + this.creationDate = this.getResponseProperty("CreationDate"); + } +} diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index 5573e644c444..172fdea3313a 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -64,6 +64,7 @@ import { OrganizationUserUpdateGroupsRequest } from "../models/request/organizat import { OrganizationUserUpdateRequest } from "../models/request/organizationUserUpdateRequest"; import { PasswordHintRequest } from "../models/request/passwordHintRequest"; import { PasswordRequest } from "../models/request/passwordRequest"; +import { PasswordlessCreateAuthRequest } from "../models/request/passwordlessCreateAuthRequest"; import { PaymentRequest } from "../models/request/paymentRequest"; import { PreloginRequest } from "../models/request/preloginRequest"; import { ProviderAddOrganizationRequest } from "../models/request/provider/providerAddOrganizationRequest"; @@ -104,6 +105,7 @@ import { VerifyEmailRequest } from "../models/request/verifyEmailRequest"; import { ApiKeyResponse } from "../models/response/apiKeyResponse"; import { AttachmentResponse } from "../models/response/attachmentResponse"; import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse"; +import { AuthRequestResponse } from "../models/response/authRequestResponse"; import { BillingHistoryResponse } from "../models/response/billingHistoryResponse"; import { BillingPaymentResponse } from "../models/response/billingPaymentResponse"; import { BillingResponse } from "../models/response/billingResponse"; @@ -217,7 +219,6 @@ export class ApiService implements ApiServiceAbstraction { this.device === DeviceType.MacOsDesktop || this.device === DeviceType.LinuxDesktop; } - // Auth APIs async postIdentityToken( @@ -283,6 +284,11 @@ export class ApiService implements ApiServiceAbstraction { } } + async postAuthRequest(request: PasswordlessCreateAuthRequest): Promise { + const r = await this.send("POST", "/auth-requests/", request, false, true); + return new AuthRequestResponse(r); + } + // Account APIs async getProfile(): Promise { From 5c91147986c9b2818375fdd148a14b9c8c412eb0 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Fri, 12 Aug 2022 01:07:54 -0600 Subject: [PATCH 10/35] fixed refrencing issue --- apps/web/src/app/accounts/login-with-device.component.ts | 4 ++-- libs/common/src/abstractions/api.service.ts | 2 +- .../src/models/request/passwordlessCreateAuthRequest.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index 8e253690ae6f..e243b0f1f6b0 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -2,13 +2,13 @@ import { Component, OnInit } from "@angular/core"; import { Router } from "@angular/router"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AppIdService } from "@bitwarden/common/abstractions/appId.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service"; import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; import { AuthRequestType } from "@bitwarden/common/enums/authRequestType"; import { Utils } from "@bitwarden/common/misc/utils"; -import { PasswordlessCreateAuthRequest } from "@bitwarden/common/src/models/request/passwordlessCreateAuthRequest"; -import { AppIdService } from "@bitwarden/common/src/services/appId.service"; +import { PasswordlessCreateAuthRequest } from "@bitwarden/common/models/request/passwordlessCreateAuthRequest"; @Component({ selector: "app-login-with-device", diff --git a/libs/common/src/abstractions/api.service.ts b/libs/common/src/abstractions/api.service.ts index 27372e7c5925..80e8a55a301b 100644 --- a/libs/common/src/abstractions/api.service.ts +++ b/libs/common/src/abstractions/api.service.ts @@ -56,6 +56,7 @@ import { OrganizationUserUpdateGroupsRequest } from "../models/request/organizat import { OrganizationUserUpdateRequest } from "../models/request/organizationUserUpdateRequest"; import { PasswordHintRequest } from "../models/request/passwordHintRequest"; import { PasswordRequest } from "../models/request/passwordRequest"; +import { PasswordlessCreateAuthRequest } from "../models/request/passwordlessCreateAuthRequest"; import { PaymentRequest } from "../models/request/paymentRequest"; import { PreloginRequest } from "../models/request/preloginRequest"; import { ProviderAddOrganizationRequest } from "../models/request/provider/providerAddOrganizationRequest"; @@ -179,7 +180,6 @@ import { TwoFactorYubiKeyResponse } from "../models/response/twoFactorYubiKeyRes import { UserKeyResponse } from "../models/response/userKeyResponse"; import { SendAccessView } from "../models/view/sendAccessView"; -import { PasswordlessCreateAuthRequest } from "./../models/request/passwordlessCreateAuthRequest"; export abstract class ApiService { send: ( diff --git a/libs/common/src/models/request/passwordlessCreateAuthRequest.ts b/libs/common/src/models/request/passwordlessCreateAuthRequest.ts index ef50fdd49cdb..2b867fdef7e9 100644 --- a/libs/common/src/models/request/passwordlessCreateAuthRequest.ts +++ b/libs/common/src/models/request/passwordlessCreateAuthRequest.ts @@ -1,4 +1,4 @@ -import { AuthRequestType } from "@bitwarden/common/enums/authRequestType"; +import { AuthRequestType } from "../../enums/authRequestType"; export class PasswordlessCreateAuthRequest { constructor( From 4abe802827487a55665c848574c0f8bc4872ab89 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Mon, 15 Aug 2022 10:27:19 -0600 Subject: [PATCH 11/35] renamed model property --- libs/common/src/models/request/passwordlessCreateAuthRequest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/src/models/request/passwordlessCreateAuthRequest.ts b/libs/common/src/models/request/passwordlessCreateAuthRequest.ts index 2b867fdef7e9..df83c547775d 100644 --- a/libs/common/src/models/request/passwordlessCreateAuthRequest.ts +++ b/libs/common/src/models/request/passwordlessCreateAuthRequest.ts @@ -3,7 +3,7 @@ import { AuthRequestType } from "../../enums/authRequestType"; export class PasswordlessCreateAuthRequest { constructor( readonly email: string, - readonly requestDeviceIdentifier: string, + readonly deviceIdentifier: string, readonly publicKey: string, readonly type: AuthRequestType, readonly accessCode: string, From 4c9d83285c7dca3d65f75c07ac22e57d1f185f09 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Tue, 16 Aug 2022 00:28:15 -0600 Subject: [PATCH 12/35] Added resend notification functionality --- .../app/accounts/login-with-device.component.html | 14 ++++++++------ .../app/accounts/login-with-device.component.ts | 2 ++ apps/web/src/app/accounts/login.component.html | 10 +++++----- libs/common/src/abstractions/api.service.ts | 1 - 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/apps/web/src/app/accounts/login-with-device.component.html b/apps/web/src/app/accounts/login-with-device.component.html index aebe5d339505..a194b4318ade 100644 --- a/apps/web/src/app/accounts/login-with-device.component.html +++ b/apps/web/src/app/accounts/login-with-device.component.html @@ -1,16 +1,16 @@
-

+

{{ "loginOrCreateNewAccount" | i18n }}

-

{{ "logInInitiated" | i18n }}

+

{{ "logInInitiated" | i18n }}

{{ "notificationSentDevice" | i18n }}

@@ -27,12 +27,14 @@

{{ "fingerprintPhraseHeader" | i18n }}


-
+

{{ "loginWithDevciceEnabledInfo" | i18n }}

{{ "viewAllLoginOptions" | i18n }}
diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index e243b0f1f6b0..c92fa15f847e 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -62,5 +62,7 @@ export class LoginWithDeviceComponent implements OnInit { this.fingerPrint = fingerprint; await this.apiService.postAuthRequest(request); + + //delay and get response } } diff --git a/apps/web/src/app/accounts/login.component.html b/apps/web/src/app/accounts/login.component.html index e32fae3912b5..31ddd8d06774 100644 --- a/apps/web/src/app/accounts/login.component.html +++ b/apps/web/src/app/accounts/login.component.html @@ -6,15 +6,15 @@ [formGroup]="formGroup" >
-

+

{{ "loginOrCreateNewAccount" | i18n }}

{{ "logIn" | i18n }} @@ -89,7 +89,7 @@ bitButton buttonType="secondary" routerLink="/register" - class="tw-w-1/2 tw-inline-block" + class="tw-inline-block tw-w-1/2" > {{ "createAccount" | i18n }} diff --git a/libs/common/src/abstractions/api.service.ts b/libs/common/src/abstractions/api.service.ts index 80e8a55a301b..10af22ce076a 100644 --- a/libs/common/src/abstractions/api.service.ts +++ b/libs/common/src/abstractions/api.service.ts @@ -180,7 +180,6 @@ import { TwoFactorYubiKeyResponse } from "../models/response/twoFactorYubiKeyRes import { UserKeyResponse } from "../models/response/userKeyResponse"; import { SendAccessView } from "../models/view/sendAccessView"; - export abstract class ApiService { send: ( method: "GET" | "POST" | "PUT" | "DELETE", From 2779f70e1acce862d168ae2d44570f1d904d6065 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Fri, 19 Aug 2022 01:51:54 -0600 Subject: [PATCH 13/35] Added new file --- .../request/identityToken/tokenRequestPasswordless.ts | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 libs/common/src/models/request/identityToken/tokenRequestPasswordless.ts diff --git a/libs/common/src/models/request/identityToken/tokenRequestPasswordless.ts b/libs/common/src/models/request/identityToken/tokenRequestPasswordless.ts new file mode 100644 index 000000000000..23874cad663c --- /dev/null +++ b/libs/common/src/models/request/identityToken/tokenRequestPasswordless.ts @@ -0,0 +1,6 @@ +export class TokenRequestPasswordless { + authRequest: string; + constructor(authRequest: string) { + this.authRequest = authRequest; + } +} From a8bb6fba03c98fc4dbb24e93dd06f6e1be0a10e3 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Fri, 19 Aug 2022 16:51:39 -0600 Subject: [PATCH 14/35] login with device first draft --- .../accounts/login-with-device.component.ts | 88 ++++++++++++++++++- .../web/src/app/accounts/login.component.html | 4 - libs/common/src/abstractions/api.service.ts | 3 +- libs/common/src/models/data/loginAuthData.ts | 11 +++ .../src/models/data/loginCompleteAuthData.ts | 17 ++++ .../src/models/domain/logInCredentials.ts | 5 +- .../identityToken/passwordTokenRequest.ts | 6 +- .../request/identityToken/tokenRequest.ts | 16 +++- libs/common/src/services/api.service.ts | 6 ++ 9 files changed, 143 insertions(+), 13 deletions(-) create mode 100644 libs/common/src/models/data/loginAuthData.ts create mode 100644 libs/common/src/models/data/loginCompleteAuthData.ts diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index c92fa15f847e..2a3405562480 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -1,22 +1,39 @@ import { Component, OnInit } from "@angular/core"; import { Router } from "@angular/router"; +import { defer, delay, from } from "rxjs"; +import { CaptchaProtectedComponent } from "@bitwarden/angular/components/captchaProtected.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AppIdService } from "@bitwarden/common/abstractions/appId.service"; +import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service"; +import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/abstractions/log.service"; import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; +import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; +import { StateService } from "@bitwarden/common/abstractions/state.service"; import { AuthRequestType } from "@bitwarden/common/enums/authRequestType"; import { Utils } from "@bitwarden/common/misc/utils"; +import { PasswordLogInCredentials } from "@bitwarden/common/models/domain/logInCredentials"; +import { TokenRequestPasswordless } from "@bitwarden/common/models/request/identityToken/tokenRequestPasswordless"; import { PasswordlessCreateAuthRequest } from "@bitwarden/common/models/request/passwordlessCreateAuthRequest"; +import { AuthRequestResponse } from "@bitwarden/common/models/response/authRequestResponse"; @Component({ selector: "app-login-with-device", templateUrl: "login-with-device.component.html", }) -export class LoginWithDeviceComponent implements OnInit { +export class LoginWithDeviceComponent extends CaptchaProtectedComponent implements OnInit { fingerPrint: string; email: string; + onSuccessfulLoginTwoFactorNavigate: () => Promise; + onSuccessfulLogin: () => Promise; + onSuccessfulLoginNavigate: () => Promise; + + protected twoFactorRoute = "2fa"; + protected successRoute = "vault"; constructor( private router: Router, @@ -24,10 +41,17 @@ export class LoginWithDeviceComponent implements OnInit { private cryptoFunctionService: CryptoFunctionService, private appIdService: AppIdService, private PasswordGenerationService: PasswordGenerationService, - private apiService: ApiService + private apiService: ApiService, + private authService: AuthService, + private logService: LogService, + private stateService: StateService, + environmentService: EnvironmentService, + i18nService: I18nService, + platformUtilsService: PlatformUtilsService ) { - const navigation = this.router.getCurrentNavigation(); + super(environmentService, i18nService, platformUtilsService); + const navigation = this.router.getCurrentNavigation(); if (navigation) { this.email = navigation.extras?.state?.email; } @@ -49,6 +73,7 @@ export class LoginWithDeviceComponent implements OnInit { const deviceIdentifier = await this.appIdService.getAppId(); const publicKey = Utils.fromBufferToB64(keypair[0]); const accessCode = await this.PasswordGenerationService.generatePassword({ length: 25 }); + let response: AuthRequestResponse = null; const request = new PasswordlessCreateAuthRequest( this.email, @@ -61,8 +86,63 @@ export class LoginWithDeviceComponent implements OnInit { this.fingerPrint = fingerprint; - await this.apiService.postAuthRequest(request); + const reqResponse = await this.apiService.postAuthRequest(request); + //replace with signalR //delay and get response + defer(() => + from(this.apiService.getAuthResponse(reqResponse.id, accessCode)).pipe(delay(2000)) + ).subscribe({ + next: (res: AuthRequestResponse) => { + response = res; + }, + error: (error) => this.logService.error(error), + }); + + if (response) { + await this.setupCaptcha(); + + // const decKey = await this.cryptoService.rsaDecrypt(response.key, keypair[1]); + const decMasterPasswordHash = await this.cryptoService.rsaDecrypt( + response.masterPasswordHash, + keypair[1] + ); + + try { + const masterPasswordHash = Utils.fromBufferToB64(decMasterPasswordHash); + + const credentials = new PasswordLogInCredentials( + this.email, + masterPasswordHash, + this.captchaToken, + null, + new TokenRequestPasswordless(accessCode) + ); + const loginResponse = await this.authService.logIn(credentials); + + if (this.handleCaptchaRequired(loginResponse)) { + return; + } else if (loginResponse.requiresTwoFactor) { + if (this.onSuccessfulLoginTwoFactorNavigate != null) { + this.onSuccessfulLoginTwoFactorNavigate(); + } else { + this.router.navigate([this.twoFactorRoute]); + } + } else { + const disableFavicon = await this.stateService.getDisableFavicon(); + await this.stateService.setDisableFavicon(!!disableFavicon); + if (this.onSuccessfulLogin != null) { + this.onSuccessfulLogin(); + } + if (this.onSuccessfulLoginNavigate != null) { + this.onSuccessfulLoginNavigate(); + } else { + this.router.navigate([this.successRoute]); + } + } + } catch (error) { + this.logService.error(error); + } + } } } diff --git a/apps/web/src/app/accounts/login.component.html b/apps/web/src/app/accounts/login.component.html index 31ddd8d06774..41b3f9c2d38a 100644 --- a/apps/web/src/app/accounts/login.component.html +++ b/apps/web/src/app/accounts/login.component.html @@ -97,10 +97,6 @@
-
-
- {{ + diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index 2a3405562480..1fcf162811fc 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -28,6 +28,7 @@ import { AuthRequestResponse } from "@bitwarden/common/models/response/authReque export class LoginWithDeviceComponent extends CaptchaProtectedComponent implements OnInit { fingerPrint: string; email: string; + resendNotification = false; onSuccessfulLoginTwoFactorNavigate: () => Promise; onSuccessfulLogin: () => Promise; onSuccessfulLoginNavigate: () => Promise; @@ -60,6 +61,7 @@ export class LoginWithDeviceComponent extends CaptchaProtectedComponent implemen async ngOnInit() { if (!this.email) { this.router.navigate(["/login"]); + return; } this.startPasswordlessLogin(); @@ -90,22 +92,37 @@ export class LoginWithDeviceComponent extends CaptchaProtectedComponent implemen //replace with signalR //delay and get response + //after request has been posted the show loader and hide resend notification defer(() => from(this.apiService.getAuthResponse(reqResponse.id, accessCode)).pipe(delay(2000)) ).subscribe({ next: (res: AuthRequestResponse) => { response = res; + if (!response.requestApproved) { + this.resendNotification = true; + } else { + this.confirmResponse(response, accessCode, keypair[1]); + } + }, + error: (error) => { + this.resendNotification = true; + this.logService.error(error); }, - error: (error) => this.logService.error(error), }); + } + private async confirmResponse( + response: AuthRequestResponse, + accessCode: string, + privateKeyVal: ArrayBuffer + ) { if (response) { await this.setupCaptcha(); // const decKey = await this.cryptoService.rsaDecrypt(response.key, keypair[1]); const decMasterPasswordHash = await this.cryptoService.rsaDecrypt( response.masterPasswordHash, - keypair[1] + privateKeyVal ); try { diff --git a/libs/common/src/models/response/authRequestResponse.ts b/libs/common/src/models/response/authRequestResponse.ts index 41adc117b98f..4770efb6cbb0 100644 --- a/libs/common/src/models/response/authRequestResponse.ts +++ b/libs/common/src/models/response/authRequestResponse.ts @@ -10,6 +10,7 @@ export class AuthRequestResponse extends BaseResponse { key: string; masterPasswordHash: string; creationDate: string; + requestApproved: boolean; constructor(response: any) { super(response); @@ -20,5 +21,6 @@ export class AuthRequestResponse extends BaseResponse { this.key = this.getResponseProperty("Key"); this.masterPasswordHash = this.getResponseProperty("MaterPasswordHash"); this.creationDate = this.getResponseProperty("CreationDate"); + this.requestApproved = this.getResponseProperty("requestApproved"); } } From d693a077aa56c6d0f268ae4f52321edd3d645dda Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Sun, 21 Aug 2022 16:43:58 -0600 Subject: [PATCH 16/35] login with device first draft --- apps/web/src/app/accounts/login-with-device.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index 1fcf162811fc..deef52cc8b9e 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -92,7 +92,7 @@ export class LoginWithDeviceComponent extends CaptchaProtectedComponent implemen //replace with signalR //delay and get response - //after request has been posted the show loader and hide resend notification + //after request has been posted resend notification defer(() => from(this.apiService.getAuthResponse(reqResponse.id, accessCode)).pipe(delay(2000)) ).subscribe({ From 25304b4ef29bc767b4980ae7963c466591c0693d Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Tue, 23 Aug 2022 13:27:45 -0600 Subject: [PATCH 17/35] login with device first draft --- .../accounts/login-with-device.component.ts | 34 ++++++------------- libs/common/src/abstractions/auth.service.ts | 6 ++++ libs/common/src/enums/notificationType.ts | 3 ++ .../models/response/notificationResponse.ts | 11 ++++++ libs/common/src/services/auth.service.ts | 14 ++++++++ .../src/services/notifications.service.ts | 7 +++- 6 files changed, 51 insertions(+), 24 deletions(-) diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index deef52cc8b9e..f82446be0c5b 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit } from "@angular/core"; import { Router } from "@angular/router"; -import { defer, delay, from } from "rxjs"; import { CaptchaProtectedComponent } from "@bitwarden/angular/components/captchaProtected.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -56,6 +55,11 @@ export class LoginWithDeviceComponent extends CaptchaProtectedComponent implemen if (navigation) { this.email = navigation.extras?.state?.email; } + + //gets signalR push notification + this.authService.getPushNotifcationObs().subscribe(() => { + this.confirmResponse(null, null, null); + }); } async ngOnInit() { @@ -75,7 +79,7 @@ export class LoginWithDeviceComponent extends CaptchaProtectedComponent implemen const deviceIdentifier = await this.appIdService.getAppId(); const publicKey = Utils.fromBufferToB64(keypair[0]); const accessCode = await this.PasswordGenerationService.generatePassword({ length: 25 }); - let response: AuthRequestResponse = null; + // let response: AuthRequestResponse = null; const request = new PasswordlessCreateAuthRequest( this.email, @@ -88,27 +92,11 @@ export class LoginWithDeviceComponent extends CaptchaProtectedComponent implemen this.fingerPrint = fingerprint; - const reqResponse = await this.apiService.postAuthRequest(request); - - //replace with signalR - //delay and get response - //after request has been posted resend notification - defer(() => - from(this.apiService.getAuthResponse(reqResponse.id, accessCode)).pipe(delay(2000)) - ).subscribe({ - next: (res: AuthRequestResponse) => { - response = res; - if (!response.requestApproved) { - this.resendNotification = true; - } else { - this.confirmResponse(response, accessCode, keypair[1]); - } - }, - error: (error) => { - this.resendNotification = true; - this.logService.error(error); - }, - }); + await this.apiService.postAuthRequest(request); + + setTimeout(() => { + this.resendNotification = true; + }, 2000); } private async confirmResponse( diff --git a/libs/common/src/abstractions/auth.service.ts b/libs/common/src/abstractions/auth.service.ts index 4947f21708bc..e1100c0950a2 100644 --- a/libs/common/src/abstractions/auth.service.ts +++ b/libs/common/src/abstractions/auth.service.ts @@ -1,3 +1,5 @@ +import { Observable } from "rxjs"; + import { AuthenticationStatus } from "../enums/authenticationStatus"; import { AuthResult } from "../models/domain/authResult"; import { @@ -7,6 +9,7 @@ import { } from "../models/domain/logInCredentials"; import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; import { TokenRequestTwoFactor } from "../models/request/identityToken/tokenRequestTwoFactor"; +import { AuthRequestPushNotification } from "../models/response/notificationResponse"; export abstract class AuthService { masterPasswordHash: string; @@ -24,4 +27,7 @@ export abstract class AuthService { authingWithSso: () => boolean; authingWithPassword: () => boolean; getAuthStatus: (userId?: string) => Promise; + authResponsePushNotifiction: (notification: AuthRequestPushNotification) => Promise; + + getPushNotifcationObs: () => Observable; } diff --git a/libs/common/src/enums/notificationType.ts b/libs/common/src/enums/notificationType.ts index 77ebde01fc57..d694053e3ea2 100644 --- a/libs/common/src/enums/notificationType.ts +++ b/libs/common/src/enums/notificationType.ts @@ -17,4 +17,7 @@ export enum NotificationType { SyncSendCreate = 12, SyncSendUpdate = 13, SyncSendDelete = 14, + + AuthRequest, + AuthRequestResponse, } diff --git a/libs/common/src/models/response/notificationResponse.ts b/libs/common/src/models/response/notificationResponse.ts index f23de8fe8be1..d8f295a920dc 100644 --- a/libs/common/src/models/response/notificationResponse.ts +++ b/libs/common/src/models/response/notificationResponse.ts @@ -96,3 +96,14 @@ export class SyncSendNotification extends BaseResponse { this.revisionDate = new Date(this.getResponseProperty("RevisionDate")); } } + +export class AuthRequestPushNotification extends BaseResponse { + id: string; + userId: string; + + constructor(response: any) { + super(response); + this.id = this.getResponseProperty("Id"); + this.userId = this.getResponseProperty("UserId"); + } +} diff --git a/libs/common/src/services/auth.service.ts b/libs/common/src/services/auth.service.ts index 6f77bca20b34..9c4fe7ed01cd 100644 --- a/libs/common/src/services/auth.service.ts +++ b/libs/common/src/services/auth.service.ts @@ -1,3 +1,5 @@ +import { Observable, Subject } from "rxjs"; + import { ApiService } from "../abstractions/api.service"; import { AppIdService } from "../abstractions/appId.service"; import { AuthService as AuthServiceAbstraction } from "../abstractions/auth.service"; @@ -28,6 +30,7 @@ import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; import { TokenRequestTwoFactor } from "../models/request/identityToken/tokenRequestTwoFactor"; import { PreloginRequest } from "../models/request/preloginRequest"; import { ErrorResponse } from "../models/response/errorResponse"; +import { AuthRequestPushNotification } from "../models/response/notificationResponse"; const sessionTimeoutLength = 2 * 60 * 1000; // 2 minutes @@ -45,6 +48,8 @@ export class AuthService implements AuthServiceAbstraction { private logInStrategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy; private sessionTimeout: any; + private pushNotificationSubject = new Subject(); + constructor( protected cryptoService: CryptoService, protected apiService: ApiService, @@ -202,6 +207,15 @@ export class AuthService implements AuthServiceAbstraction { return this.cryptoService.makeKey(masterPassword, email, kdf, kdfIterations); } + async authResponsePushNotifiction(notification: AuthRequestPushNotification): Promise { + //pass id as subject + this.pushNotificationSubject.next(notification.id); + } + + getPushNotifcationObs(): Observable { + return this.pushNotificationSubject.asObservable(); + } + private saveState(strategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy) { this.logInStrategy = strategy; this.startSessionTimeout(); diff --git a/libs/common/src/services/notifications.service.ts b/libs/common/src/services/notifications.service.ts index 39e2df541fe1..6371015873b1 100644 --- a/libs/common/src/services/notifications.service.ts +++ b/libs/common/src/services/notifications.service.ts @@ -16,7 +16,7 @@ import { SyncCipherNotification, SyncFolderNotification, SyncSendNotification, -} from "../models/response/notificationResponse"; + AuthRequestPushNotification } from "../models/response/notificationResponse"; export class NotificationsService implements NotificationsServiceAbstraction { private signalrConnection: signalR.HubConnection; @@ -183,6 +183,11 @@ export class NotificationsService implements NotificationsServiceAbstraction { case NotificationType.SyncSendDelete: await this.syncService.syncDeleteSend(notification.payload as SyncSendNotification); break; + case NotificationType.AuthRequestResponse: + await this.authService.authResponsePushNotifiction( + notification.payload as AuthRequestPushNotification + ); + break; default: break; } From b694d00411decf616f5dc59bf4e6a5bf3c6e378f Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Thu, 25 Aug 2022 14:50:23 -0600 Subject: [PATCH 18/35] connection to anonymous hub --- .../accounts/login-with-device.component.ts | 23 +++++-- .../src/services/jslib-services.module.ts | 7 +++ .../src/abstractions/anonymousHub.service.ts | 4 ++ .../models/response/notificationResponse.ts | 4 ++ .../src/services/anonymousHub.service.ts | 60 +++++++++++++++++++ 5 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 libs/common/src/abstractions/anonymousHub.service.ts create mode 100644 libs/common/src/services/anonymousHub.service.ts diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index f82446be0c5b..c07ddf9ba955 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -1,7 +1,8 @@ -import { Component, OnInit } from "@angular/core"; +import { Component, OnDestroy, OnInit } from "@angular/core"; import { Router } from "@angular/router"; import { CaptchaProtectedComponent } from "@bitwarden/angular/components/captchaProtected.component"; +import { AnonymousHubService } from "@bitwarden/common/abstractions/anonymousHub.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AppIdService } from "@bitwarden/common/abstractions/appId.service"; import { AuthService } from "@bitwarden/common/abstractions/auth.service"; @@ -24,7 +25,10 @@ import { AuthRequestResponse } from "@bitwarden/common/models/response/authReque selector: "app-login-with-device", templateUrl: "login-with-device.component.html", }) -export class LoginWithDeviceComponent extends CaptchaProtectedComponent implements OnInit { +export class LoginWithDeviceComponent + extends CaptchaProtectedComponent + implements OnInit, OnDestroy +{ fingerPrint: string; email: string; resendNotification = false; @@ -47,7 +51,8 @@ export class LoginWithDeviceComponent extends CaptchaProtectedComponent implemen private stateService: StateService, environmentService: EnvironmentService, i18nService: I18nService, - platformUtilsService: PlatformUtilsService + platformUtilsService: PlatformUtilsService, + private anonymousHubService: AnonymousHubService ) { super(environmentService, i18nService, platformUtilsService); @@ -57,8 +62,8 @@ export class LoginWithDeviceComponent extends CaptchaProtectedComponent implemen } //gets signalR push notification - this.authService.getPushNotifcationObs().subscribe(() => { - this.confirmResponse(null, null, null); + this.authService.getPushNotifcationObs().subscribe((id) => { + id; }); } @@ -92,13 +97,19 @@ export class LoginWithDeviceComponent extends CaptchaProtectedComponent implemen this.fingerPrint = fingerprint; - await this.apiService.postAuthRequest(request); + const reqResponse = await this.apiService.postAuthRequest(request); + + this.anonymousHubService.createHubConnection(reqResponse.id); setTimeout(() => { this.resendNotification = true; }, 2000); } + ngOnDestroy(): void { + this.anonymousHubService.stopHubConnection(); + } + private async confirmResponse( response: AuthRequestResponse, accessCode: string, diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 729f876d3d6f..145099688591 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -5,6 +5,7 @@ import { AbstractThemingService } from "@bitwarden/angular/services/theming/them import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service"; import { AccountApiService as AccountApiServiceAbstraction } from "@bitwarden/common/abstractions/account/account-api.service.abstraction"; import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/abstractions/account/account.service.abstraction"; +import { AnonymousHubService as AnonymousHubServiceAbstraction } from "@bitwarden/common/abstractions/anonymousHub.service"; import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service"; import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/abstractions/appId.service"; import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service"; @@ -58,6 +59,7 @@ import { Account } from "@bitwarden/common/models/domain/account"; import { GlobalState } from "@bitwarden/common/models/domain/globalState"; import { AccountApiService } from "@bitwarden/common/services/account/account-api.service"; import { AccountService } from "@bitwarden/common/services/account/account.service"; +import { AnonymousHubService } from "@bitwarden/common/services/anonymousHub.service"; import { ApiService } from "@bitwarden/common/services/api.service"; import { AppIdService } from "@bitwarden/common/services/appId.service"; import { AuditService } from "@bitwarden/common/services/audit.service"; @@ -513,6 +515,11 @@ export const LOG_MAC_FAILURES = new InjectionToken("LOG_MAC_FAILURES"); useClass: UserVerificationApiService, deps: [ApiServiceAbstraction], }, + { + provide: AnonymousHubServiceAbstraction, + useClass: AnonymousHubService, + deps: [EnvironmentServiceAbstraction, AuthServiceAbstraction, LogService], + }, ], }) export class JslibServicesModule {} diff --git a/libs/common/src/abstractions/anonymousHub.service.ts b/libs/common/src/abstractions/anonymousHub.service.ts new file mode 100644 index 000000000000..43bdabd512c9 --- /dev/null +++ b/libs/common/src/abstractions/anonymousHub.service.ts @@ -0,0 +1,4 @@ +export abstract class AnonymousHubService { + createHubConnection: (token: string) => void; + stopHubConnection: () => void; +} diff --git a/libs/common/src/models/response/notificationResponse.ts b/libs/common/src/models/response/notificationResponse.ts index d8f295a920dc..1e2a50450635 100644 --- a/libs/common/src/models/response/notificationResponse.ts +++ b/libs/common/src/models/response/notificationResponse.ts @@ -37,6 +37,10 @@ export class NotificationResponse extends BaseResponse { case NotificationType.SyncSendDelete: this.payload = new SyncSendNotification(payload); break; + case NotificationType.AuthRequest: + case NotificationType.AuthRequestResponse: + this.payload = new AuthRequestPushNotification(payload); + break; default: break; } diff --git a/libs/common/src/services/anonymousHub.service.ts b/libs/common/src/services/anonymousHub.service.ts new file mode 100644 index 000000000000..2b655acbdcf9 --- /dev/null +++ b/libs/common/src/services/anonymousHub.service.ts @@ -0,0 +1,60 @@ +import { Injectable } from "@angular/core"; +import { + HttpTransportType, + HubConnection, + HubConnectionBuilder, + IHubProtocol, +} from "@microsoft/signalr"; +import { MessagePackHubProtocol } from "@microsoft/signalr-protocol-msgpack"; + +import { AnonymousHubService as AnonymousHubServiceAbstraction } from "../abstractions/anonymousHub.service"; +import { AuthService } from "../abstractions/auth.service"; +import { EnvironmentService } from "../abstractions/environment.service"; +import { LogService } from "../abstractions/log.service"; + +import { + AuthRequestPushNotification, + NotificationResponse, +} from "./../models/response/notificationResponse"; + +@Injectable() +export class AnonymousHubService implements AnonymousHubServiceAbstraction { + private hubConnection: HubConnection; + private url: string; + + constructor( + private environmentService: EnvironmentService, + private authService: AuthService, + private logService: LogService + ) {} + + async createHubConnection(token: string) { + this.url = this.environmentService.getNotificationsUrl(); + + this.hubConnection = new HubConnectionBuilder() + .withUrl(this.url + "/anonymousHub?Token=" + token, { + skipNegotiation: true, + transport: HttpTransportType.WebSockets, + }) + .withHubProtocol(new MessagePackHubProtocol() as IHubProtocol) + .build(); + + this.hubConnection.start().catch((error) => this.logService.error(error)); + + this.hubConnection.on("AuthRequestResponseRecieved", (data: any) => { + this.ProcessNotification(new NotificationResponse(data)); + }); + } + + stopHubConnection() { + if (this.hubConnection) { + this.hubConnection.stop(); + } + } + + private async ProcessNotification(notification: NotificationResponse) { + await this.authService.authResponsePushNotifiction( + notification.payload as AuthRequestPushNotification + ); + } +} From f1bbffaa8e58a976e0052bd9ac1556c3ede5e09b Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Thu, 25 Aug 2022 23:11:32 -0600 Subject: [PATCH 19/35] connection to anonymous hub --- .../accounts/login-with-device.component.ts | 97 ++++++++++--------- .../src/services/anonymousHub.service.ts | 12 +-- .../src/services/notifications.service.ts | 7 +- 3 files changed, 57 insertions(+), 59 deletions(-) diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index c07ddf9ba955..404a65fbfc4e 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -19,7 +19,6 @@ import { Utils } from "@bitwarden/common/misc/utils"; import { PasswordLogInCredentials } from "@bitwarden/common/models/domain/logInCredentials"; import { TokenRequestPasswordless } from "@bitwarden/common/models/request/identityToken/tokenRequestPasswordless"; import { PasswordlessCreateAuthRequest } from "@bitwarden/common/models/request/passwordlessCreateAuthRequest"; -import { AuthRequestResponse } from "@bitwarden/common/models/response/authRequestResponse"; @Component({ selector: "app-login-with-device", @@ -29,6 +28,8 @@ export class LoginWithDeviceComponent extends CaptchaProtectedComponent implements OnInit, OnDestroy { + private accessCode: string; + private privateKeyValue: ArrayBuffer; fingerPrint: string; email: string; resendNotification = false; @@ -63,7 +64,7 @@ export class LoginWithDeviceComponent //gets signalR push notification this.authService.getPushNotifcationObs().subscribe((id) => { - id; + this.confirmResponse(id, this.accessCode, this.privateKeyValue); }); } @@ -77,6 +78,7 @@ export class LoginWithDeviceComponent } async startPasswordlessLogin() { + this.resendNotification = false; const keypair = await this.cryptoFunctionService.rsaGenerateKeyPair(2048); const fingerprint = await ( await this.cryptoService.getFingerprint(this.email, keypair[0]) @@ -84,7 +86,6 @@ export class LoginWithDeviceComponent const deviceIdentifier = await this.appIdService.getAppId(); const publicKey = Utils.fromBufferToB64(keypair[0]); const accessCode = await this.PasswordGenerationService.generatePassword({ length: 25 }); - // let response: AuthRequestResponse = null; const request = new PasswordlessCreateAuthRequest( this.email, @@ -99,7 +100,12 @@ export class LoginWithDeviceComponent const reqResponse = await this.apiService.postAuthRequest(request); - this.anonymousHubService.createHubConnection(reqResponse.id); + if (reqResponse.id) { + this.anonymousHubService.createHubConnection(reqResponse.id); + } + + this.accessCode = accessCode; + this.privateKeyValue = keypair[1]; setTimeout(() => { this.resendNotification = true; @@ -110,55 +116,52 @@ export class LoginWithDeviceComponent this.anonymousHubService.stopHubConnection(); } - private async confirmResponse( - response: AuthRequestResponse, - accessCode: string, - privateKeyVal: ArrayBuffer - ) { - if (response) { - await this.setupCaptcha(); + private async confirmResponse(requestId: string, accessCode: string, privateKeyVal: ArrayBuffer) { + const response = await this.apiService.getAuthResponse(requestId, accessCode); + // if (response.requestApproved) { + await this.setupCaptcha(); + + // const decKey = await this.cryptoService.rsaDecrypt(response.key, keypair[1]); + const decMasterPasswordHash = await this.cryptoService.rsaDecrypt( + response.masterPasswordHash, + privateKeyVal + ); - // const decKey = await this.cryptoService.rsaDecrypt(response.key, keypair[1]); - const decMasterPasswordHash = await this.cryptoService.rsaDecrypt( - response.masterPasswordHash, - privateKeyVal + try { + const masterPassword = Utils.fromBufferToB64(decMasterPasswordHash); + + const credentials = new PasswordLogInCredentials( + this.email, + masterPassword, + this.captchaToken, + null, + new TokenRequestPasswordless(accessCode) ); + const loginResponse = await this.authService.logIn(credentials); - try { - const masterPasswordHash = Utils.fromBufferToB64(decMasterPasswordHash); - - const credentials = new PasswordLogInCredentials( - this.email, - masterPasswordHash, - this.captchaToken, - null, - new TokenRequestPasswordless(accessCode) - ); - const loginResponse = await this.authService.logIn(credentials); - - if (this.handleCaptchaRequired(loginResponse)) { - return; - } else if (loginResponse.requiresTwoFactor) { - if (this.onSuccessfulLoginTwoFactorNavigate != null) { - this.onSuccessfulLoginTwoFactorNavigate(); - } else { - this.router.navigate([this.twoFactorRoute]); - } + if (this.handleCaptchaRequired(loginResponse)) { + return; + } else if (loginResponse.requiresTwoFactor) { + if (this.onSuccessfulLoginTwoFactorNavigate != null) { + this.onSuccessfulLoginTwoFactorNavigate(); + } else { + this.router.navigate([this.twoFactorRoute]); + } + } else { + const disableFavicon = await this.stateService.getDisableFavicon(); + await this.stateService.setDisableFavicon(!!disableFavicon); + if (this.onSuccessfulLogin != null) { + this.onSuccessfulLogin(); + } + if (this.onSuccessfulLoginNavigate != null) { + this.onSuccessfulLoginNavigate(); } else { - const disableFavicon = await this.stateService.getDisableFavicon(); - await this.stateService.setDisableFavicon(!!disableFavicon); - if (this.onSuccessfulLogin != null) { - this.onSuccessfulLogin(); - } - if (this.onSuccessfulLoginNavigate != null) { - this.onSuccessfulLoginNavigate(); - } else { - this.router.navigate([this.successRoute]); - } + this.router.navigate([this.successRoute]); } - } catch (error) { - this.logService.error(error); } + } catch (error) { + this.logService.error(error); } } + // } } diff --git a/libs/common/src/services/anonymousHub.service.ts b/libs/common/src/services/anonymousHub.service.ts index 2b655acbdcf9..13b5898b18b2 100644 --- a/libs/common/src/services/anonymousHub.service.ts +++ b/libs/common/src/services/anonymousHub.service.ts @@ -19,7 +19,7 @@ import { @Injectable() export class AnonymousHubService implements AnonymousHubServiceAbstraction { - private hubConnection: HubConnection; + private anonHubConnection: HubConnection; private url: string; constructor( @@ -31,7 +31,7 @@ export class AnonymousHubService implements AnonymousHubServiceAbstraction { async createHubConnection(token: string) { this.url = this.environmentService.getNotificationsUrl(); - this.hubConnection = new HubConnectionBuilder() + this.anonHubConnection = new HubConnectionBuilder() .withUrl(this.url + "/anonymousHub?Token=" + token, { skipNegotiation: true, transport: HttpTransportType.WebSockets, @@ -39,16 +39,16 @@ export class AnonymousHubService implements AnonymousHubServiceAbstraction { .withHubProtocol(new MessagePackHubProtocol() as IHubProtocol) .build(); - this.hubConnection.start().catch((error) => this.logService.error(error)); + this.anonHubConnection.start().catch((error) => this.logService.error(error)); - this.hubConnection.on("AuthRequestResponseRecieved", (data: any) => { + this.anonHubConnection.on("AuthRequestResponseRecieved", (data: any) => { this.ProcessNotification(new NotificationResponse(data)); }); } stopHubConnection() { - if (this.hubConnection) { - this.hubConnection.stop(); + if (this.anonHubConnection) { + this.anonHubConnection.stop(); } } diff --git a/libs/common/src/services/notifications.service.ts b/libs/common/src/services/notifications.service.ts index 6371015873b1..39e2df541fe1 100644 --- a/libs/common/src/services/notifications.service.ts +++ b/libs/common/src/services/notifications.service.ts @@ -16,7 +16,7 @@ import { SyncCipherNotification, SyncFolderNotification, SyncSendNotification, - AuthRequestPushNotification } from "../models/response/notificationResponse"; +} from "../models/response/notificationResponse"; export class NotificationsService implements NotificationsServiceAbstraction { private signalrConnection: signalR.HubConnection; @@ -183,11 +183,6 @@ export class NotificationsService implements NotificationsServiceAbstraction { case NotificationType.SyncSendDelete: await this.syncService.syncDeleteSend(notification.payload as SyncSendNotification); break; - case NotificationType.AuthRequestResponse: - await this.authService.authResponsePushNotifiction( - notification.payload as AuthRequestPushNotification - ); - break; default: break; } From f3a79517aedde9e1f5b8cef89ac2b4b4bdbea6f0 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Fri, 26 Aug 2022 13:07:19 -0600 Subject: [PATCH 20/35] refactored confirm login response --- .../accounts/login-with-device.component.ts | 55 +++------ libs/common/src/abstractions/auth.service.ts | 9 +- libs/common/src/enums/authenticationType.ts | 1 + .../src/models/domain/logInCredentials.ts | 19 ++- .../models/response/authRequestResponse.ts | 4 +- libs/common/src/services/auth.service.ts | 108 +++++++++++------- 6 files changed, 111 insertions(+), 85 deletions(-) diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index 404a65fbfc4e..74d682a72f62 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -17,7 +17,6 @@ import { StateService } from "@bitwarden/common/abstractions/state.service"; import { AuthRequestType } from "@bitwarden/common/enums/authRequestType"; import { Utils } from "@bitwarden/common/misc/utils"; import { PasswordLogInCredentials } from "@bitwarden/common/models/domain/logInCredentials"; -import { TokenRequestPasswordless } from "@bitwarden/common/models/request/identityToken/tokenRequestPasswordless"; import { PasswordlessCreateAuthRequest } from "@bitwarden/common/models/request/passwordlessCreateAuthRequest"; @Component({ @@ -39,6 +38,7 @@ export class LoginWithDeviceComponent protected twoFactorRoute = "2fa"; protected successRoute = "vault"; + private keypair: [ArrayBuffer, ArrayBuffer]; constructor( private router: Router, @@ -63,7 +63,7 @@ export class LoginWithDeviceComponent } //gets signalR push notification - this.authService.getPushNotifcationObs().subscribe((id) => { + this.authService.getPushNotifcationObs$().subscribe((id) => { this.confirmResponse(id, this.accessCode, this.privateKeyValue); }); } @@ -79,12 +79,12 @@ export class LoginWithDeviceComponent async startPasswordlessLogin() { this.resendNotification = false; - const keypair = await this.cryptoFunctionService.rsaGenerateKeyPair(2048); + this.keypair = await this.cryptoFunctionService.rsaGenerateKeyPair(2048); const fingerprint = await ( - await this.cryptoService.getFingerprint(this.email, keypair[0]) + await this.cryptoService.getFingerprint(this.email, this.keypair[0]) ).join("-"); const deviceIdentifier = await this.appIdService.getAppId(); - const publicKey = Utils.fromBufferToB64(keypair[0]); + const publicKey = Utils.fromBufferToB64(this.keypair[0]); const accessCode = await this.PasswordGenerationService.generatePassword({ length: 25 }); const request = new PasswordlessCreateAuthRequest( @@ -105,7 +105,7 @@ export class LoginWithDeviceComponent } this.accessCode = accessCode; - this.privateKeyValue = keypair[1]; + this.privateKeyValue = this.keypair[1]; setTimeout(() => { this.resendNotification = true; @@ -118,36 +118,20 @@ export class LoginWithDeviceComponent private async confirmResponse(requestId: string, accessCode: string, privateKeyVal: ArrayBuffer) { const response = await this.apiService.getAuthResponse(requestId, accessCode); - // if (response.requestApproved) { - await this.setupCaptcha(); - - // const decKey = await this.cryptoService.rsaDecrypt(response.key, keypair[1]); - const decMasterPasswordHash = await this.cryptoService.rsaDecrypt( - response.masterPasswordHash, - privateKeyVal - ); - - try { + if (response.requestApproved) { + //const decKey = await this.cryptoService.rsaDecrypt(response.key, this.keypair[1]); + const decMasterPasswordHash = await this.cryptoService.rsaDecrypt( + response.masterPasswordHash, + privateKeyVal + ); const masterPassword = Utils.fromBufferToB64(decMasterPasswordHash); + // const key = new SymmetricCryptoKey(decKey); + // const localHashedPassword = Utils.fromBufferToB64(decMasterPasswordHash); - const credentials = new PasswordLogInCredentials( - this.email, - masterPassword, - this.captchaToken, - null, - new TokenRequestPasswordless(accessCode) - ); - const loginResponse = await this.authService.logIn(credentials); + try { + const credentials = new PasswordLogInCredentials(this.email, masterPassword); + await this.authService.logIn(credentials); - if (this.handleCaptchaRequired(loginResponse)) { - return; - } else if (loginResponse.requiresTwoFactor) { - if (this.onSuccessfulLoginTwoFactorNavigate != null) { - this.onSuccessfulLoginTwoFactorNavigate(); - } else { - this.router.navigate([this.twoFactorRoute]); - } - } else { const disableFavicon = await this.stateService.getDisableFavicon(); await this.stateService.setDisableFavicon(!!disableFavicon); if (this.onSuccessfulLogin != null) { @@ -158,10 +142,9 @@ export class LoginWithDeviceComponent } else { this.router.navigate([this.successRoute]); } + } catch (error) { + this.logService.error(error); } - } catch (error) { - this.logService.error(error); } } - // } } diff --git a/libs/common/src/abstractions/auth.service.ts b/libs/common/src/abstractions/auth.service.ts index e1100c0950a2..bbe1c01bf29d 100644 --- a/libs/common/src/abstractions/auth.service.ts +++ b/libs/common/src/abstractions/auth.service.ts @@ -6,6 +6,7 @@ import { ApiLogInCredentials, PasswordLogInCredentials, SsoLogInCredentials, + PasswordlessLogInCredentials, } from "../models/domain/logInCredentials"; import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; import { TokenRequestTwoFactor } from "../models/request/identityToken/tokenRequestTwoFactor"; @@ -15,7 +16,11 @@ export abstract class AuthService { masterPasswordHash: string; email: string; logIn: ( - credentials: ApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials + credentials: + | ApiLogInCredentials + | PasswordLogInCredentials + | SsoLogInCredentials + | PasswordlessLogInCredentials ) => Promise; logInTwoFactor: ( twoFactor: TokenRequestTwoFactor, @@ -29,5 +34,5 @@ export abstract class AuthService { getAuthStatus: (userId?: string) => Promise; authResponsePushNotifiction: (notification: AuthRequestPushNotification) => Promise; - getPushNotifcationObs: () => Observable; + getPushNotifcationObs$: () => Observable; } diff --git a/libs/common/src/enums/authenticationType.ts b/libs/common/src/enums/authenticationType.ts index ed7375c8085f..5133c4f648ea 100644 --- a/libs/common/src/enums/authenticationType.ts +++ b/libs/common/src/enums/authenticationType.ts @@ -2,4 +2,5 @@ export enum AuthenticationType { Password = 0, Sso = 1, Api = 2, + Passwordless = 3, } diff --git a/libs/common/src/models/domain/logInCredentials.ts b/libs/common/src/models/domain/logInCredentials.ts index 2f1b638110e2..000e37c97911 100644 --- a/libs/common/src/models/domain/logInCredentials.ts +++ b/libs/common/src/models/domain/logInCredentials.ts @@ -1,7 +1,8 @@ +import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey"; + import { AuthenticationType } from "../../enums/authenticationType"; import { TokenRequestTwoFactor } from "../request/identityToken/tokenRequestTwoFactor"; -import { TokenRequestPasswordless } from "./../request/identityToken/tokenRequestPasswordless"; export class PasswordLogInCredentials { readonly type = AuthenticationType.Password; @@ -10,8 +11,7 @@ export class PasswordLogInCredentials { public email: string, public masterPassword: string, public captchaToken?: string, - public twoFactor?: TokenRequestTwoFactor, - public passwordless?: TokenRequestPasswordless + public twoFactor?: TokenRequestTwoFactor ) {} } @@ -32,3 +32,16 @@ export class ApiLogInCredentials { constructor(public clientId: string, public clientSecret: string) {} } + +export class PasswordlessLogInCredentials { + readonly type = AuthenticationType.Passwordless; + + constructor( + public email: string, + public accessCode: string, + public authRequestId: string, + public key: SymmetricCryptoKey, + public localHashedPassword: string, + public masterPassword: string + ) {} +} diff --git a/libs/common/src/models/response/authRequestResponse.ts b/libs/common/src/models/response/authRequestResponse.ts index 4770efb6cbb0..1a29a3da8568 100644 --- a/libs/common/src/models/response/authRequestResponse.ts +++ b/libs/common/src/models/response/authRequestResponse.ts @@ -19,8 +19,8 @@ export class AuthRequestResponse extends BaseResponse { this.requestDeviceType = this.getResponseProperty("RequestDeviceType"); this.requestIpAddress = this.getResponseProperty("RequestIpAddress"); this.key = this.getResponseProperty("Key"); - this.masterPasswordHash = this.getResponseProperty("MaterPasswordHash"); + this.masterPasswordHash = this.getResponseProperty("MasterPasswordHash"); this.creationDate = this.getResponseProperty("CreationDate"); - this.requestApproved = this.getResponseProperty("requestApproved"); + this.requestApproved = this.getResponseProperty("RequestApproved"); } } diff --git a/libs/common/src/services/auth.service.ts b/libs/common/src/services/auth.service.ts index 9c4fe7ed01cd..5540e1d1155a 100644 --- a/libs/common/src/services/auth.service.ts +++ b/libs/common/src/services/auth.service.ts @@ -25,6 +25,7 @@ import { ApiLogInCredentials, PasswordLogInCredentials, SsoLogInCredentials, + PasswordlessLogInCredentials, } from "../models/domain/logInCredentials"; import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; import { TokenRequestTwoFactor } from "../models/request/identityToken/tokenRequestTwoFactor"; @@ -66,52 +67,75 @@ export class AuthService implements AuthServiceAbstraction { ) {} async logIn( - credentials: ApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials + credentials: + | ApiLogInCredentials + | PasswordLogInCredentials + | SsoLogInCredentials + | PasswordlessLogInCredentials ): Promise { this.clearState(); let strategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy; - if (credentials.type === AuthenticationType.Password) { - strategy = new PasswordLogInStrategy( - this.cryptoService, - this.apiService, - this.tokenService, - this.appIdService, - this.platformUtilsService, - this.messagingService, - this.logService, - this.stateService, - this.twoFactorService, - this - ); - } else if (credentials.type === AuthenticationType.Sso) { - strategy = new SsoLogInStrategy( - this.cryptoService, - this.apiService, - this.tokenService, - this.appIdService, - this.platformUtilsService, - this.messagingService, - this.logService, - this.stateService, - this.twoFactorService, - this.keyConnectorService - ); - } else if (credentials.type === AuthenticationType.Api) { - strategy = new ApiLogInStrategy( - this.cryptoService, - this.apiService, - this.tokenService, - this.appIdService, - this.platformUtilsService, - this.messagingService, - this.logService, - this.stateService, - this.twoFactorService, - this.environmentService, - this.keyConnectorService - ); + switch (credentials.type) { + case AuthenticationType.Password: + strategy = new PasswordLogInStrategy( + this.cryptoService, + this.apiService, + this.tokenService, + this.appIdService, + this.platformUtilsService, + this.messagingService, + this.logService, + this.stateService, + this.twoFactorService, + this + ); + break; + case AuthenticationType.Sso: + strategy = new SsoLogInStrategy( + this.cryptoService, + this.apiService, + this.tokenService, + this.appIdService, + this.platformUtilsService, + this.messagingService, + this.logService, + this.stateService, + this.twoFactorService, + this.keyConnectorService + ); + break; + case AuthenticationType.Api: + strategy = new ApiLogInStrategy( + this.cryptoService, + this.apiService, + this.tokenService, + this.appIdService, + this.platformUtilsService, + this.messagingService, + this.logService, + this.stateService, + this.twoFactorService, + this.environmentService, + this.keyConnectorService + ); + break; + case AuthenticationType.Passwordless: + strategy = new ApiLogInStrategy( + this.cryptoService, + this.apiService, + this.tokenService, + this.appIdService, + this.platformUtilsService, + this.messagingService, + this.logService, + this.stateService, + this.twoFactorService, + this.environmentService, + this.keyConnectorService + ); + break; } const result = await strategy.logIn(credentials as any); @@ -212,7 +236,7 @@ export class AuthService implements AuthServiceAbstraction { this.pushNotificationSubject.next(notification.id); } - getPushNotifcationObs(): Observable { + getPushNotifcationObs$(): Observable { return this.pushNotificationSubject.asObservable(); } From 3dee09608792f75be0c1a02106865ecb775f73ac Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Mon, 29 Aug 2022 09:03:15 -0600 Subject: [PATCH 21/35] removed comment --- libs/common/src/models/domain/logInCredentials.ts | 1 - libs/common/src/services/auth.service.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/libs/common/src/models/domain/logInCredentials.ts b/libs/common/src/models/domain/logInCredentials.ts index 000e37c97911..a092ccac8766 100644 --- a/libs/common/src/models/domain/logInCredentials.ts +++ b/libs/common/src/models/domain/logInCredentials.ts @@ -3,7 +3,6 @@ import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCry import { AuthenticationType } from "../../enums/authenticationType"; import { TokenRequestTwoFactor } from "../request/identityToken/tokenRequestTwoFactor"; - export class PasswordLogInCredentials { readonly type = AuthenticationType.Password; diff --git a/libs/common/src/services/auth.service.ts b/libs/common/src/services/auth.service.ts index 5540e1d1155a..bd25226fab19 100644 --- a/libs/common/src/services/auth.service.ts +++ b/libs/common/src/services/auth.service.ts @@ -232,7 +232,6 @@ export class AuthService implements AuthServiceAbstraction { } async authResponsePushNotifiction(notification: AuthRequestPushNotification): Promise { - //pass id as subject this.pushNotificationSubject.next(notification.id); } From 8d6a410129fd50ae48b00722a56f52249050b888 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Tue, 30 Aug 2022 14:58:36 -0600 Subject: [PATCH 22/35] cleaned up login --- apps/web/src/app/accounts/login-with-device.component.ts | 2 +- libs/angular/src/components/login.component.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index 74d682a72f62..275ee18865bb 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -109,7 +109,7 @@ export class LoginWithDeviceComponent setTimeout(() => { this.resendNotification = true; - }, 2000); + }, 12000); } ngOnDestroy(): void { diff --git a/libs/angular/src/components/login.component.ts b/libs/angular/src/components/login.component.ts index 9d508ebb8b7a..784cda9360f2 100644 --- a/libs/angular/src/components/login.component.ts +++ b/libs/angular/src/components/login.component.ts @@ -24,7 +24,6 @@ import { CaptchaProtectedComponent } from "./captchaProtected.component"; @Directive() export class LoginComponent extends CaptchaProtectedComponent implements OnInit { showPassword = false; - showErrorSummary = false; formPromise: Promise; onSuccessfulLogin: () => Promise; onSuccessfulLoginNavigate: () => Promise; @@ -87,7 +86,6 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit await this.setupCaptcha(); this.formGroup.markAllAsTouched(); - this.showErrorSummary = true; //web if (this.formGroup.invalid && !showToast) { From 4d94cb87999b2206fc6067df0a75c96d0bab2a9a Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Mon, 12 Sep 2022 11:43:27 -0600 Subject: [PATCH 23/35] changed uptyped form builder --- libs/angular/src/components/login.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/angular/src/components/login.component.ts b/libs/angular/src/components/login.component.ts index 784cda9360f2..b0dc25023f62 100644 --- a/libs/angular/src/components/login.component.ts +++ b/libs/angular/src/components/login.component.ts @@ -1,5 +1,5 @@ import { Directive, NgZone, OnInit } from "@angular/core"; -import { UntypedFormBuilder, Validators } from "@angular/forms"; +import { FormBuilder, Validators } from "@angular/forms"; import { Router } from "@angular/router"; import { take } from "rxjs/operators"; @@ -52,7 +52,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit protected cryptoFunctionService: CryptoFunctionService, protected logService: LogService, protected ngZone: NgZone, - protected formBuilder: UntypedFormBuilder, + protected formBuilder: FormBuilder, protected formValidationErrorService: FormValidationErrorsService ) { super(environmentService, i18nService, platformUtilsService); From 376928566327147cea7f8eeaa291610262c0e577 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Mon, 12 Sep 2022 11:58:56 -0600 Subject: [PATCH 24/35] changed uptyped form builder --- apps/browser/src/popup/accounts/login.component.ts | 4 ++-- apps/desktop/src/app/accounts/login.component.ts | 4 ++-- apps/web/src/app/accounts/login.component.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/browser/src/popup/accounts/login.component.ts b/apps/browser/src/popup/accounts/login.component.ts index ba1bd8965182..5028af06bfb8 100644 --- a/apps/browser/src/popup/accounts/login.component.ts +++ b/apps/browser/src/popup/accounts/login.component.ts @@ -1,5 +1,5 @@ import { Component, NgZone } from "@angular/core"; -import { UntypedFormBuilder } from "@angular/forms"; +import { FormBuilder } from "@angular/forms"; import { Router } from "@angular/router"; import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/components/login.component"; @@ -33,7 +33,7 @@ export class LoginComponent extends BaseLoginComponent { syncService: SyncService, logService: LogService, ngZone: NgZone, - formBuilder: UntypedFormBuilder, + formBuilder: FormBuilder, formValidationErrorService: FormValidationErrorsService ) { super( diff --git a/apps/desktop/src/app/accounts/login.component.ts b/apps/desktop/src/app/accounts/login.component.ts index 69132c0c8c5c..33eefbd57ec4 100644 --- a/apps/desktop/src/app/accounts/login.component.ts +++ b/apps/desktop/src/app/accounts/login.component.ts @@ -1,5 +1,5 @@ import { Component, NgZone, OnDestroy, ViewChild, ViewContainerRef } from "@angular/core"; -import { UntypedFormBuilder } from "@angular/forms"; +import { FormBuilder } from "@angular/forms"; import { Router } from "@angular/router"; import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/components/login.component"; @@ -50,7 +50,7 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy { ngZone: NgZone, private messagingService: MessagingService, logService: LogService, - formBuilder: UntypedFormBuilder, + formBuilder: FormBuilder, formValidationErrorService: FormValidationErrorsService ) { super( diff --git a/apps/web/src/app/accounts/login.component.ts b/apps/web/src/app/accounts/login.component.ts index 41c02a4d257a..7a4832768c0f 100644 --- a/apps/web/src/app/accounts/login.component.ts +++ b/apps/web/src/app/accounts/login.component.ts @@ -1,5 +1,5 @@ import { Component, NgZone } from "@angular/core"; -import { UntypedFormBuilder } from "@angular/forms"; +import { FormBuilder } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { first } from "rxjs/operators"; @@ -51,7 +51,7 @@ export class LoginComponent extends BaseLoginComponent { protected stateService: StateService, private messagingService: MessagingService, private routerService: RouterService, - formBuilder: UntypedFormBuilder, + formBuilder: FormBuilder, formValidationErrorService: FormValidationErrorsService ) { super( From 1e59819a4a9ab3bd95188b316dc7644457393482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andre=CC=81=20Bispo?= Date: Wed, 14 Sep 2022 17:41:32 +0100 Subject: [PATCH 25/35] [SG-168] Update login strategy with passwordless login credentials. --- .../accounts/login-with-device.component.ts | 19 ++-- .../misc/logInStrategies/logIn.strategy.ts | 3 +- .../passwordlessLogin.strategy.ts | 90 +++++++++++++++++++ .../src/models/domain/logInCredentials.ts | 10 +-- libs/common/src/services/auth.service.ts | 12 +-- 5 files changed, 112 insertions(+), 22 deletions(-) create mode 100644 libs/common/src/misc/logInStrategies/passwordlessLogin.strategy.ts diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index 2c2ec4496a5b..839264b8a9fb 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -17,7 +17,8 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti import { StateService } from "@bitwarden/common/abstractions/state.service"; import { AuthRequestType } from "@bitwarden/common/enums/authRequestType"; import { Utils } from "@bitwarden/common/misc/utils"; -import { PasswordLogInCredentials } from "@bitwarden/common/models/domain/logInCredentials"; +import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey"; +import { PasswordlessLogInCredentials, PasswordLogInCredentials } from "@bitwarden/common/models/domain/logInCredentials"; import { PasswordlessCreateAuthRequest } from "@bitwarden/common/models/request/passwordlessCreateAuthRequest"; @Component({ @@ -125,20 +126,20 @@ export class LoginWithDeviceComponent private async confirmResponse(requestId: string, accessCode: string, privateKeyVal: ArrayBuffer) { const response = await this.apiService.getAuthResponse(requestId, accessCode); + console.log(response); if (response.requestApproved) { - //const decKey = await this.cryptoService.rsaDecrypt(response.key, this.keypair[1]); + const decKey = await this.cryptoService.rsaDecrypt(response.key, this.keypair[1]); const decMasterPasswordHash = await this.cryptoService.rsaDecrypt( response.masterPasswordHash, - privateKeyVal + this.keypair[1] ); - const masterPassword = Utils.fromBufferToB64(decMasterPasswordHash); - // const key = new SymmetricCryptoKey(decKey); - // const localHashedPassword = Utils.fromBufferToB64(decMasterPasswordHash); - + const key = new SymmetricCryptoKey(decKey); + const localHashedPassword = Utils.fromBufferToB64(decMasterPasswordHash); + console.log('key::', key.encKeyB64); + console.log('hashedPassword::', localHashedPassword); try { - const credentials = new PasswordLogInCredentials(this.email, masterPassword); + const credentials = new PasswordlessLogInCredentials(this.email, localHashedPassword, key); await this.authService.logIn(credentials); - const disableFavicon = await this.stateService.getDisableFavicon(); await this.stateService.setDisableFavicon(!!disableFavicon); if (this.onSuccessfulLogin != null) { diff --git a/libs/common/src/misc/logInStrategies/logIn.strategy.ts b/libs/common/src/misc/logInStrategies/logIn.strategy.ts index 8615700681b8..8231fe12af09 100644 --- a/libs/common/src/misc/logInStrategies/logIn.strategy.ts +++ b/libs/common/src/misc/logInStrategies/logIn.strategy.ts @@ -14,6 +14,7 @@ import { ApiLogInCredentials, PasswordLogInCredentials, SsoLogInCredentials, + PasswordlessLogInCredentials } from "../../models/domain/logInCredentials"; import { DeviceRequest } from "../../models/request/deviceRequest"; import { ApiTokenRequest } from "../../models/request/identityToken/apiTokenRequest"; @@ -42,7 +43,7 @@ export abstract class LogInStrategy { ) {} abstract logIn( - credentials: ApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials + credentials: ApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials | PasswordlessLogInCredentials ): Promise; async logInTwoFactor( diff --git a/libs/common/src/misc/logInStrategies/passwordlessLogin.strategy.ts b/libs/common/src/misc/logInStrategies/passwordlessLogin.strategy.ts new file mode 100644 index 000000000000..54633ab27a9f --- /dev/null +++ b/libs/common/src/misc/logInStrategies/passwordlessLogin.strategy.ts @@ -0,0 +1,90 @@ +import { ApiService } from "../../abstractions/api.service"; +import { AppIdService } from "../../abstractions/appId.service"; +import { AuthService } from "../../abstractions/auth.service"; +import { CryptoService } from "../../abstractions/crypto.service"; +import { LogService } from "../../abstractions/log.service"; +import { MessagingService } from "../../abstractions/messaging.service"; +import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; +import { StateService } from "../../abstractions/state.service"; +import { TokenService } from "../../abstractions/token.service"; +import { TwoFactorService } from "../../abstractions/twoFactor.service"; +import { HashPurpose } from "../../enums/hashPurpose"; +import { AuthResult } from "../../models/domain/authResult"; +import { PasswordlessLogInCredentials } from "../../models/domain/logInCredentials"; +import { SymmetricCryptoKey } from "../../models/domain/symmetricCryptoKey"; +import { PasswordTokenRequest } from "../../models/request/identityToken/passwordTokenRequest"; +import { TokenRequestTwoFactor } from "../../models/request/identityToken/tokenRequestTwoFactor"; + +import { LogInStrategy } from "./logIn.strategy"; + +export class PasswordlessLogInStrategy extends LogInStrategy { + get email() { + return this.tokenRequest.email; + } + + get masterPasswordHash() { + return this.tokenRequest.masterPasswordHash; + } + + tokenRequest: PasswordTokenRequest; + + private localHashedPassword: string; + private key: SymmetricCryptoKey; + + constructor( + cryptoService: CryptoService, + apiService: ApiService, + tokenService: TokenService, + appIdService: AppIdService, + platformUtilsService: PlatformUtilsService, + messagingService: MessagingService, + logService: LogService, + stateService: StateService, + twoFactorService: TwoFactorService, + private authService: AuthService + ) { + super( + cryptoService, + apiService, + tokenService, + appIdService, + platformUtilsService, + messagingService, + logService, + stateService, + twoFactorService + ); + } + + async onSuccessfulLogin() { + await this.cryptoService.setKey(this.key); + await this.cryptoService.setKeyHash(this.localHashedPassword); + } + + async logInTwoFactor( + twoFactor: TokenRequestTwoFactor, + captchaResponse: string + ): Promise { + this.tokenRequest.captchaResponse = captchaResponse ?? this.captchaBypassToken; + return super.logInTwoFactor(twoFactor); + } + + async logIn(credentials: PasswordlessLogInCredentials) { + const { email, masterPasswordHashB64, decKey, twoFactor } = credentials; + + let hashedPassword: string = null; + this.localHashedPassword = masterPasswordHashB64 + this.key = decKey + hashedPassword = masterPasswordHashB64 + + this.tokenRequest = new PasswordTokenRequest( + email, + hashedPassword, + null, + await this.buildTwoFactor(twoFactor), + await this.buildDeviceRequest() + ); + + return this.startLogIn(); + } +} diff --git a/libs/common/src/models/domain/logInCredentials.ts b/libs/common/src/models/domain/logInCredentials.ts index a092ccac8766..3e16da262ee9 100644 --- a/libs/common/src/models/domain/logInCredentials.ts +++ b/libs/common/src/models/domain/logInCredentials.ts @@ -37,10 +37,8 @@ export class PasswordlessLogInCredentials { constructor( public email: string, - public accessCode: string, - public authRequestId: string, - public key: SymmetricCryptoKey, - public localHashedPassword: string, - public masterPassword: string - ) {} + public masterPasswordHashB64: string, + public decKey: SymmetricCryptoKey, + public twoFactor?: TokenRequestTwoFactor) { + } } diff --git a/libs/common/src/services/auth.service.ts b/libs/common/src/services/auth.service.ts index bd25226fab19..b3cee5c247b7 100644 --- a/libs/common/src/services/auth.service.ts +++ b/libs/common/src/services/auth.service.ts @@ -18,6 +18,7 @@ import { AuthenticationType } from "../enums/authenticationType"; import { KdfType } from "../enums/kdfType"; import { KeySuffixOptions } from "../enums/keySuffixOptions"; import { ApiLogInStrategy } from "../misc/logInStrategies/apiLogin.strategy"; +import { PasswordlessLogInStrategy } from "../misc/logInStrategies/passwordlessLogin.strategy"; import { PasswordLogInStrategy } from "../misc/logInStrategies/passwordLogin.strategy"; import { SsoLogInStrategy } from "../misc/logInStrategies/ssoLogin.strategy"; import { AuthResult } from "../models/domain/authResult"; @@ -46,7 +47,7 @@ export class AuthService implements AuthServiceAbstraction { : null; } - private logInStrategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy; + private logInStrategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy | PasswordlessLogInStrategy; private sessionTimeout: any; private pushNotificationSubject = new Subject(); @@ -75,7 +76,7 @@ export class AuthService implements AuthServiceAbstraction { ): Promise { this.clearState(); - let strategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy; + let strategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy | PasswordlessLogInStrategy; switch (credentials.type) { case AuthenticationType.Password: @@ -122,7 +123,7 @@ export class AuthService implements AuthServiceAbstraction { ); break; case AuthenticationType.Passwordless: - strategy = new ApiLogInStrategy( + strategy = new PasswordlessLogInStrategy( this.cryptoService, this.apiService, this.tokenService, @@ -132,8 +133,7 @@ export class AuthService implements AuthServiceAbstraction { this.logService, this.stateService, this.twoFactorService, - this.environmentService, - this.keyConnectorService + this ); break; } @@ -239,7 +239,7 @@ export class AuthService implements AuthServiceAbstraction { return this.pushNotificationSubject.asObservable(); } - private saveState(strategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy) { + private saveState(strategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy | PasswordlessLogInStrategy) { this.logInStrategy = strategy; this.startSessionTimeout(); } From f7d12a6f38d47479e09c9aaa07db494893ae15ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andre=CC=81=20Bispo?= Date: Thu, 15 Sep 2022 13:37:15 +0100 Subject: [PATCH 26/35] [SG-168] Removed logs. Changed inputs for passwordless logic strategy. Removed tokenRequestPasswordless it is using the same as password. --- .../accounts/login-with-device.component.ts | 20 ++++++++++--------- .../misc/logInStrategies/logIn.strategy.ts | 8 ++++++-- .../passwordlessLogin.strategy.ts | 15 ++++++-------- .../src/models/domain/logInCredentials.ts | 8 +++++--- .../identityToken/passwordTokenRequest.ts | 6 ++---- .../request/identityToken/tokenRequest.ts | 15 +++++--------- .../identityToken/tokenRequestPasswordless.ts | 6 ------ libs/common/src/services/auth.service.ts | 20 ++++++++++++++++--- 8 files changed, 52 insertions(+), 46 deletions(-) delete mode 100644 libs/common/src/models/request/identityToken/tokenRequestPasswordless.ts diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index 839264b8a9fb..ded5693b0611 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -1,7 +1,6 @@ import { Component, OnDestroy, OnInit } from "@angular/core"; import { Router } from "@angular/router"; import { Subject, takeUntil } from "rxjs"; - import { CaptchaProtectedComponent } from "@bitwarden/angular/components/captchaProtected.component"; import { AnonymousHubService } from "@bitwarden/common/abstractions/anonymousHub.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -18,7 +17,7 @@ import { StateService } from "@bitwarden/common/abstractions/state.service"; import { AuthRequestType } from "@bitwarden/common/enums/authRequestType"; import { Utils } from "@bitwarden/common/misc/utils"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey"; -import { PasswordlessLogInCredentials, PasswordLogInCredentials } from "@bitwarden/common/models/domain/logInCredentials"; +import { PasswordlessLogInCredentials } from "@bitwarden/common/models/domain/logInCredentials"; import { PasswordlessCreateAuthRequest } from "@bitwarden/common/models/request/passwordlessCreateAuthRequest"; @Component({ @@ -126,19 +125,22 @@ export class LoginWithDeviceComponent private async confirmResponse(requestId: string, accessCode: string, privateKeyVal: ArrayBuffer) { const response = await this.apiService.getAuthResponse(requestId, accessCode); - console.log(response); if (response.requestApproved) { - const decKey = await this.cryptoService.rsaDecrypt(response.key, this.keypair[1]); + const decKey = await this.cryptoService.rsaDecrypt(response.key, privateKeyVal); const decMasterPasswordHash = await this.cryptoService.rsaDecrypt( response.masterPasswordHash, - this.keypair[1] + privateKeyVal ); const key = new SymmetricCryptoKey(decKey); - const localHashedPassword = Utils.fromBufferToB64(decMasterPasswordHash); - console.log('key::', key.encKeyB64); - console.log('hashedPassword::', localHashedPassword); + const localHashedPassword = Utils.fromBufferToUtf8(decMasterPasswordHash); try { - const credentials = new PasswordlessLogInCredentials(this.email, localHashedPassword, key); + const credentials = new PasswordlessLogInCredentials( + this.email, + accessCode, + requestId, + key, + localHashedPassword + ); await this.authService.logIn(credentials); const disableFavicon = await this.stateService.getDisableFavicon(); await this.stateService.setDisableFavicon(!!disableFavicon); diff --git a/libs/common/src/misc/logInStrategies/logIn.strategy.ts b/libs/common/src/misc/logInStrategies/logIn.strategy.ts index 8231fe12af09..577130156f7a 100644 --- a/libs/common/src/misc/logInStrategies/logIn.strategy.ts +++ b/libs/common/src/misc/logInStrategies/logIn.strategy.ts @@ -14,7 +14,7 @@ import { ApiLogInCredentials, PasswordLogInCredentials, SsoLogInCredentials, - PasswordlessLogInCredentials + PasswordlessLogInCredentials, } from "../../models/domain/logInCredentials"; import { DeviceRequest } from "../../models/request/deviceRequest"; import { ApiTokenRequest } from "../../models/request/identityToken/apiTokenRequest"; @@ -43,7 +43,11 @@ export abstract class LogInStrategy { ) {} abstract logIn( - credentials: ApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials | PasswordlessLogInCredentials + credentials: + | ApiLogInCredentials + | PasswordLogInCredentials + | SsoLogInCredentials + | PasswordlessLogInCredentials ): Promise; async logInTwoFactor( diff --git a/libs/common/src/misc/logInStrategies/passwordlessLogin.strategy.ts b/libs/common/src/misc/logInStrategies/passwordlessLogin.strategy.ts index 54633ab27a9f..d66457e53cea 100644 --- a/libs/common/src/misc/logInStrategies/passwordlessLogin.strategy.ts +++ b/libs/common/src/misc/logInStrategies/passwordlessLogin.strategy.ts @@ -70,21 +70,18 @@ export class PasswordlessLogInStrategy extends LogInStrategy { } async logIn(credentials: PasswordlessLogInCredentials) { - const { email, masterPasswordHashB64, decKey, twoFactor } = credentials; - - let hashedPassword: string = null; - this.localHashedPassword = masterPasswordHashB64 - this.key = decKey - hashedPassword = masterPasswordHashB64 + this.localHashedPassword = credentials.localPasswordHash; + this.key = credentials.decKey; this.tokenRequest = new PasswordTokenRequest( - email, - hashedPassword, + credentials.email, + credentials.accessCode, null, - await this.buildTwoFactor(twoFactor), + await this.buildTwoFactor(credentials.twoFactor), await this.buildDeviceRequest() ); + this.tokenRequest.setPasswordlessAccessCode(credentials.authRequestId); return this.startLogIn(); } } diff --git a/libs/common/src/models/domain/logInCredentials.ts b/libs/common/src/models/domain/logInCredentials.ts index 3e16da262ee9..5f2035fd1566 100644 --- a/libs/common/src/models/domain/logInCredentials.ts +++ b/libs/common/src/models/domain/logInCredentials.ts @@ -37,8 +37,10 @@ export class PasswordlessLogInCredentials { constructor( public email: string, - public masterPasswordHashB64: string, + public accessCode: string, + public authRequestId: string, public decKey: SymmetricCryptoKey, - public twoFactor?: TokenRequestTwoFactor) { - } + public localPasswordHash: string, + public twoFactor?: TokenRequestTwoFactor + ) {} } diff --git a/libs/common/src/models/request/identityToken/passwordTokenRequest.ts b/libs/common/src/models/request/identityToken/passwordTokenRequest.ts index 9d75bc808225..eda100374da5 100644 --- a/libs/common/src/models/request/identityToken/passwordTokenRequest.ts +++ b/libs/common/src/models/request/identityToken/passwordTokenRequest.ts @@ -4,7 +4,6 @@ import { CaptchaProtectedRequest } from "../captchaProtectedRequest"; import { DeviceRequest } from "../deviceRequest"; import { TokenRequest } from "./tokenRequest"; -import { TokenRequestPasswordless } from "./tokenRequestPasswordless"; import { TokenRequestTwoFactor } from "./tokenRequestTwoFactor"; export class PasswordTokenRequest extends TokenRequest implements CaptchaProtectedRequest { @@ -13,10 +12,9 @@ export class PasswordTokenRequest extends TokenRequest implements CaptchaProtect public masterPasswordHash: string, public captchaResponse: string, protected twoFactor: TokenRequestTwoFactor, - device?: DeviceRequest, - protected passwordless?: TokenRequestPasswordless + device?: DeviceRequest ) { - super(twoFactor, device, passwordless); + super(twoFactor, device); } toIdentityToken(clientId: ClientType) { diff --git a/libs/common/src/models/request/identityToken/tokenRequest.ts b/libs/common/src/models/request/identityToken/tokenRequest.ts index 7f33ce2c552a..4c571befd4b2 100644 --- a/libs/common/src/models/request/identityToken/tokenRequest.ts +++ b/libs/common/src/models/request/identityToken/tokenRequest.ts @@ -1,16 +1,11 @@ import { DeviceRequest } from "../deviceRequest"; - -import { TokenRequestPasswordless } from "./tokenRequestPasswordless"; import { TokenRequestTwoFactor } from "./tokenRequestTwoFactor"; export abstract class TokenRequest { protected device?: DeviceRequest; + protected passwordlessAuthRequest: string; - constructor( - protected twoFactor: TokenRequestTwoFactor, - device?: DeviceRequest, - protected passwordless?: TokenRequestPasswordless - ) { + constructor(protected twoFactor: TokenRequestTwoFactor, device?: DeviceRequest) { this.device = device != null ? device : null; } @@ -24,7 +19,7 @@ export abstract class TokenRequest { } setPasswordlessAccessCode(accessCode: string) { - this.passwordless.authRequest = accessCode; + this.passwordlessAuthRequest = accessCode; } protected toIdentityToken(clientId: string) { @@ -42,8 +37,8 @@ export abstract class TokenRequest { } //passswordless login - if (this.passwordless?.authRequest) { - obj.authRequest = this.passwordless.authRequest; + if (this.passwordlessAuthRequest) { + obj.authRequest = this.passwordlessAuthRequest; } if (this.twoFactor.token && this.twoFactor.provider != null) { diff --git a/libs/common/src/models/request/identityToken/tokenRequestPasswordless.ts b/libs/common/src/models/request/identityToken/tokenRequestPasswordless.ts deleted file mode 100644 index 23874cad663c..000000000000 --- a/libs/common/src/models/request/identityToken/tokenRequestPasswordless.ts +++ /dev/null @@ -1,6 +0,0 @@ -export class TokenRequestPasswordless { - authRequest: string; - constructor(authRequest: string) { - this.authRequest = authRequest; - } -} diff --git a/libs/common/src/services/auth.service.ts b/libs/common/src/services/auth.service.ts index b3cee5c247b7..e9fdac8d7e65 100644 --- a/libs/common/src/services/auth.service.ts +++ b/libs/common/src/services/auth.service.ts @@ -47,7 +47,11 @@ export class AuthService implements AuthServiceAbstraction { : null; } - private logInStrategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy | PasswordlessLogInStrategy; + private logInStrategy: + | ApiLogInStrategy + | PasswordLogInStrategy + | SsoLogInStrategy + | PasswordlessLogInStrategy; private sessionTimeout: any; private pushNotificationSubject = new Subject(); @@ -76,7 +80,11 @@ export class AuthService implements AuthServiceAbstraction { ): Promise { this.clearState(); - let strategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy | PasswordlessLogInStrategy; + let strategy: + | ApiLogInStrategy + | PasswordLogInStrategy + | SsoLogInStrategy + | PasswordlessLogInStrategy; switch (credentials.type) { case AuthenticationType.Password: @@ -239,7 +247,13 @@ export class AuthService implements AuthServiceAbstraction { return this.pushNotificationSubject.asObservable(); } - private saveState(strategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy | PasswordlessLogInStrategy) { + private saveState( + strategy: + | ApiLogInStrategy + | PasswordLogInStrategy + | SsoLogInStrategy + | PasswordlessLogInStrategy + ) { this.logInStrategy = strategy; this.startSessionTimeout(); } From 07f26e949124634d8e74839e160ad8c746a9b683 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Thu, 15 Sep 2022 14:48:08 -0600 Subject: [PATCH 27/35] code cleanup --- .../accounts/login-with-device.component.html | 7 ++++--- .../accounts/login-with-device.component.ts | 19 ++++++++++++------- .../passwordlessLogin.strategy.ts | 1 - .../request/identityToken/tokenRequest.ts | 1 + libs/common/src/services/auth.service.ts | 2 +- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/apps/web/src/app/accounts/login-with-device.component.html b/apps/web/src/app/accounts/login-with-device.component.html index a9917684d6ce..6d14f1d3dab8 100644 --- a/apps/web/src/app/accounts/login-with-device.component.html +++ b/apps/web/src/app/accounts/login-with-device.component.html @@ -22,8 +22,9 @@

{{ "logInInitiated" | i18n }}

{{ "fingerprintPhraseHeader" | i18n }}

- - {{ fingerPrint }} +

+ {{ fingerPrint }} +

@@ -35,7 +36,7 @@

{{ "fingerprintPhraseHeader" | i18n }}


-

{{ "loginWithDevciceEnabledInfo" | i18n }}

+ {{ "loginWithDevciceEnabledInfo" | i18n }} {{ "viewAllLoginOptions" | i18n }}
diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index ded5693b0611..67493fadcd19 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -1,6 +1,7 @@ import { Component, OnDestroy, OnInit } from "@angular/core"; import { Router } from "@angular/router"; import { Subject, takeUntil } from "rxjs"; + import { CaptchaProtectedComponent } from "@bitwarden/angular/components/captchaProtected.component"; import { AnonymousHubService } from "@bitwarden/common/abstractions/anonymousHub.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -16,8 +17,8 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti import { StateService } from "@bitwarden/common/abstractions/state.service"; import { AuthRequestType } from "@bitwarden/common/enums/authRequestType"; import { Utils } from "@bitwarden/common/misc/utils"; -import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey"; import { PasswordlessLogInCredentials } from "@bitwarden/common/models/domain/logInCredentials"; +import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey"; import { PasswordlessCreateAuthRequest } from "@bitwarden/common/models/request/passwordlessCreateAuthRequest"; @Component({ @@ -103,14 +104,18 @@ export class LoginWithDeviceComponent this.fingerPrint = fingerprint; - const reqResponse = await this.apiService.postAuthRequest(request); + try { + const reqResponse = await this.apiService.postAuthRequest(request); - if (reqResponse.id) { - this.anonymousHubService.createHubConnection(reqResponse.id); - } + if (reqResponse.id) { + this.anonymousHubService.createHubConnection(reqResponse.id); - this.accessCode = accessCode; - this.privateKeyValue = this.keypair[1]; + this.accessCode = accessCode; + this.privateKeyValue = this.keypair[1]; + } + } catch (e) { + this.logService.error(e); + } setTimeout(() => { this.resendNotification = true; diff --git a/libs/common/src/misc/logInStrategies/passwordlessLogin.strategy.ts b/libs/common/src/misc/logInStrategies/passwordlessLogin.strategy.ts index d66457e53cea..0acc4a49f00c 100644 --- a/libs/common/src/misc/logInStrategies/passwordlessLogin.strategy.ts +++ b/libs/common/src/misc/logInStrategies/passwordlessLogin.strategy.ts @@ -8,7 +8,6 @@ import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; import { StateService } from "../../abstractions/state.service"; import { TokenService } from "../../abstractions/token.service"; import { TwoFactorService } from "../../abstractions/twoFactor.service"; -import { HashPurpose } from "../../enums/hashPurpose"; import { AuthResult } from "../../models/domain/authResult"; import { PasswordlessLogInCredentials } from "../../models/domain/logInCredentials"; import { SymmetricCryptoKey } from "../../models/domain/symmetricCryptoKey"; diff --git a/libs/common/src/models/request/identityToken/tokenRequest.ts b/libs/common/src/models/request/identityToken/tokenRequest.ts index 4c571befd4b2..5e38d2069b07 100644 --- a/libs/common/src/models/request/identityToken/tokenRequest.ts +++ b/libs/common/src/models/request/identityToken/tokenRequest.ts @@ -1,4 +1,5 @@ import { DeviceRequest } from "../deviceRequest"; + import { TokenRequestTwoFactor } from "./tokenRequestTwoFactor"; export abstract class TokenRequest { diff --git a/libs/common/src/services/auth.service.ts b/libs/common/src/services/auth.service.ts index e9fdac8d7e65..3807eee3d69b 100644 --- a/libs/common/src/services/auth.service.ts +++ b/libs/common/src/services/auth.service.ts @@ -18,8 +18,8 @@ import { AuthenticationType } from "../enums/authenticationType"; import { KdfType } from "../enums/kdfType"; import { KeySuffixOptions } from "../enums/keySuffixOptions"; import { ApiLogInStrategy } from "../misc/logInStrategies/apiLogin.strategy"; -import { PasswordlessLogInStrategy } from "../misc/logInStrategies/passwordlessLogin.strategy"; import { PasswordLogInStrategy } from "../misc/logInStrategies/passwordLogin.strategy"; +import { PasswordlessLogInStrategy } from "../misc/logInStrategies/passwordlessLogin.strategy"; import { SsoLogInStrategy } from "../misc/logInStrategies/ssoLogin.strategy"; import { AuthResult } from "../models/domain/authResult"; import { From 1a869a9cf7e3f2811bd0b98c3c0d6cf11c97c357 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Thu, 15 Sep 2022 14:57:18 -0600 Subject: [PATCH 28/35] code cleanup --- apps/web/src/app/accounts/login-with-device.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index 67493fadcd19..146813bf0546 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -130,6 +130,7 @@ export class LoginWithDeviceComponent private async confirmResponse(requestId: string, accessCode: string, privateKeyVal: ArrayBuffer) { const response = await this.apiService.getAuthResponse(requestId, accessCode); + if (response.requestApproved) { const decKey = await this.cryptoService.rsaDecrypt(response.key, privateKeyVal); const decMasterPasswordHash = await this.cryptoService.rsaDecrypt( From c5d796eccb9d6dc55d82f61bf9b9c0e41a84ecec Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Tue, 20 Sep 2022 22:51:51 -0600 Subject: [PATCH 29/35] removed login with device from self hosted --- apps/web/src/app/accounts/login.component.html | 2 +- libs/angular/src/components/login.component.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/accounts/login.component.html b/apps/web/src/app/accounts/login.component.html index 41b3f9c2d38a..a7facd42916c 100644 --- a/apps/web/src/app/accounts/login.component.html +++ b/apps/web/src/app/accounts/login.component.html @@ -96,7 +96,7 @@
-
+
-
+
{{ "resendNotification" | i18n }} diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login-with-device.component.ts index 146813bf0546..64ab6b09faac 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login-with-device.component.ts @@ -21,6 +21,8 @@ import { PasswordlessLogInCredentials } from "@bitwarden/common/models/domain/lo import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey"; import { PasswordlessCreateAuthRequest } from "@bitwarden/common/models/request/passwordlessCreateAuthRequest"; +import { AuthRequestResponse } from "./../../../../../libs/common/src/models/response/authRequestResponse"; + @Component({ selector: "app-login-with-device", templateUrl: "login-with-device.component.html", @@ -29,19 +31,17 @@ export class LoginWithDeviceComponent extends CaptchaProtectedComponent implements OnInit, OnDestroy { - private accessCode: string; - private privateKeyValue: ArrayBuffer; private destroy$ = new Subject(); - fingerPrint: string; email: string; - resendNotification = false; + showResendNotification = false; + passwordlessRequest: PasswordlessCreateAuthRequest; onSuccessfulLoginTwoFactorNavigate: () => Promise; onSuccessfulLogin: () => Promise; onSuccessfulLoginNavigate: () => Promise; protected twoFactorRoute = "2fa"; protected successRoute = "vault"; - private keypair: [ArrayBuffer, ArrayBuffer]; + private authRequestKeyPair: [publicKey: ArrayBuffer, privateKey: ArrayBuffer]; constructor( private router: Router, @@ -70,7 +70,7 @@ export class LoginWithDeviceComponent .getPushNotifcationObs$() .pipe(takeUntil(this.destroy$)) .subscribe((id) => { - this.confirmResponse(id, this.accessCode, this.privateKeyValue); + this.confirmResponse(id); }); } @@ -84,41 +84,21 @@ export class LoginWithDeviceComponent } async startPasswordlessLogin() { - this.resendNotification = false; - this.keypair = await this.cryptoFunctionService.rsaGenerateKeyPair(2048); - const fingerprint = await ( - await this.cryptoService.getFingerprint(this.email, this.keypair[0]) - ).join("-"); - const deviceIdentifier = await this.appIdService.getAppId(); - const publicKey = Utils.fromBufferToB64(this.keypair[0]); - const accessCode = await this.passwordGenerationService.generatePassword({ length: 25 }); - - const request = new PasswordlessCreateAuthRequest( - this.email, - deviceIdentifier, - publicKey, - AuthRequestType.AuthenticateAndUnlock, - accessCode, - fingerprint - ); - - this.fingerPrint = fingerprint; + this.showResendNotification = false; try { - const reqResponse = await this.apiService.postAuthRequest(request); + this.buildAuthRequest(); + const reqResponse = await this.apiService.postAuthRequest(this.passwordlessRequest); if (reqResponse.id) { this.anonymousHubService.createHubConnection(reqResponse.id); - - this.accessCode = accessCode; - this.privateKeyValue = this.keypair[1]; } } catch (e) { this.logService.error(e); } setTimeout(() => { - this.resendNotification = true; + this.showResendNotification = true; }, 12000); } @@ -128,39 +108,69 @@ export class LoginWithDeviceComponent this.anonymousHubService.stopHubConnection(); } - private async confirmResponse(requestId: string, accessCode: string, privateKeyVal: ArrayBuffer) { - const response = await this.apiService.getAuthResponse(requestId, accessCode); - - if (response.requestApproved) { - const decKey = await this.cryptoService.rsaDecrypt(response.key, privateKeyVal); - const decMasterPasswordHash = await this.cryptoService.rsaDecrypt( - response.masterPasswordHash, - privateKeyVal + private async confirmResponse(requestId: string) { + try { + const response = await this.apiService.getAuthResponse( + requestId, + this.passwordlessRequest.accessCode ); - const key = new SymmetricCryptoKey(decKey); - const localHashedPassword = Utils.fromBufferToUtf8(decMasterPasswordHash); - try { - const credentials = new PasswordlessLogInCredentials( - this.email, - accessCode, - requestId, - key, - localHashedPassword - ); - await this.authService.logIn(credentials); - const disableFavicon = await this.stateService.getDisableFavicon(); - await this.stateService.setDisableFavicon(!!disableFavicon); - if (this.onSuccessfulLogin != null) { - this.onSuccessfulLogin(); - } - if (this.onSuccessfulLoginNavigate != null) { - this.onSuccessfulLoginNavigate(); - } else { - this.router.navigate([this.successRoute]); - } - } catch (error) { - this.logService.error(error); + + if (!response.requestApproved) { + return; + } + + const credentials = await this.buildLoginCredntials(requestId, response); + await this.authService.logIn(credentials); + if (this.onSuccessfulLogin != null) { + this.onSuccessfulLogin(); + } + if (this.onSuccessfulLoginNavigate != null) { + this.onSuccessfulLoginNavigate(); + } else { + this.router.navigate([this.successRoute]); } + } catch (error) { + this.logService.error(error); } } + + private async buildAuthRequest() { + this.authRequestKeyPair = await this.cryptoFunctionService.rsaGenerateKeyPair(2048); + const fingerprint = await ( + await this.cryptoService.getFingerprint(this.email, this.authRequestKeyPair[0]) + ).join("-"); + const deviceIdentifier = await this.appIdService.getAppId(); + const publicKey = Utils.fromBufferToB64(this.authRequestKeyPair[0]); + const accessCode = await this.passwordGenerationService.generatePassword({ length: 25 }); + + this.passwordlessRequest = new PasswordlessCreateAuthRequest( + this.email, + deviceIdentifier, + publicKey, + AuthRequestType.AuthenticateAndUnlock, + accessCode, + fingerprint + ); + } + + private async buildLoginCredntials( + requestId: string, + response: AuthRequestResponse + ): Promise { + const decKey = await this.cryptoService.rsaDecrypt(response.key, this.authRequestKeyPair[1]); + const decMasterPasswordHash = await this.cryptoService.rsaDecrypt( + response.masterPasswordHash, + this.authRequestKeyPair[1] + ); + const key = new SymmetricCryptoKey(decKey); + const localHashedPassword = Utils.fromBufferToUtf8(decMasterPasswordHash); + + return new PasswordlessLogInCredentials( + this.email, + this.passwordlessRequest.accessCode, + requestId, + key, + localHashedPassword + ); + } } diff --git a/libs/common/src/enums/notificationType.ts b/libs/common/src/enums/notificationType.ts index d694053e3ea2..457ad174cad7 100644 --- a/libs/common/src/enums/notificationType.ts +++ b/libs/common/src/enums/notificationType.ts @@ -18,6 +18,6 @@ export enum NotificationType { SyncSendUpdate = 13, SyncSendDelete = 14, - AuthRequest, - AuthRequestResponse, + AuthRequest = 15, + AuthRequestResponse = 16, } diff --git a/libs/common/src/models/data/loginAuthData.ts b/libs/common/src/models/data/loginAuthData.ts deleted file mode 100644 index 0c2aefcb5ea4..000000000000 --- a/libs/common/src/models/data/loginAuthData.ts +++ /dev/null @@ -1,11 +0,0 @@ -export class LoginAuthData { - captchaToken?: string; - - constructor( - readonly email: string, - readonly accessCode: string, - readonly authRequestId: string, - readonly masterKey: ArrayBuffer, - readonly masterPasswordHash: ArrayBuffer - ) {} -} diff --git a/libs/common/src/models/data/loginCompleteAuthData.ts b/libs/common/src/models/data/loginCompleteAuthData.ts deleted file mode 100644 index 1a0e18b2b060..000000000000 --- a/libs/common/src/models/data/loginCompleteAuthData.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { TwoFactorProviderType } from "@bitwarden/common/enums/twoFactorProviderType"; - -import { LoginAuthData } from "./loginAuthData"; -export class LoginCompleteAuthData extends LoginAuthData { - remember?: boolean; - constructor( - readonly email: string, - readonly accessCode: string, - readonly authRequestId: string, - readonly masterKey: ArrayBuffer, - readonly masterPasswordHash: ArrayBuffer, - readonly twoFactorProvider: TwoFactorProviderType, - readonly twoFactorToken: string - ) { - super(email, accessCode, authRequestId, masterKey, masterPasswordHash); - } -} From 424bc1b79819d5482a223fca1faa52298bd9beac Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Thu, 22 Sep 2022 12:15:31 -0600 Subject: [PATCH 31/35] added module for login --- .../{ => login}/login-with-device.component.html | 0 .../{ => login}/login-with-device.component.ts | 3 +-- .../app/accounts/{ => login}/login.component.html | 0 .../src/app/accounts/{ => login}/login.component.ts | 2 +- apps/web/src/app/accounts/login/login.module.ts | 13 +++++++++++++ apps/web/src/app/oss-routing.module.ts | 4 ++-- apps/web/src/app/oss.module.ts | 3 +++ apps/web/src/app/shared/loose-components.module.ts | 7 ------- 8 files changed, 20 insertions(+), 12 deletions(-) rename apps/web/src/app/accounts/{ => login}/login-with-device.component.html (100%) rename apps/web/src/app/accounts/{ => login}/login-with-device.component.ts (98%) rename apps/web/src/app/accounts/{ => login}/login.component.html (100%) rename apps/web/src/app/accounts/{ => login}/login.component.ts (99%) create mode 100644 apps/web/src/app/accounts/login/login.module.ts diff --git a/apps/web/src/app/accounts/login-with-device.component.html b/apps/web/src/app/accounts/login/login-with-device.component.html similarity index 100% rename from apps/web/src/app/accounts/login-with-device.component.html rename to apps/web/src/app/accounts/login/login-with-device.component.html diff --git a/apps/web/src/app/accounts/login-with-device.component.ts b/apps/web/src/app/accounts/login/login-with-device.component.ts similarity index 98% rename from apps/web/src/app/accounts/login-with-device.component.ts rename to apps/web/src/app/accounts/login/login-with-device.component.ts index 64ab6b09faac..ec8fbb9fcd2e 100644 --- a/apps/web/src/app/accounts/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login/login-with-device.component.ts @@ -20,8 +20,7 @@ import { Utils } from "@bitwarden/common/misc/utils"; import { PasswordlessLogInCredentials } from "@bitwarden/common/models/domain/logInCredentials"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey"; import { PasswordlessCreateAuthRequest } from "@bitwarden/common/models/request/passwordlessCreateAuthRequest"; - -import { AuthRequestResponse } from "./../../../../../libs/common/src/models/response/authRequestResponse"; +import { AuthRequestResponse } from "@bitwarden/common/models/response/authRequestResponse"; @Component({ selector: "app-login-with-device", diff --git a/apps/web/src/app/accounts/login.component.html b/apps/web/src/app/accounts/login/login.component.html similarity index 100% rename from apps/web/src/app/accounts/login.component.html rename to apps/web/src/app/accounts/login/login.component.html diff --git a/apps/web/src/app/accounts/login.component.ts b/apps/web/src/app/accounts/login/login.component.ts similarity index 99% rename from apps/web/src/app/accounts/login.component.ts rename to apps/web/src/app/accounts/login/login.component.ts index 7a4832768c0f..4dfcd7d99337 100644 --- a/apps/web/src/app/accounts/login.component.ts +++ b/apps/web/src/app/accounts/login/login.component.ts @@ -22,7 +22,7 @@ import { Policy } from "@bitwarden/common/models/domain/policy"; import { ListResponse } from "@bitwarden/common/models/response/listResponse"; import { PolicyResponse } from "@bitwarden/common/models/response/policyResponse"; -import { RouterService, StateService } from "../core"; +import { RouterService, StateService } from "../../core"; @Component({ selector: "app-login", diff --git a/apps/web/src/app/accounts/login/login.module.ts b/apps/web/src/app/accounts/login/login.module.ts new file mode 100644 index 000000000000..9ab8dfb3a1b2 --- /dev/null +++ b/apps/web/src/app/accounts/login/login.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from "@angular/core"; + +import { SharedModule } from "../../shared"; + +import { LoginWithDeviceComponent } from "./login-with-device.component"; +import { LoginComponent } from "./login.component"; + +@NgModule({ + imports: [SharedModule], + declarations: [LoginComponent, LoginWithDeviceComponent], + exports: [LoginComponent, LoginWithDeviceComponent], +}) +export class LoginModule {} diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 5b2b5b3ce955..6e1c4e569ef1 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -11,8 +11,8 @@ import { AcceptEmergencyComponent } from "./accounts/accept-emergency.component" import { AcceptOrganizationComponent } from "./accounts/accept-organization.component"; import { HintComponent } from "./accounts/hint.component"; import { LockComponent } from "./accounts/lock.component"; -import { LoginWithDeviceComponent } from "./accounts/login-with-device.component"; -import { LoginComponent } from "./accounts/login.component"; +import { LoginWithDeviceComponent } from "./accounts/login/login-with-device.component"; +import { LoginComponent } from "./accounts/login/login.component"; import { RecoverDeleteComponent } from "./accounts/recover-delete.component"; import { RecoverTwoFactorComponent } from "./accounts/recover-two-factor.component"; import { RegisterComponent } from "./accounts/register.component"; diff --git a/apps/web/src/app/oss.module.ts b/apps/web/src/app/oss.module.ts index 0885d7d5d770..457200a0e9b8 100644 --- a/apps/web/src/app/oss.module.ts +++ b/apps/web/src/app/oss.module.ts @@ -1,5 +1,6 @@ import { NgModule } from "@angular/core"; +import { LoginModule } from "./accounts/login/login.module"; import { TrialInitiationModule } from "./accounts/trial-initiation/trial-initiation.module"; import { OrganizationCreateModule } from "./organizations/create/organization-create.module"; import { OrganizationManageModule } from "./organizations/manage/organization-manage.module"; @@ -18,6 +19,7 @@ import { VaultFilterModule } from "./vault/vault-filter/vault-filter.module"; OrganizationManageModule, OrganizationUserModule, OrganizationCreateModule, + LoginModule, ], exports: [ SharedModule, @@ -25,6 +27,7 @@ import { VaultFilterModule } from "./vault/vault-filter/vault-filter.module"; TrialInitiationModule, VaultFilterModule, OrganizationBadgeModule, + LoginModule, ], bootstrap: [], }) diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index 28585b2464e8..c68741b5a2bb 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -6,7 +6,6 @@ import { AcceptEmergencyComponent } from "../accounts/accept-emergency.component import { AcceptOrganizationComponent } from "../accounts/accept-organization.component"; import { HintComponent } from "../accounts/hint.component"; import { LockComponent } from "../accounts/lock.component"; -import { LoginComponent } from "../accounts/login.component"; import { RecoverDeleteComponent } from "../accounts/recover-delete.component"; import { RecoverTwoFactorComponent } from "../accounts/recover-two-factor.component"; import { RegisterFormModule } from "../accounts/register-form/register-form.module"; @@ -150,8 +149,6 @@ import { OrganizationBadgeModule } from "../vault/organization-badge/organizatio import { ShareComponent } from "../vault/share.component"; import { VaultFilterModule } from "../vault/vault-filter/vault-filter.module"; -import { LoginWithDeviceComponent } from "./../accounts/login-with-device.component"; - import { SharedModule } from "."; // Please do not add to this list of declarations - we should refactor these into modules when doing so makes sense until there are none left. @@ -212,8 +209,6 @@ import { SharedModule } from "."; FrontendLayoutComponent, HintComponent, LockComponent, - LoginComponent, - LoginWithDeviceComponent, MasterPasswordPolicyComponent, NavbarComponent, NestedCheckboxComponent, @@ -358,8 +353,6 @@ import { SharedModule } from "."; FrontendLayoutComponent, HintComponent, LockComponent, - LoginComponent, - LoginWithDeviceComponent, MasterPasswordPolicyComponent, NavbarComponent, NestedCheckboxComponent, From c434e03f2c7deabbe0b5247cb04d2d7cd4521f00 Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Thu, 22 Sep 2022 14:12:28 -0600 Subject: [PATCH 32/35] fixed post request bug --- apps/web/src/app/accounts/login/login-with-device.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/app/accounts/login/login-with-device.component.ts b/apps/web/src/app/accounts/login/login-with-device.component.ts index ec8fbb9fcd2e..4c6f6268dfd7 100644 --- a/apps/web/src/app/accounts/login/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login/login-with-device.component.ts @@ -86,7 +86,7 @@ export class LoginWithDeviceComponent this.showResendNotification = false; try { - this.buildAuthRequest(); + await this.buildAuthRequest(); const reqResponse = await this.apiService.postAuthRequest(this.passwordlessRequest); if (reqResponse.id) { From 2a08188425e10c6091dd7cdb7ab1f34867d46e3f Mon Sep 17 00:00:00 2001 From: gbubemismith Date: Mon, 26 Sep 2022 14:28:53 -0600 Subject: [PATCH 33/35] added feature flag --- apps/web/config/cloud.json | 3 ++- apps/web/config/development.json | 3 ++- apps/web/src/app/accounts/login/login.component.html | 2 +- apps/web/src/app/accounts/login/login.component.ts | 4 ++++ apps/web/src/utils/flags.ts | 1 + 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/web/config/cloud.json b/apps/web/config/cloud.json index 96d692f7e8dd..5bd5e6b0608e 100644 --- a/apps/web/config/cloud.json +++ b/apps/web/config/cloud.json @@ -16,6 +16,7 @@ "proxyEvents": "https://events.bitwarden.com" }, "flags": { - "showTrial": true + "showTrial": true, + "showPasswordless": false } } diff --git a/apps/web/config/development.json b/apps/web/config/development.json index e3048db7a228..44dd9bee0930 100644 --- a/apps/web/config/development.json +++ b/apps/web/config/development.json @@ -10,6 +10,7 @@ "proxyNotifications": "http://localhost:61840" }, "flags": { - "showTrial": true + "showTrial": true, + "showPasswordless": false } } diff --git a/apps/web/src/app/accounts/login/login.component.html b/apps/web/src/app/accounts/login/login.component.html index a7facd42916c..7df9777f39c6 100644 --- a/apps/web/src/app/accounts/login/login.component.html +++ b/apps/web/src/app/accounts/login/login.component.html @@ -96,7 +96,7 @@
-
+