mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-30 13:03:53 +01:00
[PM-1033] Org invite user creation flow 1 (#5611)
* [PM-1033] feat: basic redirection to login initiated * [PM-1033] feat: add ui for TDE enrollment * [PM-1033] feat: implement auto-enroll * [PM-1033] chore: add todo * [PM-1033] feat: add support in browser * [PM-1033] feat: add support for desktop * [PM-1033] feat: improve key check hack to allow regular accounts * [PM-1033] feat: init asymmetric account keys * [PM-1033] chore: temporary fix bug from merge * [PM-1033] feat: properly check if user can go ahead an auto-enroll * [PM-1033] feat: simplify approval required * [PM-1033] feat: rewrite using discrete states * [PM-1033] fix: clean-up and fix merge artifacts * [PM-1033] chore: clean up empty ng-container * [PM-1033] fix: new user identification logic * [PM-1033] feat: optimize data fetching * [PM-1033] feat: split user creating and reset enrollment * [PM-1033] fix: add missing loading false statement * [PM-1033] fix: navigation logic in sso component * [PM-1033] fix: add missing query param * [PM-1033] chore: rename to `ExistingUserUntrustedDevice` * PM-1033 - fix component templates to reference `ExistingUserUntrustedDevice` so clients can build --------- Co-authored-by: Jared Snider <jsnider@bitwarden.com>
This commit is contained in:
parent
e50e524920
commit
887b2ec78e
@ -11,6 +11,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-container *ngIf="!loading">
|
<ng-container *ngIf="!loading">
|
||||||
|
<ng-container *ngIf="data.state == State.ExistingUserUntrustedDevice">
|
||||||
<div class="standard-x-margin">
|
<div class="standard-x-margin">
|
||||||
<p class="lead">{{ "logInInitiated" | i18n }}</p>
|
<p class="lead">{{ "logInInitiated" | i18n }}</p>
|
||||||
<h6 class="mb-20px">{{ "deviceApprovalRequired" | i18n }}</h6>
|
<h6 class="mb-20px">{{ "deviceApprovalRequired" | i18n }}</h6>
|
||||||
@ -21,7 +22,7 @@
|
|||||||
class="mb-20px standard-x-margin"
|
class="mb-20px standard-x-margin"
|
||||||
[formGroup]="rememberDeviceForm"
|
[formGroup]="rememberDeviceForm"
|
||||||
>
|
>
|
||||||
<div class="">
|
<div>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="rememberDevice"
|
id="rememberDevice"
|
||||||
@ -37,7 +38,7 @@
|
|||||||
|
|
||||||
<div class="box mb-20px">
|
<div class="box mb-20px">
|
||||||
<button
|
<button
|
||||||
*ngIf="showApproveFromOtherDeviceBtn"
|
*ngIf="data.showApproveFromOtherDeviceBtn"
|
||||||
(click)="approveFromOtherDevice()"
|
(click)="approveFromOtherDevice()"
|
||||||
type="button"
|
type="button"
|
||||||
class="btn primary block"
|
class="btn primary block"
|
||||||
@ -45,7 +46,7 @@
|
|||||||
<b>{{ "approveFromYourOtherDevice" | i18n }}</b>
|
<b>{{ "approveFromYourOtherDevice" | i18n }}</b>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
*ngIf="showReqAdminApprovalBtn"
|
*ngIf="data.showReqAdminApprovalBtn"
|
||||||
(click)="requestAdminApproval()"
|
(click)="requestAdminApproval()"
|
||||||
type="button"
|
type="button"
|
||||||
class="btn block btn-top-margin"
|
class="btn block btn-top-margin"
|
||||||
@ -53,19 +54,51 @@
|
|||||||
{{ "requestAdminApproval" | i18n }}
|
{{ "requestAdminApproval" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
*ngIf="data.showApproveWithMasterPasswordBtn"
|
||||||
type="button"
|
type="button"
|
||||||
class="btn block btn-top-margin"
|
class="btn block btn-top-margin"
|
||||||
*ngIf="showApproveWithMasterPasswordBtn"
|
|
||||||
(click)="approveWithMasterPassword()"
|
(click)="approveWithMasterPassword()"
|
||||||
>
|
>
|
||||||
{{ "approveWithMasterPassword" | i18n }}
|
{{ "approveWithMasterPassword" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="data.state == State.NewUser">
|
||||||
|
<div class="standard-x-margin">
|
||||||
|
<p class="lead">{{ "logInInitiated" | i18n }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form
|
||||||
|
id="rememberDeviceForm"
|
||||||
|
class="mb-20px standard-x-margin"
|
||||||
|
[formGroup]="rememberDeviceForm"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="rememberDevice"
|
||||||
|
name="rememberDevice"
|
||||||
|
formControlName="rememberDevice"
|
||||||
|
/>
|
||||||
|
<label for="rememberDevice">
|
||||||
|
{{ "rememberThisDevice" | i18n }}
|
||||||
|
</label>
|
||||||
|
<p id="rememberThisDeviceHintText">{{ "uncheckIfPublicDevice" | i18n }}</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="box mb-20px">
|
||||||
|
<button (click)="createUser()" type="button" class="btn primary block">
|
||||||
|
<b>{{ "continue" | i18n }}</b>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<hr class="muted-hr mx-5px mb-20px" />
|
<hr class="muted-hr mx-5px mb-20px" />
|
||||||
|
|
||||||
<div class="small mx-5px">
|
<div class="small mx-5px">
|
||||||
<p class="no-margin">{{ "loggingInAs" | i18n }} {{ userEmail }}</p>
|
<p class="no-margin">{{ "loggingInAs" | i18n }} {{ data.userEmail }}</p>
|
||||||
<a tabindex="0" role="button" style="cursor: pointer" (click)="logOut()">{{
|
<a tabindex="0" role="button" style="cursor: pointer" (click)="logOut()">{{
|
||||||
"notYou" | i18n
|
"notYou" | i18n
|
||||||
}}</a>
|
}}</a>
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component";
|
import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component";
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction";
|
import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction";
|
||||||
|
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
|
||||||
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||||
|
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||||
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
@ -16,22 +22,36 @@ import { ValidationService } from "@bitwarden/common/platform/abstractions/valid
|
|||||||
})
|
})
|
||||||
export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent {
|
export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent {
|
||||||
constructor(
|
constructor(
|
||||||
formBuilder: FormBuilder,
|
protected formBuilder: FormBuilder,
|
||||||
devicesService: DevicesServiceAbstraction,
|
protected devicesService: DevicesServiceAbstraction,
|
||||||
stateService: StateService,
|
protected stateService: StateService,
|
||||||
router: Router,
|
protected router: Router,
|
||||||
messagingService: MessagingService,
|
protected activatedRoute: ActivatedRoute,
|
||||||
loginService: LoginService,
|
protected messagingService: MessagingService,
|
||||||
validationService: ValidationService,
|
protected tokenService: TokenService,
|
||||||
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction
|
protected loginService: LoginService,
|
||||||
|
protected organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
|
protected cryptoService: CryptoService,
|
||||||
|
protected organizationUserService: OrganizationUserService,
|
||||||
|
protected apiService: ApiService,
|
||||||
|
protected i18nService: I18nService,
|
||||||
|
protected validationService: ValidationService,
|
||||||
|
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
formBuilder,
|
formBuilder,
|
||||||
devicesService,
|
devicesService,
|
||||||
stateService,
|
stateService,
|
||||||
router,
|
router,
|
||||||
|
activatedRoute,
|
||||||
messagingService,
|
messagingService,
|
||||||
|
tokenService,
|
||||||
loginService,
|
loginService,
|
||||||
|
organizationApiService,
|
||||||
|
cryptoService,
|
||||||
|
organizationUserService,
|
||||||
|
apiService,
|
||||||
|
i18nService,
|
||||||
validationService,
|
validationService,
|
||||||
deviceTrustCryptoService
|
deviceTrustCryptoService
|
||||||
);
|
);
|
||||||
|
@ -8,7 +8,13 @@
|
|||||||
|
|
||||||
<ng-container *ngIf="!loading">
|
<ng-container *ngIf="!loading">
|
||||||
<h1 id="heading">{{ "logInInitiated" | i18n }}</h1>
|
<h1 id="heading">{{ "logInInitiated" | i18n }}</h1>
|
||||||
<h6 id="subHeading" class="standard-bottom-margin">{{ "deviceApprovalRequired" | i18n }}</h6>
|
<h6
|
||||||
|
*ngIf="data.state == State.ExistingUserUntrustedDevice"
|
||||||
|
id="subHeading"
|
||||||
|
class="standard-bottom-margin"
|
||||||
|
>
|
||||||
|
{{ "deviceApprovalRequired" | i18n }}
|
||||||
|
</h6>
|
||||||
|
|
||||||
<form id="rememberDeviceForm" class="standard-bottom-margin" [formGroup]="rememberDeviceForm">
|
<form id="rememberDeviceForm" class="standard-bottom-margin" [formGroup]="rememberDeviceForm">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
@ -25,41 +31,34 @@
|
|||||||
<span id="rememberThisDeviceHintText">{{ "uncheckIfPublicDevice" | i18n }}</span>
|
<span id="rememberThisDeviceHintText">{{ "uncheckIfPublicDevice" | i18n }}</span>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="buttons with-rows">
|
<div *ngIf="data.state == State.ExistingUserUntrustedDevice" class="buttons with-rows">
|
||||||
<div class="buttons-row" *ngIf="showApproveFromOtherDeviceBtn">
|
<div class="buttons-row" *ngIf="data.showApproveFromOtherDeviceBtn">
|
||||||
<button
|
<button (click)="approveFromOtherDevice()" type="button" class="btn primary block">
|
||||||
(click)="approveFromOtherDevice()"
|
|
||||||
type="button"
|
|
||||||
class="btn primary block"
|
|
||||||
[appA11yTitle]="'approveFromYourOtherDevice' | i18n"
|
|
||||||
>
|
|
||||||
{{ "approveFromYourOtherDevice" | i18n }}
|
{{ "approveFromYourOtherDevice" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons-row" *ngIf="showReqAdminApprovalBtn">
|
<div class="buttons-row" *ngIf="data.showReqAdminApprovalBtn">
|
||||||
<button
|
<button (click)="requestAdminApproval()" type="button" class="btn block">
|
||||||
(click)="requestAdminApproval()"
|
|
||||||
type="button"
|
|
||||||
class="btn block"
|
|
||||||
[appA11yTitle]="'requestAdminApproval' | i18n"
|
|
||||||
>
|
|
||||||
{{ "requestAdminApproval" | i18n }}
|
{{ "requestAdminApproval" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons-row" *ngIf="showApproveWithMasterPasswordBtn">
|
<div class="buttons-row" *ngIf="data.showApproveWithMasterPasswordBtn">
|
||||||
<button
|
<button (click)="approveWithMasterPassword()" type="button" class="btn block">
|
||||||
(click)="approveWithMasterPassword()"
|
|
||||||
type="button"
|
|
||||||
class="btn block"
|
|
||||||
[appA11yTitle]="'approveWithMasterPassword' | i18n"
|
|
||||||
>
|
|
||||||
{{ "approveWithMasterPassword" | i18n }}
|
{{ "approveWithMasterPassword" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="data.state == State.NewUser" class="buttons with-rows">
|
||||||
|
<div class="buttons-row">
|
||||||
|
<button (click)="createUser()" type="button" class="btn block">
|
||||||
|
{{ "continue" | i18n }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style="text-align: center">
|
<div style="text-align: center">
|
||||||
<p class="no-margin">{{ "loggingInAs" | i18n }} {{ userEmail }}</p>
|
<p class="no-margin">{{ "loggingInAs" | i18n }} {{ data.userEmail }}</p>
|
||||||
<a [routerLink]="[]" (click)="logOut()">{{ "notYou" | i18n }}</a>
|
<a [routerLink]="[]" (click)="logOut()">{{ "notYou" | i18n }}</a>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component";
|
import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component";
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction";
|
import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction";
|
||||||
|
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
|
||||||
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||||
|
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||||
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
@ -16,22 +22,36 @@ import { ValidationService } from "@bitwarden/common/platform/abstractions/valid
|
|||||||
})
|
})
|
||||||
export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent {
|
export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent {
|
||||||
constructor(
|
constructor(
|
||||||
formBuilder: FormBuilder,
|
protected formBuilder: FormBuilder,
|
||||||
devicesService: DevicesServiceAbstraction,
|
protected devicesService: DevicesServiceAbstraction,
|
||||||
stateService: StateService,
|
protected stateService: StateService,
|
||||||
router: Router,
|
protected router: Router,
|
||||||
messagingService: MessagingService,
|
protected activatedRoute: ActivatedRoute,
|
||||||
loginService: LoginService,
|
protected messagingService: MessagingService,
|
||||||
validationService: ValidationService,
|
protected tokenService: TokenService,
|
||||||
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction
|
protected loginService: LoginService,
|
||||||
|
protected organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
|
protected cryptoService: CryptoService,
|
||||||
|
protected organizationUserService: OrganizationUserService,
|
||||||
|
protected apiService: ApiService,
|
||||||
|
protected i18nService: I18nService,
|
||||||
|
protected validationService: ValidationService,
|
||||||
|
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
formBuilder,
|
formBuilder,
|
||||||
devicesService,
|
devicesService,
|
||||||
stateService,
|
stateService,
|
||||||
router,
|
router,
|
||||||
|
activatedRoute,
|
||||||
messagingService,
|
messagingService,
|
||||||
|
tokenService,
|
||||||
loginService,
|
loginService,
|
||||||
|
organizationApiService,
|
||||||
|
cryptoService,
|
||||||
|
organizationUserService,
|
||||||
|
apiService,
|
||||||
|
i18nService,
|
||||||
validationService,
|
validationService,
|
||||||
deviceTrustCryptoService
|
deviceTrustCryptoService
|
||||||
);
|
);
|
||||||
|
@ -21,9 +21,12 @@
|
|||||||
*ngIf="!loading"
|
*ngIf="!loading"
|
||||||
class="tw-w-full tw-rounded-md tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-6"
|
class="tw-w-full tw-rounded-md tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-6"
|
||||||
>
|
>
|
||||||
|
<ng-container *ngIf="data.state == State.ExistingUserUntrustedDevice">
|
||||||
<h2 bitTypography="h2" class="tw-mb-6">{{ "loginInitiated" | i18n }}</h2>
|
<h2 bitTypography="h2" class="tw-mb-6">{{ "loginInitiated" | i18n }}</h2>
|
||||||
|
|
||||||
<p bitTypography="body1" class="tw-mb-6">{{ "deviceApprovalRequired" | i18n }}</p>
|
<p bitTypography="body1" class="tw-mb-6">
|
||||||
|
{{ "deviceApprovalRequired" | i18n }}
|
||||||
|
</p>
|
||||||
|
|
||||||
<form [formGroup]="rememberDeviceForm">
|
<form [formGroup]="rememberDeviceForm">
|
||||||
<bit-form-control>
|
<bit-form-control>
|
||||||
@ -35,18 +38,18 @@
|
|||||||
|
|
||||||
<div class="tw-mb-6 tw-flex tw-flex-col tw-space-y-3">
|
<div class="tw-mb-6 tw-flex tw-flex-col tw-space-y-3">
|
||||||
<button
|
<button
|
||||||
*ngIf="showApproveFromOtherDeviceBtn"
|
*ngIf="data.showApproveFromOtherDeviceBtn"
|
||||||
(click)="approveFromOtherDevice()"
|
(click)="approveFromOtherDevice()"
|
||||||
bitButton
|
bitButton
|
||||||
type="button"
|
type="button"
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
[block]="true"
|
block
|
||||||
>
|
>
|
||||||
{{ "approveFromYourOtherDevice" | i18n }}
|
{{ "approveFromYourOtherDevice" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
*ngIf="showReqAdminApprovalBtn"
|
*ngIf="data.showReqAdminApprovalBtn"
|
||||||
(click)="requestAdminApproval()"
|
(click)="requestAdminApproval()"
|
||||||
bitButton
|
bitButton
|
||||||
type="button"
|
type="button"
|
||||||
@ -56,20 +59,45 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
*ngIf="showApproveWithMasterPasswordBtn"
|
*ngIf="data.showApproveWithMasterPasswordBtn"
|
||||||
(click)="approveWithMasterPassword()"
|
(click)="approveWithMasterPassword()"
|
||||||
bitButton
|
bitButton
|
||||||
type="button"
|
type="button"
|
||||||
buttonType="secondary"
|
buttonType="secondary"
|
||||||
|
block
|
||||||
>
|
>
|
||||||
{{ "approveWithMasterPassword" | i18n }}
|
{{ "approveWithMasterPassword" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="data.state == State.NewUser">
|
||||||
|
<h2 bitTypography="h2" class="tw-mb-6">{{ "loggedIn" | i18n }}</h2>
|
||||||
|
|
||||||
|
<form [formGroup]="rememberDeviceForm">
|
||||||
|
<bit-form-control>
|
||||||
|
<input type="checkbox" bitCheckbox formControlName="rememberDevice" />
|
||||||
|
<bit-label>{{ "rememberThisDevice" | i18n }} </bit-label>
|
||||||
|
<bit-hint bitTypography="body2">{{ "uncheckIfPublicDevice" | i18n }}</bit-hint>
|
||||||
|
</bit-form-control>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<button
|
||||||
|
bitButton
|
||||||
|
type="button"
|
||||||
|
buttonType="primary"
|
||||||
|
block
|
||||||
|
class="tw-mb-6"
|
||||||
|
[bitAction]="createUser"
|
||||||
|
>
|
||||||
|
{{ "continue" | i18n }}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<hr class="tw-mb-6 tw-mt-0" />
|
<hr class="tw-mb-6 tw-mt-0" />
|
||||||
|
|
||||||
<div class="tw-m-0 tw-text-sm">
|
<div class="tw-m-0 tw-text-sm">
|
||||||
<p class="tw-mb-1">{{ "loggingInAs" | i18n }} {{ userEmail }}</p>
|
<p class="tw-mb-1">{{ "loggingInAs" | i18n }} {{ data.userEmail }}</p>
|
||||||
<a [routerLink]="[]" (click)="logOut()">{{ "notYou" | i18n }}</a>
|
<a [routerLink]="[]" (click)="logOut()">{{ "notYou" | i18n }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component";
|
import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component";
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction";
|
import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction";
|
||||||
|
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
|
||||||
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||||
|
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||||
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
@ -15,22 +21,36 @@ import { ValidationService } from "@bitwarden/common/platform/abstractions/valid
|
|||||||
})
|
})
|
||||||
export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent {
|
export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent {
|
||||||
constructor(
|
constructor(
|
||||||
formBuilder: FormBuilder,
|
protected formBuilder: FormBuilder,
|
||||||
devicesService: DevicesServiceAbstraction,
|
protected devicesService: DevicesServiceAbstraction,
|
||||||
stateService: StateService,
|
protected stateService: StateService,
|
||||||
router: Router,
|
protected router: Router,
|
||||||
messagingService: MessagingService,
|
protected activatedRoute: ActivatedRoute,
|
||||||
loginService: LoginService,
|
protected messagingService: MessagingService,
|
||||||
validationService: ValidationService,
|
protected tokenService: TokenService,
|
||||||
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction
|
protected loginService: LoginService,
|
||||||
|
protected organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
|
protected cryptoService: CryptoService,
|
||||||
|
protected organizationUserService: OrganizationUserService,
|
||||||
|
protected apiService: ApiService,
|
||||||
|
protected i18nService: I18nService,
|
||||||
|
protected validationService: ValidationService,
|
||||||
|
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
formBuilder,
|
formBuilder,
|
||||||
devicesService,
|
devicesService,
|
||||||
stateService,
|
stateService,
|
||||||
router,
|
router,
|
||||||
|
activatedRoute,
|
||||||
messagingService,
|
messagingService,
|
||||||
|
tokenService,
|
||||||
loginService,
|
loginService,
|
||||||
|
organizationApiService,
|
||||||
|
cryptoService,
|
||||||
|
organizationUserService,
|
||||||
|
apiService,
|
||||||
|
i18nService,
|
||||||
validationService,
|
validationService,
|
||||||
deviceTrustCryptoService
|
deviceTrustCryptoService
|
||||||
);
|
);
|
||||||
|
@ -38,10 +38,10 @@ export class SsoComponent extends BaseSsoComponent {
|
|||||||
environmentService: EnvironmentService,
|
environmentService: EnvironmentService,
|
||||||
passwordGenerationService: PasswordGenerationServiceAbstraction,
|
passwordGenerationService: PasswordGenerationServiceAbstraction,
|
||||||
logService: LogService,
|
logService: LogService,
|
||||||
configService: ConfigServiceAbstraction,
|
|
||||||
private orgDomainApiService: OrgDomainApiServiceAbstraction,
|
private orgDomainApiService: OrgDomainApiServiceAbstraction,
|
||||||
private loginService: LoginService,
|
private loginService: LoginService,
|
||||||
private validationService: ValidationService
|
private validationService: ValidationService,
|
||||||
|
configService: ConfigServiceAbstraction
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
authService,
|
authService,
|
||||||
|
@ -1,29 +1,72 @@
|
|||||||
import { Directive, OnDestroy, OnInit } from "@angular/core";
|
import { Directive, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { FormBuilder, FormControl } from "@angular/forms";
|
import { FormBuilder, FormControl } from "@angular/forms";
|
||||||
import { Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { Observable, Subject, catchError, forkJoin, from, of, finalize, takeUntil } from "rxjs";
|
import {
|
||||||
|
firstValueFrom,
|
||||||
|
map,
|
||||||
|
switchMap,
|
||||||
|
Subject,
|
||||||
|
catchError,
|
||||||
|
forkJoin,
|
||||||
|
from,
|
||||||
|
of,
|
||||||
|
finalize,
|
||||||
|
takeUntil,
|
||||||
|
} from "rxjs";
|
||||||
|
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction";
|
import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction";
|
||||||
|
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
|
||||||
|
import { OrganizationUserResetPasswordEnrollmentRequest } from "@bitwarden/common/abstractions/organization-user/requests";
|
||||||
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||||
|
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||||
import {
|
import {
|
||||||
DesktopDeviceTypes,
|
DesktopDeviceTypes,
|
||||||
DeviceType,
|
DeviceType,
|
||||||
MobileDeviceTypes,
|
MobileDeviceTypes,
|
||||||
} from "@bitwarden/common/enums/device-type.enum";
|
} from "@bitwarden/common/enums/device-type.enum";
|
||||||
|
import { KeysRequest } from "@bitwarden/common/models/request/keys.request";
|
||||||
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { AccountDecryptionOptions } from "@bitwarden/common/platform/models/domain/account";
|
import { AccountDecryptionOptions } from "@bitwarden/common/platform/models/domain/account";
|
||||||
|
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||||
|
import { UserKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
NewUser,
|
||||||
|
ExistingUserUntrustedDevice,
|
||||||
|
}
|
||||||
|
|
||||||
|
type NewUserData = {
|
||||||
|
readonly state: State.NewUser;
|
||||||
|
readonly organizationId: string;
|
||||||
|
readonly userEmail: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ExistingUserUntrustedDeviceData = {
|
||||||
|
readonly state: State.ExistingUserUntrustedDevice;
|
||||||
|
readonly showApproveFromOtherDeviceBtn: boolean;
|
||||||
|
readonly showReqAdminApprovalBtn: boolean;
|
||||||
|
readonly showApproveWithMasterPasswordBtn: boolean;
|
||||||
|
readonly userEmail: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Data = NewUserData | ExistingUserUntrustedDeviceData;
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
showApproveFromOtherDeviceBtn: boolean;
|
protected State = State;
|
||||||
showReqAdminApprovalBtn: boolean;
|
|
||||||
showApproveWithMasterPasswordBtn: boolean;
|
protected data?: Data;
|
||||||
userEmail: string;
|
protected loading = true;
|
||||||
|
|
||||||
// Remember device means for the user to trust the device
|
// Remember device means for the user to trust the device
|
||||||
rememberDeviceForm = this.formBuilder.group({
|
rememberDeviceForm = this.formBuilder.group({
|
||||||
@ -34,20 +77,43 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||||||
return this.rememberDeviceForm?.controls.rememberDevice;
|
return this.rememberDeviceForm?.controls.rememberDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
loading = true;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected formBuilder: FormBuilder,
|
protected formBuilder: FormBuilder,
|
||||||
protected devicesService: DevicesServiceAbstraction,
|
protected devicesService: DevicesServiceAbstraction,
|
||||||
protected stateService: StateService,
|
protected stateService: StateService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
|
protected activatedRoute: ActivatedRoute,
|
||||||
protected messagingService: MessagingService,
|
protected messagingService: MessagingService,
|
||||||
|
protected tokenService: TokenService,
|
||||||
protected loginService: LoginService,
|
protected loginService: LoginService,
|
||||||
|
protected organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
|
protected cryptoService: CryptoService,
|
||||||
|
protected organizationUserService: OrganizationUserService,
|
||||||
|
protected apiService: ApiService,
|
||||||
|
protected i18nService: I18nService,
|
||||||
protected validationService: ValidationService,
|
protected validationService: ValidationService,
|
||||||
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction
|
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
async ngOnInit() {
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const accountDecryptionOptions: AccountDecryptionOptions =
|
||||||
|
await this.stateService.getAccountDecryptionOptions();
|
||||||
|
|
||||||
|
if (
|
||||||
|
!accountDecryptionOptions?.trustedDeviceOption?.hasAdminApproval &&
|
||||||
|
!accountDecryptionOptions?.hasMasterPassword
|
||||||
|
) {
|
||||||
|
// We are dealing with a new account if:
|
||||||
|
// - User does not have admin approval (i.e. has not enrolled into admin reset)
|
||||||
|
// - AND does not have a master password
|
||||||
|
this.loadNewUserData();
|
||||||
|
} else {
|
||||||
|
this.loadUntrustedDeviceData(accountDecryptionOptions);
|
||||||
|
}
|
||||||
|
|
||||||
// Note: this is probably not a comprehensive write up of all scenarios:
|
// Note: this is probably not a comprehensive write up of all scenarios:
|
||||||
|
|
||||||
// If the TDE feature flag is enabled and TDE is configured for the org that the user is a member of,
|
// If the TDE feature flag is enabled and TDE is configured for the org that the user is a member of,
|
||||||
@ -66,11 +132,39 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||||||
// - Determine if device is trusted or not via device crypto service (method not yet written)
|
// - Determine if device is trusted or not via device crypto service (method not yet written)
|
||||||
// - If not trusted, present user with login decryption options (approve from other device, approve with master password, request admin approval)
|
// - If not trusted, present user with login decryption options (approve from other device, approve with master password, request admin approval)
|
||||||
// - loadUntrustedDeviceData()
|
// - loadUntrustedDeviceData()
|
||||||
|
} catch (err) {
|
||||||
this.loadUntrustedDeviceData();
|
this.validationService.showError(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadUntrustedDeviceData() {
|
async loadNewUserData() {
|
||||||
|
const autoEnrollStatus$ = this.activatedRoute.queryParamMap.pipe(
|
||||||
|
map((params) => params.get("identifier")),
|
||||||
|
switchMap((identifier) => {
|
||||||
|
if (identifier == null) {
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return from(this.organizationApiService.getAutoEnrollStatus(identifier));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const email$ = from(this.stateService.getEmail()).pipe(
|
||||||
|
catchError((err: unknown) => {
|
||||||
|
this.validationService.showError(err);
|
||||||
|
return of(undefined);
|
||||||
|
}),
|
||||||
|
takeUntil(this.destroy$)
|
||||||
|
);
|
||||||
|
|
||||||
|
const autoEnrollStatus = await firstValueFrom(autoEnrollStatus$);
|
||||||
|
const email = await firstValueFrom(email$);
|
||||||
|
|
||||||
|
this.data = { state: State.NewUser, organizationId: autoEnrollStatus.id, userEmail: email };
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadUntrustedDeviceData(accountDecryptionOptions: AccountDecryptionOptions) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
const mobileAndDesktopDeviceTypes: DeviceType[] = Array.from(MobileDeviceTypes).concat(
|
const mobileAndDesktopDeviceTypes: DeviceType[] = Array.from(MobileDeviceTypes).concat(
|
||||||
@ -90,16 +184,6 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||||||
takeUntil(this.destroy$)
|
takeUntil(this.destroy$)
|
||||||
);
|
);
|
||||||
|
|
||||||
const accountDecryptionOptions$: Observable<AccountDecryptionOptions> = from(
|
|
||||||
this.stateService.getAccountDecryptionOptions()
|
|
||||||
).pipe(
|
|
||||||
catchError((err: unknown) => {
|
|
||||||
this.validationService.showError(err);
|
|
||||||
return of(undefined);
|
|
||||||
}),
|
|
||||||
takeUntil(this.destroy$)
|
|
||||||
);
|
|
||||||
|
|
||||||
const email$ = from(this.stateService.getEmail()).pipe(
|
const email$ = from(this.stateService.getEmail()).pipe(
|
||||||
catchError((err: unknown) => {
|
catchError((err: unknown) => {
|
||||||
this.validationService.showError(err);
|
this.validationService.showError(err);
|
||||||
@ -110,7 +194,6 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
forkJoin({
|
forkJoin({
|
||||||
mobileOrDesktopDevicesExistence: mobileOrDesktopDevicesExistence$,
|
mobileOrDesktopDevicesExistence: mobileOrDesktopDevicesExistence$,
|
||||||
accountDecryptionOptions: accountDecryptionOptions$,
|
|
||||||
email: email$,
|
email: email$,
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
@ -119,16 +202,24 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.subscribe(({ mobileOrDesktopDevicesExistence, accountDecryptionOptions, email }) => {
|
.subscribe(({ mobileOrDesktopDevicesExistence, email }) => {
|
||||||
this.showApproveFromOtherDeviceBtn = mobileOrDesktopDevicesExistence || false;
|
const showApproveFromOtherDeviceBtn = mobileOrDesktopDevicesExistence || false;
|
||||||
|
|
||||||
this.showReqAdminApprovalBtn =
|
const showReqAdminApprovalBtn =
|
||||||
!!accountDecryptionOptions?.trustedDeviceOption?.hasAdminApproval || false;
|
!!accountDecryptionOptions?.trustedDeviceOption?.hasAdminApproval || false;
|
||||||
|
|
||||||
this.showApproveWithMasterPasswordBtn =
|
const showApproveWithMasterPasswordBtn =
|
||||||
accountDecryptionOptions?.hasMasterPassword || false;
|
accountDecryptionOptions?.hasMasterPassword || false;
|
||||||
|
|
||||||
this.userEmail = email;
|
const userEmail = email;
|
||||||
|
|
||||||
|
this.data = {
|
||||||
|
state: State.ExistingUserUntrustedDevice,
|
||||||
|
showApproveFromOtherDeviceBtn,
|
||||||
|
showReqAdminApprovalBtn,
|
||||||
|
showApproveWithMasterPasswordBtn,
|
||||||
|
userEmail,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +227,11 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||||||
// TODO: plan is to re-use existing login-with-device component but rework it to have two flows
|
// TODO: plan is to re-use existing login-with-device component but rework it to have two flows
|
||||||
// (1) Standard flow for unauthN user based on AuthService status
|
// (1) Standard flow for unauthN user based on AuthService status
|
||||||
// (2) New flow for authN user based on AuthService status b/c they have just authenticated w/ SSO
|
// (2) New flow for authN user based on AuthService status b/c they have just authenticated w/ SSO
|
||||||
this.loginService.setEmail(this.userEmail);
|
if (this.data.state !== State.ExistingUserUntrustedDevice) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loginService.setEmail(this.data.userEmail);
|
||||||
this.router.navigate(["/login-with-device"]);
|
this.router.navigate(["/login-with-device"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,6 +257,69 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||||||
this.router.navigate(["/lock"]);
|
this.router.navigate(["/lock"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createUser = async () => {
|
||||||
|
if (this.data.state !== State.NewUser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this.loading to support clients without async-actions-support
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const { userKey, publicKey, privateKey } = await this.cryptoService.initAccount();
|
||||||
|
const keysRequest = new KeysRequest(publicKey, privateKey.encryptedString);
|
||||||
|
await this.apiService.postAccountKeys(keysRequest);
|
||||||
|
|
||||||
|
await this.passwordResetEnroll(userKey, publicKey, privateKey);
|
||||||
|
|
||||||
|
if (this.rememberDeviceForm.value.rememberDevice) {
|
||||||
|
await this.deviceTrustCryptoService.trustDevice();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.validationService.showError(error);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordResetEnroll = async (userKey: UserKey, publicKey: string, privateKey: EncString) => {
|
||||||
|
if (this.data.state !== State.NewUser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this.loading to support clients without async-actions-support
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const orgKeyResponse = await this.organizationApiService.getKeys(this.data.organizationId);
|
||||||
|
if (orgKeyResponse == null) {
|
||||||
|
throw new Error(this.i18nService.t("resetPasswordOrgKeysError"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const orgPublicKey = Utils.fromB64ToArray(orgKeyResponse.publicKey);
|
||||||
|
|
||||||
|
// RSA Encrypt user's userKey.key with organization public key
|
||||||
|
const userId = await this.stateService.getUserId();
|
||||||
|
const encryptedKey = await this.cryptoService.rsaEncrypt(userKey.key, orgPublicKey.buffer);
|
||||||
|
|
||||||
|
const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest();
|
||||||
|
resetRequest.resetPasswordKey = encryptedKey.encryptedString;
|
||||||
|
|
||||||
|
await this.organizationUserService.putOrganizationUserResetPasswordEnrollment(
|
||||||
|
this.data.organizationId,
|
||||||
|
userId,
|
||||||
|
resetRequest
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: On browser this should close the window. But since we might extract
|
||||||
|
// this logic into a service I'm gonna leaves this as-is untill that
|
||||||
|
// refactor is done
|
||||||
|
await this.router.navigate(["/vault"]);
|
||||||
|
} catch (error) {
|
||||||
|
this.validationService.showError(error);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
logOut() {
|
logOut() {
|
||||||
this.loading = true; // to avoid an awkward delay in browser extension
|
this.loading = true; // to avoid an awkward delay in browser extension
|
||||||
this.messagingService.send("logout");
|
this.messagingService.send("logout");
|
||||||
|
@ -37,6 +37,7 @@ export class SsoComponent {
|
|||||||
protected successRoute = "lock";
|
protected successRoute = "lock";
|
||||||
protected trustedDeviceEncRoute = "login-initiated";
|
protected trustedDeviceEncRoute = "login-initiated";
|
||||||
protected changePasswordRoute = "set-password";
|
protected changePasswordRoute = "set-password";
|
||||||
|
protected tdeLogin = "login-initiated";
|
||||||
protected forcePasswordResetRoute = "update-temp-password";
|
protected forcePasswordResetRoute = "update-temp-password";
|
||||||
protected clientId: string;
|
protected clientId: string;
|
||||||
protected redirectUri: string;
|
protected redirectUri: string;
|
||||||
@ -190,6 +191,13 @@ export class SsoComponent {
|
|||||||
this.formPromise = this.authService.logIn(credentials);
|
this.formPromise = this.authService.logIn(credentials);
|
||||||
const response = await this.formPromise;
|
const response = await this.formPromise;
|
||||||
|
|
||||||
|
const trustedDeviceEncryptionFeatureActive = await this.configService.getFeatureFlagBool(
|
||||||
|
FeatureFlag.TrustedDeviceEncryption
|
||||||
|
);
|
||||||
|
|
||||||
|
const accountDecryptionOptions: AccountDecryptionOptions =
|
||||||
|
await this.stateService.getAccountDecryptionOptions();
|
||||||
|
|
||||||
if (response.requiresTwoFactor) {
|
if (response.requiresTwoFactor) {
|
||||||
if (this.onSuccessfulLoginTwoFactorNavigate != null) {
|
if (this.onSuccessfulLoginTwoFactorNavigate != null) {
|
||||||
await this.onSuccessfulLoginTwoFactorNavigate();
|
await this.onSuccessfulLoginTwoFactorNavigate();
|
||||||
@ -201,6 +209,15 @@ export class SsoComponent {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
trustedDeviceEncryptionFeatureActive &&
|
||||||
|
accountDecryptionOptions.trustedDeviceOption !== undefined
|
||||||
|
) {
|
||||||
|
this.router.navigate([this.trustedDeviceEncRoute], {
|
||||||
|
queryParams: {
|
||||||
|
identifier: orgIdFromState,
|
||||||
|
},
|
||||||
|
});
|
||||||
} else if (response.resetMasterPassword) {
|
} else if (response.resetMasterPassword) {
|
||||||
// TODO: for TDE, we are going to deprecate using response.resetMasterPassword
|
// TODO: for TDE, we are going to deprecate using response.resetMasterPassword
|
||||||
// and instead rely on accountDecryptionOptions to determine if the user needs to set a password
|
// and instead rely on accountDecryptionOptions to determine if the user needs to set a password
|
||||||
@ -227,24 +244,10 @@ export class SsoComponent {
|
|||||||
}
|
}
|
||||||
if (this.onSuccessfulLoginNavigate != null) {
|
if (this.onSuccessfulLoginNavigate != null) {
|
||||||
await this.onSuccessfulLoginNavigate();
|
await this.onSuccessfulLoginNavigate();
|
||||||
} else {
|
|
||||||
const trustedDeviceEncryptionFeatureActive = await this.configService.getFeatureFlagBool(
|
|
||||||
FeatureFlag.TrustedDeviceEncryption
|
|
||||||
);
|
|
||||||
|
|
||||||
const accountDecryptionOptions: AccountDecryptionOptions =
|
|
||||||
await this.stateService.getAccountDecryptionOptions();
|
|
||||||
|
|
||||||
if (
|
|
||||||
trustedDeviceEncryptionFeatureActive &&
|
|
||||||
accountDecryptionOptions.trustedDeviceOption !== undefined
|
|
||||||
) {
|
|
||||||
this.router.navigate([this.trustedDeviceEncRoute]);
|
|
||||||
} else {
|
} else {
|
||||||
this.router.navigate([this.successRoute]);
|
this.router.navigate([this.successRoute]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
|
|
||||||
|
@ -336,6 +336,17 @@ export abstract class CryptoService {
|
|||||||
rsaDecrypt: (encValue: string, privateKeyValue?: ArrayBuffer) => Promise<ArrayBuffer>;
|
rsaDecrypt: (encValue: string, privateKeyValue?: ArrayBuffer) => Promise<ArrayBuffer>;
|
||||||
randomNumber: (min: number, max: number) => Promise<number>;
|
randomNumber: (min: number, max: number) => Promise<number>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all necessary crypto keys needed for a new account.
|
||||||
|
* Warning! This completely replaces any existing keys!
|
||||||
|
* @returns The user's newly created public key, private key, and encrypted private key
|
||||||
|
*/
|
||||||
|
initAccount: () => Promise<{
|
||||||
|
userKey: UserKey;
|
||||||
|
publicKey: string;
|
||||||
|
privateKey: EncString;
|
||||||
|
}>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Left for migration purposes. Use decryptUserKeyWithPin instead.
|
* @deprecated Left for migration purposes. Use decryptUserKeyWithPin instead.
|
||||||
*/
|
*/
|
||||||
|
@ -690,6 +690,28 @@ export class CryptoService implements CryptoServiceAbstraction {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all necessary crypto keys needed for a new account.
|
||||||
|
* Warning! This completely replaces any existing keys!
|
||||||
|
*/
|
||||||
|
async initAccount(): Promise<{
|
||||||
|
userKey: UserKey;
|
||||||
|
publicKey: string;
|
||||||
|
privateKey: EncString;
|
||||||
|
}> {
|
||||||
|
const randomBytes = await this.cryptoFunctionService.randomBytes(64);
|
||||||
|
const userKey = new SymmetricCryptoKey(randomBytes) as UserKey;
|
||||||
|
const [publicKey, privateKey] = await this.makeKeyPair(userKey);
|
||||||
|
await this.stateService.setUserKey(userKey);
|
||||||
|
await this.stateService.setEncryptedPrivateKey(privateKey.encryptedString);
|
||||||
|
|
||||||
|
return {
|
||||||
|
userKey,
|
||||||
|
publicKey,
|
||||||
|
privateKey,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regenerates any additional keys if needed. Useful to make sure
|
* Regenerates any additional keys if needed. Useful to make sure
|
||||||
* other keys stay in sync when the user key has been rotated.
|
* other keys stay in sync when the user key has been rotated.
|
||||||
|
@ -582,7 +582,7 @@ export class StateService<
|
|||||||
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||||
);
|
);
|
||||||
|
|
||||||
if (options.userId == this.activeAccountSubject.getValue()) {
|
if (options?.userId == this.activeAccountSubject.getValue()) {
|
||||||
const nextValue = value != null;
|
const nextValue = value != null;
|
||||||
|
|
||||||
// Avoid emitting if we are already unlocked
|
// Avoid emitting if we are already unlocked
|
||||||
|
Loading…
Reference in New Issue
Block a user