mirror of
https://github.com/bitwarden/browser
synced 2025-12-17 16:53:34 +00:00
Refactored fido2 popup to use auth guard when routing to component, added BrowserRouterService to track previous page and route using that
This commit is contained in:
35
apps/browser/src/auth/guards/fido2-auth.guard.ts
Normal file
35
apps/browser/src/auth/guards/fido2-auth.guard.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { inject } from "@angular/core";
|
||||||
|
import {
|
||||||
|
ActivatedRouteSnapshot,
|
||||||
|
CanActivateFn,
|
||||||
|
Router,
|
||||||
|
RouterStateSnapshot,
|
||||||
|
} from "@angular/router";
|
||||||
|
|
||||||
|
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
|
|
||||||
|
import { BrowserRouterService } from "../../platform/popup/services/browser-router.service";
|
||||||
|
|
||||||
|
export const fido2AuthGuard: CanActivateFn = async (
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot
|
||||||
|
) => {
|
||||||
|
const routerService = inject(BrowserRouterService);
|
||||||
|
const authService = inject(AuthService);
|
||||||
|
const router = inject(Router);
|
||||||
|
|
||||||
|
const authStatus = await authService.getAuthStatus();
|
||||||
|
|
||||||
|
if (authStatus === AuthenticationStatus.LoggedOut) {
|
||||||
|
await routerService.setPreviousUrl(state.url);
|
||||||
|
return router.createUrlTree(["/home"], { queryParams: route.queryParams });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authStatus === AuthenticationStatus.Locked) {
|
||||||
|
await routerService.setPreviousUrl(state.url);
|
||||||
|
return router.createUrlTree(["/lock"], { queryParams: route.queryParams });
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { ActivatedRoute, ActivatedRouteSnapshot, NavigationEnd, Router } from "@angular/router";
|
||||||
|
import { filter } from "rxjs";
|
||||||
|
|
||||||
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: "root",
|
||||||
|
})
|
||||||
|
export class BrowserRouterService {
|
||||||
|
constructor(router: Router, private stateService: StateService) {
|
||||||
|
router.events
|
||||||
|
.pipe(filter((e) => e instanceof NavigationEnd))
|
||||||
|
.subscribe((event: NavigationEnd) => {
|
||||||
|
const state: ActivatedRouteSnapshot = router.routerState.snapshot.root;
|
||||||
|
|
||||||
|
let child = state.firstChild;
|
||||||
|
while (child.firstChild) {
|
||||||
|
child = child.firstChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateUrl = !child?.data?.doNotSaveUrl ?? true;
|
||||||
|
|
||||||
|
if (updateUrl) {
|
||||||
|
this.setPreviousUrl(event.url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPreviousUrl() {
|
||||||
|
return this.stateService.getPreviousUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check validity of previous url
|
||||||
|
async hasPreviousUrl() {
|
||||||
|
return (await this.getPreviousUrl()) != "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
async setPreviousUrl(url: string) {
|
||||||
|
await this.stateService.setPreviousUrl(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getDeepestChild(activatedRoute: ActivatedRoute): ActivatedRoute {
|
||||||
|
let child = activatedRoute;
|
||||||
|
while (child.firstChild) {
|
||||||
|
child = child.firstChild;
|
||||||
|
}
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
import { canAccessFeature } from "@bitwarden/angular/guard/feature-flag.guard";
|
import { canAccessFeature } from "@bitwarden/angular/guard/feature-flag.guard";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
|
||||||
|
import { fido2AuthGuard } from "../auth/guards/fido2-auth.guard";
|
||||||
import { EnvironmentComponent } from "../auth/popup/environment.component";
|
import { EnvironmentComponent } from "../auth/popup/environment.component";
|
||||||
import { HintComponent } from "../auth/popup/hint.component";
|
import { HintComponent } from "../auth/popup/hint.component";
|
||||||
import { HomeComponent } from "../auth/popup/home.component";
|
import { HomeComponent } from "../auth/popup/home.component";
|
||||||
@@ -72,17 +73,19 @@ const routes: Routes = [
|
|||||||
path: "home",
|
path: "home",
|
||||||
component: HomeComponent,
|
component: HomeComponent,
|
||||||
canActivate: [UnauthGuard],
|
canActivate: [UnauthGuard],
|
||||||
data: { state: "home" },
|
data: { state: "home", doNotSaveUrl: true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "fido2",
|
path: "fido2",
|
||||||
component: Fido2Component,
|
component: Fido2Component,
|
||||||
|
canActivate: [fido2AuthGuard],
|
||||||
|
data: { state: "fido2" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "login",
|
path: "login",
|
||||||
component: LoginComponent,
|
component: LoginComponent,
|
||||||
canActivate: [UnauthGuard],
|
canActivate: [UnauthGuard],
|
||||||
data: { state: "login" },
|
data: { state: "login", doNotSaveUrl: true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "login-with-device",
|
path: "login-with-device",
|
||||||
@@ -100,13 +103,13 @@ const routes: Routes = [
|
|||||||
path: "lock",
|
path: "lock",
|
||||||
component: LockComponent,
|
component: LockComponent,
|
||||||
canActivate: [lockGuard()],
|
canActivate: [lockGuard()],
|
||||||
data: { state: "lock" },
|
data: { state: "lock", doNotSaveUrl: true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "2fa",
|
path: "2fa",
|
||||||
component: TwoFactorComponent,
|
component: TwoFactorComponent,
|
||||||
canActivate: [UnauthGuard],
|
canActivate: [UnauthGuard],
|
||||||
data: { state: "2fa" },
|
data: { state: "2fa", doNotSaveUrl: true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "2fa-options",
|
path: "2fa-options",
|
||||||
|
|||||||
@@ -524,4 +524,17 @@ export abstract class StateService<T extends Account = Account> {
|
|||||||
value: Record<string, Record<string, boolean>>,
|
value: Record<string, Record<string, boolean>>,
|
||||||
options?: StorageOptions
|
options?: StorageOptions
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
|
/**
|
||||||
|
* fetches string value of the URL stored here, usually only called after SSO flows.
|
||||||
|
* @param options Defines the storage options for the URL; Defaults to Local Storage.
|
||||||
|
* @returns route called prior to SSO routing to organizations configured IdP.
|
||||||
|
*/
|
||||||
|
getPreviousUrl: (options?: StorageOptions) => Promise<string>;
|
||||||
|
/**
|
||||||
|
* Store URL in local storage by default, but can be configured. Developed to handle
|
||||||
|
* SSO routing to organizations configured IdP.
|
||||||
|
* @param url URL of route
|
||||||
|
* @param options Defines the storage options for the URL; Defaults to Local Storage.
|
||||||
|
*/
|
||||||
|
setPreviousUrl: (url: string, options?: StorageOptions) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,4 +37,5 @@ export class GlobalState {
|
|||||||
enableBrowserIntegrationFingerprint?: boolean;
|
enableBrowserIntegrationFingerprint?: boolean;
|
||||||
enableDuckDuckGoBrowserIntegration?: boolean;
|
enableDuckDuckGoBrowserIntegration?: boolean;
|
||||||
region?: string;
|
region?: string;
|
||||||
|
previousUrl?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2828,6 +2828,23 @@ export class StateService<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getPreviousUrl(options?: StorageOptions): Promise<string> {
|
||||||
|
return (
|
||||||
|
await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
|
||||||
|
)?.previousUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setPreviousUrl(url: string, options?: StorageOptions): Promise<void> {
|
||||||
|
const globals = await this.getGlobals(
|
||||||
|
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())
|
||||||
|
);
|
||||||
|
globals.previousUrl = url;
|
||||||
|
await this.saveGlobals(
|
||||||
|
globals,
|
||||||
|
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
protected async getGlobals(options: StorageOptions): Promise<TGlobalState> {
|
protected async getGlobals(options: StorageOptions): Promise<TGlobalState> {
|
||||||
let globals: TGlobalState;
|
let globals: TGlobalState;
|
||||||
if (this.useMemory(options.storageLocation)) {
|
if (this.useMemory(options.storageLocation)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user