1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-10 05:30:01 +00:00

Added access code

This commit is contained in:
Todd Martin
2025-03-22 14:01:39 -04:00
parent b257be6f9f
commit f176b752c3
2 changed files with 78 additions and 62 deletions

View File

@@ -1,57 +1,65 @@
<div class="tw-text-center">
<ng-container *ngIf="flow === Flow.StandardAuthRequest">
<p *ngIf="clientType !== ClientType.Web">
{{ "notificationSentDevicePart1" | i18n }}
<a
bitLink
linkType="primary"
class="tw-cursor-pointer"
[href]="deviceManagementUrl"
target="_blank"
rel="noreferrer"
>{{ "notificationSentDeviceAnchor" | i18n }}</a
>. {{ "notificationSentDevicePart2" | i18n }}
</p>
<p *ngIf="clientType === ClientType.Web">
{{ "notificationSentDeviceComplete" | i18n }}
</p>
<ng-container *ngIf="loading">
<div class="tw-flex tw-items-center tw-justify-center">
<i class="bwi bwi-spinner bwi-spin bwi-3x" aria-hidden="true"></i>
</div>
</ng-container>
<div class="tw-font-semibold">{{ "fingerprintPhraseHeader" | i18n }}</div>
<code class="tw-text-code">{{ fingerprintPhrase }}</code>
<ng-container *ngIf="!loading">
<div class="tw-text-center">
<ng-container *ngIf="flow === Flow.StandardAuthRequest">
<p *ngIf="clientType !== ClientType.Web">
{{ "notificationSentDevicePart1" | i18n }}
<a
bitLink
linkType="primary"
class="tw-cursor-pointer"
[href]="deviceManagementUrl"
target="_blank"
rel="noreferrer"
>{{ "notificationSentDeviceAnchor" | i18n }}</a
>. {{ "notificationSentDevicePart2" | i18n }}
</p>
<p *ngIf="clientType === ClientType.Web">
{{ "notificationSentDeviceComplete" | i18n }}
</p>
<button
*ngIf="showResendNotification"
type="button"
bitButton
block
buttonType="secondary"
class="tw-mt-4"
(click)="startStandardAuthRequestLogin()"
>
{{ "resendNotification" | i18n }}
</button>
<div class="tw-font-semibold">{{ "fingerprintPhraseHeader" | i18n }}</div>
<code class="tw-text-code">{{ fingerprintPhrase }}</code>
<div *ngIf="clientType !== ClientType.Browser" class="tw-mt-4">
<span>{{ "needAnotherOptionV1" | i18n }}</span
>&nbsp;
<a [routerLink]="backToRoute" bitLink linkType="primary">{{
"viewAllLogInOptions" | i18n
}}</a>
</div>
</ng-container>
<button
*ngIf="showResendNotification"
type="button"
bitButton
block
buttonType="secondary"
class="tw-mt-4"
(click)="startStandardAuthRequestLogin()"
>
{{ "resendNotification" | i18n }}
</button>
<ng-container *ngIf="flow === Flow.AdminAuthRequest">
<p>{{ "youWillBeNotifiedOnceTheRequestIsApproved" | i18n }}</p>
<div *ngIf="clientType !== ClientType.Browser" class="tw-mt-4">
<span>{{ "needAnotherOptionV1" | i18n }}</span
>&nbsp;
<a [routerLink]="backToRoute" bitLink linkType="primary">{{
"viewAllLogInOptions" | i18n
}}</a>
</div>
</ng-container>
<div class="tw-font-semibold">{{ "fingerprintPhraseHeader" | i18n }}</div>
<code class="tw-text-code">{{ fingerprintPhrase }}</code>
<ng-container *ngIf="flow === Flow.AdminAuthRequest">
<p>{{ "youWillBeNotifiedOnceTheRequestIsApproved" | i18n }}</p>
<div class="tw-mt-4">
<span>{{ "troubleLoggingIn" | i18n }}</span
>&nbsp;
<a [routerLink]="backToRoute" bitLink linkType="primary">{{
"viewAllLogInOptions" | i18n
}}</a>
</div>
</ng-container>
</div>
<div class="tw-font-semibold">{{ "fingerprintPhraseHeader" | i18n }}</div>
<code class="tw-text-code">{{ fingerprintPhrase }}</code>
<div class="tw-mt-4">
<span>{{ "troubleLoggingIn" | i18n }}</span
>&nbsp;
<a [routerLink]="backToRoute" bitLink linkType="primary">{{
"viewAllLogInOptions" | i18n
}}</a>
</div>
</ng-container>
</div>
</ng-container>

View File

@@ -68,6 +68,7 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
private accessCode: string | undefined = undefined;
private authStatus: AuthenticationStatus | undefined = undefined;
private showResendNotificationTimeoutSeconds = 12;
private loading = true;
protected backToRoute = "/login";
protected clientType: ClientType;
@@ -110,13 +111,14 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
this.authRequestService.authRequestPushNotification$
.pipe(takeUntilDestroyed())
.subscribe((requestId) => {
this.loading = true;
this.processAuthRequestResponse(requestId).catch((e: Error) => {
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("error"),
message: e.message,
});
this.loading = false;
this.logService.error("Failed to use approved auth request: " + e.message);
});
});
@@ -149,6 +151,7 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
} else {
await this.initStandardAuthRequestFlow();
}
this.loading = false;
}
private async initAdminAuthRequestFlow(): Promise<void> {
@@ -199,6 +202,7 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
this.loginViaAuthRequestCacheService.getCachedLoginViaAuthRequestView();
if (cachedAuthRequest) {
this.logService.info("Found cached auth request.");
if (!cachedAuthRequest.id) {
this.logService.error(
"No id on the cached auth request when in the standard auth request flow.",
@@ -286,12 +290,6 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
cachedAuthRequest: LoginViaAuthRequestView,
): Promise<void> {
if (cachedAuthRequest) {
// Grab the cached information and store it back in component state.
// We don't need the public key for handling the authentication request because
// the verifyAndHandleApprovedAuthReq function will receive the public key back
// from the looked up auth request and all we need is to make sure that
// we can use the cached private key that is associated with it.
if (!this.email) {
this.logService.error("Email not defined when handling an existing auth request.");
return;
@@ -302,6 +300,11 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
return;
}
if (!cachedAuthRequest.accessCode) {
this.logService.error("No access code on the cached auth request.");
return;
}
const privateKey = Utils.fromB64ToArray(cachedAuthRequest.privateKey);
// Re-derive the user's fingerprint phrase
@@ -314,12 +317,16 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
derivedPublicKeyArrayBuffer,
);
// Request still pending response from admin set keypair and create hub connection
// so that any approvals will be received via push notification
// We don't need the public key for handling the authentication request because
// the processAuthRequestResponse function will receive the public key back
// from the looked up auth request, and all we need is to make sure that
// we can use the cached private key that is associated with it.
this.authRequestKeyPair = {
privateKey: privateKey,
publicKey: undefined,
};
this.accessCode = cachedAuthRequest.accessCode;
}
}
@@ -517,13 +524,14 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
} catch (error) {
// If the request no longer exists, we treat it as if it's been answered (and denied).
if (error instanceof ErrorResponse && error.statusCode === HttpStatusCode.NotFound) {
authRequestResponse = null;
} else {
this.logService.error(error.message);
throw new Error(error.message);
}
}
if (authRequestResponse === undefined) {
throw new Error("Auth reqeust response not generated");
throw new Error("Auth request response not generated");
}
return authRequestResponse;