mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-24 21:41:33 +01:00
[AC-1416] Expose Organization Fingerprint (#5557)
* refactor: change getFingerprint param to fingerprintMaterial, refs PM-1522 * feat: generate and show fingerprint for organization (WIP), refs AC-1416 * feat: update legacy params subscription to best practice (WIP), refs AC-1461 * refactor: update to use reactive forms, refs AC-1416 * refactor: remove boostrap specific classes and update to component library paradigms, refs AC-1416 * refactor: remove boostrap specific classes and update to component library paradigms, refs AC-1416 * refactor: create shared fingerprint component to redude boilerplate for settings fingerprint views, refs AC-1416 * refactor: use grid to emulate col-6 and remove unnecessary theme extensions, refs AC-1416 * refactor: remove negative margin and clean up extra divs, refs AC-1416 * [AC-1431] Add missing UserVerificationModule import (#5555) * [PM-2238] Add nord and solarize themes (#5491) * Fix simple configurable dialog stories (#5560) * chore(deps): update bitwarden/gh-actions digest to 72594be (#5523) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor: remove extra div leftover from card-body class, refs AC-1416 * refactor: use bitTypography for headers, refs AC-1416 * fix: update crypto service abstraction path, refs AC-1416 * refactor: remove try/catch on handler, remove bootstrap class, update api chaining in observable, refs AC-1416 * fix: replace faulty combineLatest logic, refs AC-1416 * refactor: simplify observable logic again, refs AC-1416 --------- Co-authored-by: Shane Melton <smelton@bitwarden.com> Co-authored-by: Oscar Hinton <Hinton@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This commit is contained in:
parent
0afbd90a2d
commit
5cd51374d7
@ -1,95 +1,66 @@
|
||||
<div class="page-header">
|
||||
<h1>{{ "organizationInfo" | i18n }}</h1>
|
||||
</div>
|
||||
<h1 bitTypography="h1" class="tw-pb-2.5">{{ "organizationInfo" | i18n }}</h1>
|
||||
<div *ngIf="loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||
</div>
|
||||
<form
|
||||
*ngIf="org && !loading"
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
ngNativeValidate
|
||||
>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="form-group">
|
||||
<label for="name">{{ "organizationName" | i18n }}</label>
|
||||
<input
|
||||
id="name"
|
||||
class="form-control"
|
||||
type="text"
|
||||
name="Name"
|
||||
[(ngModel)]="org.name"
|
||||
[disabled]="selfHosted"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="billingEmail">{{ "billingEmail" | i18n }}</label>
|
||||
<input
|
||||
id="billingEmail"
|
||||
class="form-control"
|
||||
type="text"
|
||||
name="BillingEmail"
|
||||
[(ngModel)]="org.billingEmail"
|
||||
[disabled]="selfHosted || !canEditSubscription"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="businessName">{{ "businessName" | i18n }}</label>
|
||||
<input
|
||||
id="businessName"
|
||||
class="form-control"
|
||||
type="text"
|
||||
name="BusinessName"
|
||||
[(ngModel)]="org.businessName"
|
||||
[disabled]="selfHosted || !canEditSubscription"
|
||||
/>
|
||||
</div>
|
||||
<form *ngIf="org && !loading" #form [bitSubmit]="submit" [formGroup]="formGroup">
|
||||
<div class="tw-grid tw-grid-cols-2 tw-gap-5">
|
||||
<div>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "organizationName" | i18n }}</bit-label>
|
||||
<input bitInput id="orgName" type="text" formControlName="orgName" />
|
||||
</bit-form-field>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "billingEmail" | i18n }}</bit-label>
|
||||
<input bitInput id="billingEmail" formControlName="billingEmail" type="email" />
|
||||
</bit-form-field>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "businessName" | i18n }}</bit-label>
|
||||
<input bitInput id="businessName" formControlName="businessName" type="text" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div>
|
||||
<bit-avatar [text]="org.name" [id]="org.id" size="large"></bit-avatar>
|
||||
<app-account-fingerprint
|
||||
[fingerprintMaterial]="organizationId"
|
||||
[publicKeyBuffer]="publicKeyBuffer"
|
||||
fingerprintLabel="{{ 'yourOrganizationsFingerprint' | i18n }}"
|
||||
>
|
||||
</app-account-fingerprint>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" buttonType="primary" bitButton [loading]="form.loading">
|
||||
<button type="submit" bitButton bitFormButton buttonType="primary">
|
||||
{{ "save" | i18n }}
|
||||
</button>
|
||||
</form>
|
||||
<ng-container *ngIf="canUseApi">
|
||||
<div class="secondary-header border-0 mb-0">
|
||||
<h1>{{ "apiKey" | i18n }}</h1>
|
||||
</div>
|
||||
<h1 bitTypography="h1" class="tw-mt-16 tw-pb-2.5">{{ "apiKey" | i18n }}</h1>
|
||||
<p>
|
||||
{{ "apiKeyDesc" | i18n }}
|
||||
<a href="https://docs.bitwarden.com" target="_blank" rel="noopener">
|
||||
{{ "learnMore" | i18n }}
|
||||
</a>
|
||||
</p>
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="viewApiKey()">
|
||||
<button type="button" bitButton buttonType="secondary" (click)="viewApiKey()">
|
||||
{{ "viewApiKey" | i18n }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="rotateApiKey()">
|
||||
<button type="button" bitButton buttonType="secondary" (click)="rotateApiKey()">
|
||||
{{ "rotateApiKey" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
<div class="secondary-header text-danger border-0 mb-0">
|
||||
<h1>{{ "dangerZone" | i18n }}</h1>
|
||||
</div>
|
||||
<div class="card border-danger">
|
||||
<div class="card-body">
|
||||
<p>{{ "dangerZoneDesc" | i18n }}</p>
|
||||
<button type="button" class="btn btn-outline-danger" (click)="deleteOrganization()">
|
||||
{{ "deleteOrganization" | i18n }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-danger" (click)="purgeVault()">
|
||||
{{ "purgeVault" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<h1 bitTypography="h1" class="tw-mt-16 tw-pb-2.5 !tw-text-danger">{{ "dangerZone" | i18n }}</h1>
|
||||
<div class="tw-rounded tw-border tw-border-solid tw-border-danger-500 tw-bg-background tw-p-5">
|
||||
<p>{{ "dangerZoneDesc" | i18n }}</p>
|
||||
<button type="button" bitButton buttonType="danger" (click)="deleteOrganization()">
|
||||
{{ "deleteOrganization" | i18n }}
|
||||
</button>
|
||||
<button type="button" bitButton buttonType="danger" (click)="purgeVault()">
|
||||
{{ "purgeVault" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<ng-template #purgeOrganizationTemplate></ng-template>
|
||||
<ng-template #apiKeyTemplate></ng-template>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { FormBuilder, Validators } from "@angular/forms";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { lastValueFrom } from "rxjs";
|
||||
import { combineLatest, lastValueFrom, Subject, switchMap, takeUntil, from } from "rxjs";
|
||||
|
||||
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
@ -13,6 +14,7 @@ import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.se
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
|
||||
import { ApiKeyComponent } from "../../../settings/api-key.component";
|
||||
import { PurgeVaultComponent } from "../../../settings/purge-vault.component";
|
||||
@ -23,7 +25,6 @@ import { DeleteOrganizationDialogResult, openDeleteOrganizationDialog } from "./
|
||||
selector: "app-org-account",
|
||||
templateUrl: "account.component.html",
|
||||
})
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
export class AccountComponent {
|
||||
@ViewChild("purgeOrganizationTemplate", { read: ViewContainerRef, static: true })
|
||||
purgeModalRef: ViewContainerRef;
|
||||
@ -40,7 +41,29 @@ export class AccountComponent {
|
||||
formPromise: Promise<OrganizationResponse>;
|
||||
taxFormPromise: Promise<unknown>;
|
||||
|
||||
private organizationId: string;
|
||||
// FormGroup validators taken from server Organization domain object
|
||||
protected formGroup = this.formBuilder.group({
|
||||
orgName: this.formBuilder.control(
|
||||
{ value: "", disabled: true },
|
||||
{
|
||||
validators: [Validators.required, Validators.maxLength(50)],
|
||||
updateOn: "change",
|
||||
}
|
||||
),
|
||||
billingEmail: this.formBuilder.control(
|
||||
{ value: "", disabled: true },
|
||||
{ validators: [Validators.required, Validators.email, Validators.maxLength(256)] }
|
||||
),
|
||||
businessName: this.formBuilder.control(
|
||||
{ value: "", disabled: true },
|
||||
{ validators: [Validators.maxLength(50)] }
|
||||
),
|
||||
});
|
||||
|
||||
protected organizationId: string;
|
||||
protected publicKeyBuffer: ArrayBuffer;
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private modalService: ModalService,
|
||||
@ -52,53 +75,88 @@ export class AccountComponent {
|
||||
private router: Router,
|
||||
private organizationService: OrganizationService,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private dialogService: DialogServiceAbstraction
|
||||
private dialogService: DialogServiceAbstraction,
|
||||
private formBuilder: FormBuilder
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.selfHosted = this.platformUtilsService.isSelfHost();
|
||||
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
this.route.parent.parent.params.subscribe(async (params) => {
|
||||
this.organizationId = params.organizationId;
|
||||
this.canEditSubscription = this.organizationService.get(
|
||||
this.organizationId
|
||||
).canEditSubscription;
|
||||
try {
|
||||
this.org = await this.organizationApiService.get(this.organizationId);
|
||||
this.canUseApi = this.org.useApi;
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
});
|
||||
this.loading = false;
|
||||
this.route.parent.parent.params
|
||||
.pipe(
|
||||
switchMap((params) => {
|
||||
return combineLatest([
|
||||
// Organization domain
|
||||
this.organizationService.get$(params.organizationId),
|
||||
// OrganizationResponse for form population
|
||||
from(this.organizationApiService.get(params.organizationId)),
|
||||
// Organization Public Key
|
||||
from(this.organizationApiService.getKeys(params.organizationId)),
|
||||
]);
|
||||
}),
|
||||
takeUntil(this.destroy$)
|
||||
)
|
||||
.subscribe(([organization, orgResponse, orgKeys]) => {
|
||||
// Set domain level organization variables
|
||||
this.organizationId = organization.id;
|
||||
this.canEditSubscription = organization.canEditSubscription;
|
||||
this.canUseApi = organization.useApi;
|
||||
|
||||
// Org Response
|
||||
this.org = orgResponse;
|
||||
|
||||
// Public Key Buffer for Org Fingerprint Generation
|
||||
this.publicKeyBuffer = Utils.fromB64ToArray(orgKeys?.publicKey)?.buffer;
|
||||
|
||||
// Patch existing values
|
||||
this.formGroup.patchValue({
|
||||
orgName: this.org.name,
|
||||
billingEmail: this.org.billingEmail,
|
||||
businessName: this.org.businessName,
|
||||
});
|
||||
|
||||
// Update disabled states - reactive forms prefers not using disabled attribute
|
||||
if (!this.selfHosted) {
|
||||
this.formGroup.get("orgName").enable();
|
||||
}
|
||||
|
||||
if (!this.selfHosted || this.canEditSubscription) {
|
||||
this.formGroup.get("billingEmail").enable();
|
||||
this.formGroup.get("businessName").enable();
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
async submit() {
|
||||
try {
|
||||
const request = new OrganizationUpdateRequest();
|
||||
request.name = this.org.name;
|
||||
request.businessName = this.org.businessName;
|
||||
request.billingEmail = this.org.billingEmail;
|
||||
ngOnDestroy(): void {
|
||||
// You must first call .next() in order for the notifier to properly close subscriptions using takeUntil
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
// Backfill pub/priv key if necessary
|
||||
if (!this.org.hasPublicAndPrivateKeys) {
|
||||
const orgShareKey = await this.cryptoService.getOrgKey(this.organizationId);
|
||||
const orgKeys = await this.cryptoService.makeKeyPair(orgShareKey);
|
||||
request.keys = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString);
|
||||
}
|
||||
|
||||
this.formPromise = this.organizationApiService.save(this.organizationId, request);
|
||||
await this.formPromise;
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
null,
|
||||
this.i18nService.t("organizationUpdated")
|
||||
);
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
submit = async () => {
|
||||
this.formGroup.markAllAsTouched();
|
||||
if (this.formGroup.invalid) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const request = new OrganizationUpdateRequest();
|
||||
request.name = this.formGroup.value.orgName;
|
||||
request.businessName = this.formGroup.value.businessName;
|
||||
request.billingEmail = this.formGroup.value.billingEmail;
|
||||
|
||||
// Backfill pub/priv key if necessary
|
||||
if (!this.org.hasPublicAndPrivateKeys) {
|
||||
const orgShareKey = await this.cryptoService.getOrgKey(this.organizationId);
|
||||
const orgKeys = await this.cryptoService.makeKeyPair(orgShareKey);
|
||||
request.keys = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString);
|
||||
}
|
||||
|
||||
this.formPromise = this.organizationApiService.save(this.organizationId, request);
|
||||
await this.formPromise;
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("organizationUpdated"));
|
||||
};
|
||||
|
||||
async deleteOrganization() {
|
||||
const dialog = openDeleteOrganizationDialog(this.dialogService, {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { LooseComponentsModule, SharedModule } from "../../../shared";
|
||||
import { AccountFingerprintComponent } from "../../../shared/components/account-fingerprint/account-fingerprint.component";
|
||||
import { PoliciesModule } from "../../organizations/policies";
|
||||
|
||||
import { AccountComponent } from "./account.component";
|
||||
@ -9,7 +10,13 @@ import { SettingsComponent } from "./settings.component";
|
||||
import { TwoFactorSetupComponent } from "./two-factor-setup.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [SharedModule, LooseComponentsModule, PoliciesModule, OrganizationSettingsRoutingModule],
|
||||
imports: [
|
||||
SharedModule,
|
||||
LooseComponentsModule,
|
||||
PoliciesModule,
|
||||
OrganizationSettingsRoutingModule,
|
||||
AccountFingerprintComponent,
|
||||
],
|
||||
declarations: [SettingsComponent, AccountComponent, TwoFactorSetupComponent],
|
||||
})
|
||||
export class OrganizationSettingsModule {}
|
||||
|
@ -46,19 +46,11 @@
|
||||
Customize
|
||||
</button>
|
||||
</div>
|
||||
<hr />
|
||||
<p *ngIf="fingerprint">
|
||||
{{ "yourAccountsFingerprint" | i18n }}:
|
||||
<a
|
||||
href="https://bitwarden.com/help/fingerprint-phrase/"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i></a
|
||||
><br />
|
||||
<code>{{ fingerprint }}</code>
|
||||
</p>
|
||||
<app-account-fingerprint
|
||||
[fingerprintMaterial]="fingerprintMaterial"
|
||||
fingerprintLabel="{{ 'yourAccountsFingerprint' | i18n }}"
|
||||
>
|
||||
</app-account-fingerprint>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
||||
|
@ -3,10 +3,8 @@ import { Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||
import { UpdateProfileRequest } from "@bitwarden/common/auth/models/request/update-profile.request";
|
||||
import { ProfileResponse } from "@bitwarden/common/models/response/profile.response";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@ -21,7 +19,7 @@ import { ChangeAvatarComponent } from "./change-avatar.component";
|
||||
export class ProfileComponent implements OnInit, OnDestroy {
|
||||
loading = true;
|
||||
profile: ProfileResponse;
|
||||
fingerprint: string;
|
||||
fingerprintMaterial: string;
|
||||
|
||||
formPromise: Promise<any>;
|
||||
@ViewChild("avatarModalTemplate", { read: ViewContainerRef, static: true })
|
||||
@ -32,9 +30,7 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
||||
private apiService: ApiService,
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private cryptoService: CryptoService,
|
||||
private logService: LogService,
|
||||
private keyConnectorService: KeyConnectorService,
|
||||
private stateService: StateService,
|
||||
private modalService: ModalService
|
||||
) {}
|
||||
@ -42,12 +38,7 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
||||
async ngOnInit() {
|
||||
this.profile = await this.apiService.getProfile();
|
||||
this.loading = false;
|
||||
const fingerprint = await this.cryptoService.getFingerprint(
|
||||
await this.stateService.getUserId()
|
||||
);
|
||||
if (fingerprint != null) {
|
||||
this.fingerprint = fingerprint.join("-");
|
||||
}
|
||||
this.fingerprintMaterial = await this.stateService.getUserId();
|
||||
}
|
||||
|
||||
async ngOnDestroy() {
|
||||
|
@ -0,0 +1,16 @@
|
||||
<ng-container>
|
||||
<hr />
|
||||
<p *ngIf="fingerprint">
|
||||
{{ fingerprintLabel }}:
|
||||
<a
|
||||
href="https://bitwarden.com/help/fingerprint-phrase/"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i
|
||||
></a>
|
||||
<br />
|
||||
<code class="tw-text-code">{{ fingerprint }}</code>
|
||||
</p>
|
||||
</ng-container>
|
@ -0,0 +1,30 @@
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
|
||||
import { SharedModule } from "../../shared.module";
|
||||
|
||||
@Component({
|
||||
selector: "app-account-fingerprint",
|
||||
templateUrl: "account-fingerprint.component.html",
|
||||
standalone: true,
|
||||
imports: [SharedModule],
|
||||
})
|
||||
export class AccountFingerprintComponent implements OnInit {
|
||||
@Input() fingerprintMaterial: string;
|
||||
@Input() publicKeyBuffer: ArrayBuffer;
|
||||
@Input() fingerprintLabel: string;
|
||||
|
||||
protected fingerprint: string;
|
||||
|
||||
constructor(private cryptoService: CryptoService) {}
|
||||
|
||||
async ngOnInit() {
|
||||
// TODO - In the future, remove this code and use the fingerprint pipe once merged
|
||||
const generatedFingerprint = await this.cryptoService.getFingerprint(
|
||||
this.fingerprintMaterial,
|
||||
this.publicKeyBuffer
|
||||
);
|
||||
this.fingerprint = generatedFingerprint?.join("-") ?? null;
|
||||
}
|
||||
}
|
@ -107,6 +107,7 @@ import { AddEditComponent as OrgAddEditComponent } from "../vault/org-vault/add-
|
||||
import { AttachmentsComponent as OrgAttachmentsComponent } from "../vault/org-vault/attachments.component";
|
||||
import { CollectionsComponent as OrgCollectionsComponent } from "../vault/org-vault/collections.component";
|
||||
|
||||
import { AccountFingerprintComponent } from "./components/account-fingerprint/account-fingerprint.component";
|
||||
import { UserVerificationModule } from "./components/user-verification";
|
||||
import { SharedModule } from "./shared.module";
|
||||
|
||||
@ -121,6 +122,7 @@ import { SharedModule } from "./shared.module";
|
||||
UserVerificationModule,
|
||||
ChangeKdfModule,
|
||||
DynamicAvatarComponent,
|
||||
AccountFingerprintComponent,
|
||||
],
|
||||
declarations: [
|
||||
PremiumBadgeComponent,
|
||||
|
@ -6845,6 +6845,10 @@
|
||||
"updatedTempPassword": {
|
||||
"message": "User updated a password issued through account recovery."
|
||||
},
|
||||
"yourOrganizationsFingerprint": {
|
||||
"message": "Your organization's fingerprint phrase",
|
||||
"description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their organization's public key with another user, for the purposes of sharing."
|
||||
},
|
||||
"deviceApprovals": {
|
||||
"message": "Device approvals"
|
||||
},
|
||||
|
@ -24,7 +24,7 @@ export abstract class CryptoService {
|
||||
getEncKey: (key?: SymmetricCryptoKey) => Promise<SymmetricCryptoKey>;
|
||||
getPublicKey: () => Promise<ArrayBuffer>;
|
||||
getPrivateKey: () => Promise<ArrayBuffer>;
|
||||
getFingerprint: (userId: string, publicKey?: ArrayBuffer) => Promise<string[]>;
|
||||
getFingerprint: (fingerprintMaterial: string, publicKey?: ArrayBuffer) => Promise<string[]>;
|
||||
getOrgKeys: () => Promise<Map<string, SymmetricCryptoKey>>;
|
||||
getOrgKey: (orgId: string) => Promise<SymmetricCryptoKey>;
|
||||
getProviderKey: (providerId: string) => Promise<SymmetricCryptoKey>;
|
||||
|
@ -204,7 +204,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
async getFingerprint(userId: string, publicKey?: ArrayBuffer): Promise<string[]> {
|
||||
async getFingerprint(fingerprintMaterial: string, publicKey?: ArrayBuffer): Promise<string[]> {
|
||||
if (publicKey == null) {
|
||||
publicKey = await this.getPublicKey();
|
||||
}
|
||||
@ -214,7 +214,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
const keyFingerprint = await this.cryptoFunctionService.hash(publicKey, "sha256");
|
||||
const userFingerprint = await this.cryptoFunctionService.hkdfExpand(
|
||||
keyFingerprint,
|
||||
userId,
|
||||
fingerprintMaterial,
|
||||
32,
|
||||
"sha256"
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user