mirror of
https://github.com/bitwarden/browser
synced 2025-12-23 03:33:54 +00:00
feat(sso): [PM-8114] implement SSO component UI refresh
Consolidates existing SSO components into a single unified component in libs/auth, matching the new design system. This implementation: - Creates a new shared SsoComponent with extracted business logic - Adds feature flag support for unauth-ui-refresh - Updates page styling including new icons and typography - Preserves web client claimed domain logic - Maintains backwards compatibility with legacy views PM-8114 --------- Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Co-authored-by: Jared Snider <jsnider@bitwarden.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import { TestBed } from "@angular/core/testing";
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import {
|
||||
EnvironmentService,
|
||||
Environment,
|
||||
} from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
|
||||
import { BrowserApi } from "../../../platform/browser/browser-api";
|
||||
|
||||
import { ExtensionSsoComponentService } from "./extension-sso-component.service";
|
||||
|
||||
describe("ExtensionSsoComponentService", () => {
|
||||
let service: ExtensionSsoComponentService;
|
||||
const baseUrl = "https://vault.bitwarden.com";
|
||||
|
||||
let syncService: MockProxy<SyncService>;
|
||||
let authService: MockProxy<AuthService>;
|
||||
let environmentService: MockProxy<EnvironmentService>;
|
||||
let i18nService: MockProxy<I18nService>;
|
||||
let logService: MockProxy<LogService>;
|
||||
|
||||
beforeEach(() => {
|
||||
syncService = mock<SyncService>();
|
||||
authService = mock<AuthService>();
|
||||
environmentService = mock<EnvironmentService>();
|
||||
i18nService = mock<I18nService>();
|
||||
logService = mock<LogService>();
|
||||
environmentService.environment$ = new BehaviorSubject<Environment>({
|
||||
getWebVaultUrl: () => baseUrl,
|
||||
} as Environment);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{ provide: SyncService, useValue: syncService },
|
||||
{ provide: AuthService, useValue: authService },
|
||||
{ provide: EnvironmentService, useValue: environmentService },
|
||||
{ provide: I18nService, useValue: i18nService },
|
||||
{ provide: LogService, useValue: logService },
|
||||
ExtensionSsoComponentService,
|
||||
],
|
||||
});
|
||||
|
||||
service = TestBed.inject(ExtensionSsoComponentService);
|
||||
|
||||
jest.spyOn(BrowserApi, "reloadOpenWindows").mockImplementation();
|
||||
});
|
||||
|
||||
it("creates the service", () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
describe("closeWindow", () => {
|
||||
it("closes window", async () => {
|
||||
const windowSpy = jest.spyOn(window, "close").mockImplementation();
|
||||
|
||||
await service.closeWindow?.();
|
||||
|
||||
expect(windowSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { DefaultSsoComponentService, SsoComponentService } from "@bitwarden/auth/angular";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
|
||||
/**
|
||||
* This service is used to handle the SSO login process for the browser extension.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ExtensionSsoComponentService
|
||||
extends DefaultSsoComponentService
|
||||
implements SsoComponentService
|
||||
{
|
||||
constructor(
|
||||
protected syncService: SyncService,
|
||||
protected authService: AuthService,
|
||||
protected environmentService: EnvironmentService,
|
||||
protected i18nService: I18nService,
|
||||
protected logService: LogService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the popup window after a successful login.
|
||||
*/
|
||||
async closeWindow() {
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,9 @@ import { BrowserApi } from "../../platform/browser/browser-api";
|
||||
|
||||
@Component({
|
||||
selector: "app-sso",
|
||||
templateUrl: "sso.component.html",
|
||||
templateUrl: "sso-v1.component.html",
|
||||
})
|
||||
export class SsoComponent extends BaseSsoComponent {
|
||||
export class SsoComponentV1 extends BaseSsoComponent {
|
||||
constructor(
|
||||
ssoLoginService: SsoLoginServiceAbstraction,
|
||||
loginStrategyService: LoginStrategyServiceAbstraction,
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
VaultIcon,
|
||||
LoginDecryptionOptionsComponent,
|
||||
DevicesIcon,
|
||||
SsoComponent,
|
||||
TwoFactorTimeoutIcon,
|
||||
} from "@bitwarden/auth/angular";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
@@ -62,7 +63,7 @@ import { RemovePasswordComponent } from "../auth/popup/remove-password.component
|
||||
import { SetPasswordComponent } from "../auth/popup/set-password.component";
|
||||
import { AccountSecurityComponent as AccountSecurityV1Component } from "../auth/popup/settings/account-security-v1.component";
|
||||
import { AccountSecurityComponent } from "../auth/popup/settings/account-security.component";
|
||||
import { SsoComponent } from "../auth/popup/sso.component";
|
||||
import { SsoComponentV1 } from "../auth/popup/sso-v1.component";
|
||||
import { TwoFactorAuthComponent } from "../auth/popup/two-factor-auth.component";
|
||||
import { TwoFactorOptionsComponent } from "../auth/popup/two-factor-options.component";
|
||||
import { TwoFactorComponent } from "../auth/popup/two-factor.component";
|
||||
@@ -230,12 +231,40 @@ const routes: Routes = [
|
||||
canActivate: [unauthGuardFn(unauthRouteOverrides)],
|
||||
data: { elevation: 1 } satisfies RouteDataProperties,
|
||||
},
|
||||
{
|
||||
path: "sso",
|
||||
component: SsoComponent,
|
||||
canActivate: [unauthGuardFn(unauthRouteOverrides)],
|
||||
data: { elevation: 1 } satisfies RouteDataProperties,
|
||||
},
|
||||
...unauthUiRefreshSwap(
|
||||
SsoComponentV1,
|
||||
ExtensionAnonLayoutWrapperComponent,
|
||||
{
|
||||
path: "sso",
|
||||
canActivate: [unauthGuardFn(unauthRouteOverrides)],
|
||||
data: { elevation: 1 } satisfies RouteDataProperties,
|
||||
},
|
||||
{
|
||||
path: "sso",
|
||||
canActivate: [unauthGuardFn(unauthRouteOverrides)],
|
||||
data: {
|
||||
pageIcon: VaultIcon,
|
||||
pageTitle: {
|
||||
key: "enterpriseSingleSignOn",
|
||||
},
|
||||
pageSubtitle: {
|
||||
key: "singleSignOnEnterOrgIdentifierText",
|
||||
},
|
||||
elevation: 1,
|
||||
} satisfies RouteDataProperties & ExtensionAnonLayoutWrapperData,
|
||||
children: [
|
||||
{ path: "", component: SsoComponent },
|
||||
{
|
||||
path: "",
|
||||
component: EnvironmentSelectorComponent,
|
||||
outlet: "environment-selector",
|
||||
data: {
|
||||
overlayPosition: ExtensionDefaultOverlayPosition,
|
||||
} satisfies EnvironmentSelectorRouteData,
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
{
|
||||
path: "set-password",
|
||||
component: SetPasswordComponent,
|
||||
|
||||
@@ -33,7 +33,7 @@ import { SetPasswordComponent } from "../auth/popup/set-password.component";
|
||||
import { AccountSecurityComponent as AccountSecurityComponentV1 } from "../auth/popup/settings/account-security-v1.component";
|
||||
import { AccountSecurityComponent } from "../auth/popup/settings/account-security.component";
|
||||
import { VaultTimeoutInputComponent } from "../auth/popup/settings/vault-timeout-input.component";
|
||||
import { SsoComponent } from "../auth/popup/sso.component";
|
||||
import { SsoComponentV1 } from "../auth/popup/sso-v1.component";
|
||||
import { TwoFactorOptionsComponent } from "../auth/popup/two-factor-options.component";
|
||||
import { TwoFactorComponent } from "../auth/popup/two-factor.component";
|
||||
import { UpdateTempPasswordComponent } from "../auth/popup/update-temp-password.component";
|
||||
@@ -177,7 +177,7 @@ import "../platform/popup/locales";
|
||||
SettingsComponent,
|
||||
VaultSettingsComponent,
|
||||
ShareComponent,
|
||||
SsoComponent,
|
||||
SsoComponentV1,
|
||||
SyncComponent,
|
||||
TabsComponent,
|
||||
TabsV2Component,
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
AnonLayoutWrapperDataService,
|
||||
LoginComponentService,
|
||||
LockComponentService,
|
||||
SsoComponentService,
|
||||
LoginDecryptionOptionsService,
|
||||
} from "@bitwarden/auth/angular";
|
||||
import { LockService, LoginEmailService, PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||
@@ -119,6 +120,7 @@ import { PasswordRepromptService } from "@bitwarden/vault";
|
||||
import { ForegroundLockService } from "../../auth/popup/accounts/foreground-lock.service";
|
||||
import { ExtensionAnonLayoutWrapperDataService } from "../../auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper-data.service";
|
||||
import { ExtensionLoginComponentService } from "../../auth/popup/login/extension-login-component.service";
|
||||
import { ExtensionSsoComponentService } from "../../auth/popup/login/extension-sso-component.service";
|
||||
import { ExtensionLoginDecryptionOptionsService } from "../../auth/popup/login-decryption-options/extension-login-decryption-options.service";
|
||||
import { AutofillService as AutofillServiceAbstraction } from "../../autofill/services/abstractions/autofill.service";
|
||||
import AutofillService from "../../autofill/services/autofill.service";
|
||||
@@ -597,6 +599,11 @@ const safeProviders: SafeProvider[] = [
|
||||
useExisting: PopupCompactModeService,
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SsoComponentService,
|
||||
useClass: ExtensionSsoComponentService,
|
||||
deps: [SyncService, AuthService, EnvironmentService, I18nServiceAbstraction, LogService],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: LoginDecryptionOptionsService,
|
||||
useClass: ExtensionLoginDecryptionOptionsService,
|
||||
|
||||
Reference in New Issue
Block a user