1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-16 08:34:39 +00:00
Files
browser/apps/desktop/src/app/app-routing.module.ts
Oscar Hinton 5fe01323bb Improve routing during account switching to ensure feature flags are triggered (#18037)
When account switching from the old layout it's not bringing you into the new design. This resolves it by ensuring the route guard for vault is triggered after account switching.
2026-01-13 10:23:29 +00:00

382 lines
11 KiB
TypeScript

import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { AuthenticationTimeoutComponent } from "@bitwarden/angular/auth/components/authentication-timeout.component";
import { AuthRoute } from "@bitwarden/angular/auth/constants";
import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/environment-selector/environment-selector.component";
import {
authGuard,
lockGuard,
activeAuthGuard,
redirectGuard,
tdeDecryptionRequiredGuard,
unauthGuardFn,
} from "@bitwarden/angular/auth/guards";
import { ChangePasswordComponent } from "@bitwarden/angular/auth/password-management/change-password";
import { SetInitialPasswordComponent } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.component";
import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard";
import {
DevicesIcon,
RegistrationUserAddIcon,
TwoFactorTimeoutIcon,
TwoFactorAuthEmailIcon,
UserLockIcon,
VaultIcon,
LockIcon,
DomainIcon,
} from "@bitwarden/assets/svg";
import {
LoginComponent,
LoginSecondaryContentComponent,
LoginViaAuthRequestComponent,
PasswordHintComponent,
RegistrationFinishComponent,
RegistrationStartComponent,
RegistrationStartSecondaryComponent,
RegistrationStartSecondaryComponentData,
LoginDecryptionOptionsComponent,
SsoComponent,
TwoFactorAuthComponent,
TwoFactorAuthGuard,
NewDeviceVerificationComponent,
} from "@bitwarden/auth/angular";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components";
import {
LockComponent,
ConfirmKeyConnectorDomainComponent,
RemovePasswordComponent,
} from "@bitwarden/key-management-ui";
import { maxAccountsGuardFn } from "../auth/guards/max-accounts.guard";
import { reactiveUnlockVaultGuard } from "../autofill/guards/reactive-vault-guard";
import { Fido2CreateComponent } from "../autofill/modal/credentials/fido2-create.component";
import { Fido2ExcludedCiphersComponent } from "../autofill/modal/credentials/fido2-excluded-ciphers.component";
import { Fido2VaultComponent } from "../autofill/modal/credentials/fido2-vault.component";
import { VaultV2Component } from "../vault/app/vault/vault-v2.component";
import { VaultComponent } from "../vault/app/vault-v3/vault.component";
import { DesktopLayoutComponent } from "./layout/desktop-layout.component";
import { SendComponent } from "./tools/send/send.component";
import { SendV2Component } from "./tools/send-v2/send-v2.component";
/**
* Data properties acceptable for use in route objects in the desktop
*/
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface RouteDataProperties {
// For any new route data properties, add them here.
// then assert that the data object satisfies this interface in the route object.
}
const routes: Routes = [
{
path: "",
pathMatch: "full",
children: [], // Children lets us have an empty component.
canActivate: [redirectGuard({ loggedIn: "/vault", loggedOut: "/login", locked: "/lock" })],
},
{
path: AuthRoute.AuthenticationTimeout,
component: AnonLayoutWrapperComponent,
children: [
{
path: "",
component: AuthenticationTimeoutComponent,
},
],
data: {
pageIcon: TwoFactorTimeoutIcon,
pageTitle: {
key: "authenticationTimeout",
},
} satisfies RouteDataProperties & AnonLayoutWrapperData,
},
{
path: AuthRoute.NewDeviceVerification,
component: AnonLayoutWrapperComponent,
canActivate: [unauthGuardFn(), activeAuthGuard()],
children: [{ path: "", component: NewDeviceVerificationComponent }],
data: {
pageIcon: TwoFactorAuthEmailIcon,
pageTitle: {
key: "verifyYourIdentity",
},
pageSubtitle: {
key: "weDontRecognizeThisDevice",
},
} satisfies RouteDataProperties & AnonLayoutWrapperData,
},
{
path: "vault",
component: VaultV2Component,
canActivate: [
authGuard,
canAccessFeature(FeatureFlag.DesktopUiMigrationMilestone1, false, "new-vault", false),
],
// Needed to ensure feature flag changes are picked up on account switching
runGuardsAndResolvers: "always",
},
{
path: "send",
component: SendComponent,
canActivate: [authGuard],
},
{
path: "fido2-assertion",
component: Fido2VaultComponent,
},
{
path: "fido2-creation",
component: Fido2CreateComponent,
},
{
path: "fido2-excluded",
component: Fido2ExcludedCiphersComponent,
},
{
path: "",
component: AnonLayoutWrapperComponent,
children: [
{
path: AuthRoute.SignUp,
canActivate: [unauthGuardFn()],
data: {
pageIcon: RegistrationUserAddIcon,
pageTitle: {
key: "createAccount",
},
} satisfies AnonLayoutWrapperData,
children: [
{
path: "",
component: RegistrationStartComponent,
},
{
path: "",
component: RegistrationStartSecondaryComponent,
outlet: "secondary",
data: {
loginRoute: `/${AuthRoute.Login}`,
} satisfies RegistrationStartSecondaryComponentData,
},
],
},
{
path: AuthRoute.FinishSignUp,
canActivate: [unauthGuardFn()],
data: {
pageIcon: LockIcon,
} satisfies AnonLayoutWrapperData,
children: [
{
path: "",
component: RegistrationFinishComponent,
},
],
},
{
path: AuthRoute.Login,
canActivate: [maxAccountsGuardFn()],
data: {
pageTitle: {
key: "logInToBitwarden",
},
pageIcon: VaultIcon,
},
children: [
{ path: "", component: LoginComponent },
{ path: "", component: LoginSecondaryContentComponent, outlet: "secondary" },
{
path: "",
component: EnvironmentSelectorComponent,
outlet: "environment-selector",
},
],
},
{
path: AuthRoute.LoginInitiated,
canActivate: [tdeDecryptionRequiredGuard()],
data: {
pageIcon: DevicesIcon,
},
children: [{ path: "", component: LoginDecryptionOptionsComponent }],
},
{
path: AuthRoute.Sso,
data: {
pageIcon: VaultIcon,
pageTitle: {
key: "enterpriseSingleSignOn",
},
pageSubtitle: {
key: "singleSignOnEnterOrgIdentifierText",
},
} satisfies AnonLayoutWrapperData,
children: [
{ path: "", component: SsoComponent },
{
path: "",
component: EnvironmentSelectorComponent,
outlet: "environment-selector",
},
],
},
{
path: AuthRoute.LoginWithDevice,
data: {
pageIcon: DevicesIcon,
pageTitle: {
key: "logInRequestSent",
},
pageSubtitle: {
key: "aNotificationWasSentToYourDevice",
},
} satisfies AnonLayoutWrapperData,
children: [
{ path: "", component: LoginViaAuthRequestComponent },
{
path: "",
component: EnvironmentSelectorComponent,
outlet: "environment-selector",
},
],
},
{
path: AuthRoute.AdminApprovalRequested,
data: {
pageIcon: DevicesIcon,
pageTitle: {
key: "adminApprovalRequested",
},
pageSubtitle: {
key: "adminApprovalRequestSentToAdmins",
},
} satisfies AnonLayoutWrapperData,
children: [{ path: "", component: LoginViaAuthRequestComponent }],
},
{
path: AuthRoute.PasswordHint,
canActivate: [unauthGuardFn()],
data: {
pageTitle: {
key: "requestPasswordHint",
},
pageSubtitle: {
key: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou",
},
pageIcon: UserLockIcon,
} satisfies AnonLayoutWrapperData,
children: [
{ path: "", component: PasswordHintComponent },
{
path: "",
component: EnvironmentSelectorComponent,
outlet: "environment-selector",
},
],
},
{
path: "lock",
canActivate: [lockGuard(), reactiveUnlockVaultGuard],
data: {
pageIcon: LockIcon,
pageTitle: {
key: "yourVaultIsLockedV2",
},
showReadonlyHostname: true,
} satisfies AnonLayoutWrapperData,
children: [
{
path: "",
component: LockComponent,
},
],
},
{
path: AuthRoute.TwoFactor,
canActivate: [unauthGuardFn(), TwoFactorAuthGuard],
children: [
{
path: "",
component: TwoFactorAuthComponent,
},
],
data: {
pageTitle: {
key: "verifyYourIdentity",
},
// `TwoFactorAuthComponent` manually sets its icon based on the 2fa type
pageIcon: null,
} satisfies RouteDataProperties & AnonLayoutWrapperData,
},
{
path: AuthRoute.SetInitialPassword,
canActivate: [authGuard],
component: SetInitialPasswordComponent,
data: {
maxWidth: "lg",
pageIcon: LockIcon,
} satisfies AnonLayoutWrapperData,
},
{
path: AuthRoute.ChangePassword,
component: ChangePasswordComponent,
canActivate: [authGuard],
data: {
pageIcon: LockIcon,
} satisfies AnonLayoutWrapperData,
},
{
path: "remove-password",
component: RemovePasswordComponent,
canActivate: [authGuard],
data: {
pageTitle: {
key: "verifyYourOrganization",
},
pageIcon: LockIcon,
} satisfies RouteDataProperties & AnonLayoutWrapperData,
},
{
path: "confirm-key-connector-domain",
component: ConfirmKeyConnectorDomainComponent,
canActivate: [],
data: {
pageTitle: {
key: "verifyYourOrganization",
},
pageIcon: DomainIcon,
} satisfies RouteDataProperties & AnonLayoutWrapperData,
},
],
},
{
path: "",
component: DesktopLayoutComponent,
canActivate: [authGuard],
children: [
{
path: "new-vault",
component: VaultComponent,
},
{
path: "new-sends",
component: SendV2Component,
data: { pageTitle: { key: "send" } } satisfies RouteDataProperties,
},
],
},
];
@NgModule({
imports: [
RouterModule.forRoot(routes, {
useHash: true,
// enableTracing: true,
}),
],
exports: [RouterModule],
})
export class AppRoutingModule {}