mirror of
https://github.com/bitwarden/browser
synced 2026-02-07 12:13:45 +00:00
* [PM-29918] Implement new Send auth flows * [PM-29918] Fix types * Trigger Claude code review * [PM-29918] Address PR review comments * [PM-29918] Remove duplicate AuthType const
137 lines
4.8 KiB
TypeScript
137 lines
4.8 KiB
TypeScript
import {
|
|
ChangeDetectionStrategy,
|
|
Component,
|
|
computed,
|
|
input,
|
|
OnInit,
|
|
output,
|
|
signal,
|
|
} from "@angular/core";
|
|
|
|
import { SendAccessToken } from "@bitwarden/common/auth/send-access";
|
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
|
import { SendAccess } from "@bitwarden/common/tools/send/models/domain/send-access";
|
|
import { SendAccessRequest } from "@bitwarden/common/tools/send/models/request/send-access.request";
|
|
import { SendAccessResponse } from "@bitwarden/common/tools/send/models/response/send-access.response";
|
|
import { SendAccessView } from "@bitwarden/common/tools/send/models/view/send-access.view";
|
|
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
|
import { SendType } from "@bitwarden/common/tools/send/types/send-type";
|
|
import { AnonLayoutWrapperDataService, ToastService } from "@bitwarden/components";
|
|
import { KeyService } from "@bitwarden/key-management";
|
|
|
|
import { SharedModule } from "../../../shared";
|
|
|
|
import { SendAccessFileComponent } from "./send-access-file.component";
|
|
import { SendAccessTextComponent } from "./send-access-text.component";
|
|
|
|
@Component({
|
|
selector: "app-send-view",
|
|
templateUrl: "send-view.component.html",
|
|
imports: [SendAccessFileComponent, SendAccessTextComponent, SharedModule],
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
})
|
|
export class SendViewComponent implements OnInit {
|
|
readonly id = input.required<string>();
|
|
readonly key = input.required<string>();
|
|
readonly accessToken = input<SendAccessToken | null>(null);
|
|
readonly sendResponse = input<SendAccessResponse | null>(null);
|
|
readonly accessRequest = input<SendAccessRequest>(new SendAccessRequest());
|
|
|
|
authRequired = output<void>();
|
|
|
|
readonly send = signal<SendAccessView | null>(null);
|
|
readonly expirationDate = computed<Date | null>(() => this.send()?.expirationDate ?? null);
|
|
readonly creatorIdentifier = computed<string | null>(
|
|
() => this.send()?.creatorIdentifier ?? null,
|
|
);
|
|
readonly hideEmail = computed<boolean>(
|
|
() => this.send() != null && this.creatorIdentifier() == null,
|
|
);
|
|
readonly loading = signal<boolean>(false);
|
|
readonly unavailable = signal<boolean>(false);
|
|
readonly error = signal<boolean>(false);
|
|
|
|
sendType = SendType;
|
|
decKey!: SymmetricCryptoKey;
|
|
|
|
constructor(
|
|
private keyService: KeyService,
|
|
private sendApiService: SendApiService,
|
|
private toastService: ToastService,
|
|
private i18nService: I18nService,
|
|
private layoutWrapperDataService: AnonLayoutWrapperDataService,
|
|
private configService: ConfigService,
|
|
) {}
|
|
|
|
ngOnInit() {
|
|
void this.load();
|
|
}
|
|
|
|
private async load() {
|
|
this.loading.set(true);
|
|
this.unavailable.set(false);
|
|
this.error.set(false);
|
|
|
|
try {
|
|
const sendEmailOtp = await this.configService.getFeatureFlag(FeatureFlag.SendEmailOTP);
|
|
let response: SendAccessResponse;
|
|
if (sendEmailOtp) {
|
|
const accessToken = this.accessToken();
|
|
if (!accessToken) {
|
|
this.authRequired.emit();
|
|
return;
|
|
}
|
|
response = await this.sendApiService.postSendAccessV2(accessToken);
|
|
} else {
|
|
const sendResponse = this.sendResponse();
|
|
if (!sendResponse) {
|
|
this.authRequired.emit();
|
|
return;
|
|
}
|
|
response = sendResponse;
|
|
}
|
|
const keyArray = Utils.fromUrlB64ToArray(this.key());
|
|
const sendAccess = new SendAccess(response);
|
|
this.decKey = await this.keyService.makeSendKey(keyArray);
|
|
const decSend = await sendAccess.decrypt(this.decKey);
|
|
this.send.set(decSend);
|
|
} catch (e) {
|
|
this.send.set(null);
|
|
if (e instanceof ErrorResponse) {
|
|
if (e.statusCode === 401) {
|
|
this.authRequired.emit();
|
|
} else if (e.statusCode === 404) {
|
|
this.unavailable.set(true);
|
|
} else if (e.statusCode === 400) {
|
|
this.toastService.showToast({
|
|
variant: "error",
|
|
title: this.i18nService.t("errorOccurred"),
|
|
message: e.message,
|
|
});
|
|
} else {
|
|
this.error.set(true);
|
|
}
|
|
} else {
|
|
this.error.set(true);
|
|
}
|
|
} finally {
|
|
this.loading.set(false);
|
|
}
|
|
|
|
const creatorIdentifier = this.creatorIdentifier();
|
|
if (creatorIdentifier != null) {
|
|
this.layoutWrapperDataService.setAnonLayoutWrapperData({
|
|
pageSubtitle: {
|
|
key: "sendAccessCreatorIdentifier",
|
|
placeholders: [creatorIdentifier],
|
|
},
|
|
});
|
|
}
|
|
}
|
|
}
|