mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 14:23:32 +00:00
[PM-5364] Create SSO Login Service and add state ownership (#7485)
* create sso service * rename sso service to sso-login service * rename service * add references to sso login service and update state calls * fix browser * fix desktop * return promises * remove sso state from account and global objects * more descriptive org sso identifier method names * fix sso tests * fix tests
This commit is contained in:
@@ -21,6 +21,7 @@ import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abst
|
||||
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
|
||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { PasswordResetEnrollmentServiceAbstraction } from "@bitwarden/common/auth/abstractions/password-reset-enrollment.service.abstraction";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { KeysRequest } from "@bitwarden/common/models/request/keys.request";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
@@ -88,6 +89,7 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
||||
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected passwordResetEnrollmentService: PasswordResetEnrollmentServiceAbstraction,
|
||||
protected ssoLoginService: SsoLoginServiceAbstraction,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -163,7 +165,7 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
||||
|
||||
async loadNewUserData() {
|
||||
const autoEnrollStatus$ = defer(() =>
|
||||
this.stateService.getUserSsoOrganizationIdentifier(),
|
||||
this.ssoLoginService.getActiveUserOrganizationSsoIdentifier(),
|
||||
).pipe(
|
||||
switchMap((organizationIdentifier) => {
|
||||
if (organizationIdentifier == undefined) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { take, takeUntil } from "rxjs/operators";
|
||||
import { LoginStrategyServiceAbstraction, PasswordLoginCredentials } from "@bitwarden/auth/common";
|
||||
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
|
||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login.service.abstraction";
|
||||
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
@@ -78,6 +79,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit,
|
||||
protected formValidationErrorService: FormValidationErrorsService,
|
||||
protected route: ActivatedRoute,
|
||||
protected loginService: LoginService,
|
||||
protected ssoLoginService: SsoLoginServiceAbstraction,
|
||||
protected webAuthnLoginService: WebAuthnLoginServiceAbstraction,
|
||||
) {
|
||||
super(environmentService, i18nService, platformUtilsService);
|
||||
@@ -241,8 +243,8 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit,
|
||||
const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
|
||||
|
||||
// Save sso params
|
||||
await this.stateService.setSsoState(state);
|
||||
await this.stateService.setSsoCodeVerifier(ssoCodeVerifier);
|
||||
await this.ssoLoginService.setSsoState(state);
|
||||
await this.ssoLoginService.setCodeVerifier(ssoCodeVerifier);
|
||||
|
||||
// Build URI
|
||||
const webUrl = this.environmentService.getWebVaultUrl();
|
||||
|
||||
@@ -11,6 +11,7 @@ import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abs
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||
import { OrganizationAutoEnrollStatusResponse } from "@bitwarden/common/admin-console/models/response/organization-auto-enroll-status.response";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request";
|
||||
import { KeysRequest } from "@bitwarden/common/models/request/keys.request";
|
||||
@@ -63,6 +64,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||
stateService: StateService,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private organizationUserService: OrganizationUserService,
|
||||
private ssoLoginService: SsoLoginServiceAbstraction,
|
||||
dialogService: DialogService,
|
||||
) {
|
||||
super(
|
||||
@@ -96,7 +98,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||
} else {
|
||||
// Try to get orgSsoId from state as fallback
|
||||
// Note: this is primarily for the TDE user w/out MP obtains admin MP reset permission scenario.
|
||||
return this.stateService.getUserSsoOrganizationIdentifier();
|
||||
return this.ssoLoginService.getActiveUserOrganizationSsoIdentifier();
|
||||
}
|
||||
}),
|
||||
filter((orgSsoId) => orgSsoId != null),
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Observable, of } from "rxjs";
|
||||
|
||||
import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
@@ -53,6 +54,7 @@ describe("SsoComponent", () => {
|
||||
let mockQueryParams: Observable<any>;
|
||||
let mockActivatedRoute: ActivatedRoute;
|
||||
|
||||
let mockSsoLoginService: MockProxy<SsoLoginServiceAbstraction>;
|
||||
let mockStateService: MockProxy<StateService>;
|
||||
let mockPlatformUtilsService: MockProxy<PlatformUtilsService>;
|
||||
let mockApiService: MockProxy<ApiService>;
|
||||
@@ -99,6 +101,7 @@ describe("SsoComponent", () => {
|
||||
queryParams: mockQueryParams,
|
||||
} as any as ActivatedRoute;
|
||||
|
||||
mockSsoLoginService = mock<SsoLoginServiceAbstraction>();
|
||||
mockStateService = mock<StateService>();
|
||||
mockPlatformUtilsService = mock<PlatformUtilsService>();
|
||||
mockApiService = mock<ApiService>();
|
||||
@@ -167,6 +170,7 @@ describe("SsoComponent", () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [TestSsoComponent],
|
||||
providers: [
|
||||
{ provide: SsoLoginServiceAbstraction, useValue: mockSsoLoginService },
|
||||
{ provide: LoginStrategyServiceAbstraction, useValue: mockLoginStrategyService },
|
||||
{ provide: Router, useValue: mockRouter },
|
||||
{ provide: I18nService, useValue: mockI18nService },
|
||||
|
||||
@@ -4,6 +4,7 @@ import { first } from "rxjs/operators";
|
||||
|
||||
import { LoginStrategyServiceAbstraction, SsoLoginCredentials } from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
import { TrustedDeviceUserDecryptionOption } from "@bitwarden/common/auth/models/domain/user-decryption-options/trusted-device-user-decryption-option";
|
||||
@@ -46,6 +47,7 @@ export class SsoComponent {
|
||||
protected codeChallenge: string;
|
||||
|
||||
constructor(
|
||||
protected ssoLoginService: SsoLoginServiceAbstraction,
|
||||
protected loginStrategyService: LoginStrategyServiceAbstraction,
|
||||
protected router: Router,
|
||||
protected i18nService: I18nService,
|
||||
@@ -64,10 +66,10 @@ export class SsoComponent {
|
||||
// eslint-disable-next-line rxjs/no-async-subscribe
|
||||
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
|
||||
if (qParams.code != null && qParams.state != null) {
|
||||
const codeVerifier = await this.stateService.getSsoCodeVerifier();
|
||||
const state = await this.stateService.getSsoState();
|
||||
await this.stateService.setSsoCodeVerifier(null);
|
||||
await this.stateService.setSsoState(null);
|
||||
const codeVerifier = await this.ssoLoginService.getCodeVerifier();
|
||||
const state = await this.ssoLoginService.getSsoState();
|
||||
await this.ssoLoginService.setCodeVerifier(null);
|
||||
await this.ssoLoginService.setSsoState(null);
|
||||
if (
|
||||
qParams.code != null &&
|
||||
codeVerifier != null &&
|
||||
@@ -133,7 +135,7 @@ export class SsoComponent {
|
||||
const codeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions);
|
||||
const codeVerifierHash = await this.cryptoFunctionService.hash(codeVerifier, "sha256");
|
||||
codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
|
||||
await this.stateService.setSsoCodeVerifier(codeVerifier);
|
||||
await this.ssoLoginService.setCodeVerifier(codeVerifier);
|
||||
}
|
||||
|
||||
if (state == null) {
|
||||
@@ -147,7 +149,7 @@ export class SsoComponent {
|
||||
state += `_identifier=${this.identifier}`;
|
||||
|
||||
// Save state (regardless of new or existing)
|
||||
await this.stateService.setSsoState(state);
|
||||
await this.ssoLoginService.setSsoState(state);
|
||||
|
||||
let authorizeUrl =
|
||||
this.environmentService.getIdentityUrl() +
|
||||
@@ -203,7 +205,7 @@ export class SsoComponent {
|
||||
// - TDE login decryption options component
|
||||
// - Browser SSO on extension open
|
||||
// Note: you cannot set this in state before 2FA b/c there won't be an account in state.
|
||||
await this.stateService.setUserSsoOrganizationIdentifier(orgSsoIdentifier);
|
||||
await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier(orgSsoIdentifier);
|
||||
|
||||
// Users enrolled in admin acct recovery can be forced to set a new password after
|
||||
// having the admin set a temp password for them (affects TDE & standard users)
|
||||
|
||||
@@ -8,6 +8,7 @@ import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
|
||||
import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
@@ -55,6 +56,7 @@ describe("TwoFactorComponent", () => {
|
||||
let mockTwoFactorService: MockProxy<TwoFactorService>;
|
||||
let mockAppIdService: MockProxy<AppIdService>;
|
||||
let mockLoginService: MockProxy<LoginService>;
|
||||
let mockSsoLoginService: MockProxy<SsoLoginServiceAbstraction>;
|
||||
let mockConfigService: MockProxy<ConfigServiceAbstraction>;
|
||||
|
||||
let mockAcctDecryptionOpts: {
|
||||
@@ -81,6 +83,7 @@ describe("TwoFactorComponent", () => {
|
||||
mockTwoFactorService = mock<TwoFactorService>();
|
||||
mockAppIdService = mock<AppIdService>();
|
||||
mockLoginService = mock<LoginService>();
|
||||
mockSsoLoginService = mock<SsoLoginServiceAbstraction>();
|
||||
mockConfigService = mock<ConfigServiceAbstraction>();
|
||||
|
||||
mockAcctDecryptionOpts = {
|
||||
@@ -150,6 +153,7 @@ describe("TwoFactorComponent", () => {
|
||||
{ provide: TwoFactorService, useValue: mockTwoFactorService },
|
||||
{ provide: AppIdService, useValue: mockAppIdService },
|
||||
{ provide: LoginService, useValue: mockLoginService },
|
||||
{ provide: SsoLoginServiceAbstraction, useValue: mockSsoLoginService },
|
||||
{ provide: ConfigServiceAbstraction, useValue: mockConfigService },
|
||||
],
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
|
||||
import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||
@@ -83,6 +84,7 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
||||
protected twoFactorService: TwoFactorService,
|
||||
protected appIdService: AppIdService,
|
||||
protected loginService: LoginService,
|
||||
protected ssoLoginService: SsoLoginServiceAbstraction,
|
||||
protected configService: ConfigServiceAbstraction,
|
||||
) {
|
||||
super(environmentService, i18nService, platformUtilsService);
|
||||
@@ -278,7 +280,7 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
||||
// Save off the OrgSsoIdentifier for use in the TDE flows
|
||||
// - TDE login decryption options component
|
||||
// - Browser SSO on extension open
|
||||
await this.stateService.setUserSsoOrganizationIdentifier(this.orgIdentifier);
|
||||
await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier(this.orgIdentifier);
|
||||
this.loginService.clearValues();
|
||||
|
||||
// note: this flow affects both TDE & standard users
|
||||
|
||||
@@ -55,6 +55,7 @@ import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstraction
|
||||
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { PasswordResetEnrollmentServiceAbstraction } from "@bitwarden/common/auth/abstractions/password-reset-enrollment.service.abstraction";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||
import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction";
|
||||
@@ -73,6 +74,7 @@ import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services
|
||||
import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
|
||||
import { LoginService } from "@bitwarden/common/auth/services/login.service";
|
||||
import { PasswordResetEnrollmentServiceImplementation } from "@bitwarden/common/auth/services/password-reset-enrollment.service.implementation";
|
||||
import { SsoLoginService } from "@bitwarden/common/auth/services/sso-login.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/services/token.service";
|
||||
import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service";
|
||||
import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service";
|
||||
@@ -534,6 +536,11 @@ import { ModalService } from "./modal.service";
|
||||
provide: VaultTimeoutServiceAbstraction,
|
||||
useExisting: VaultTimeoutService,
|
||||
},
|
||||
{
|
||||
provide: SsoLoginServiceAbstraction,
|
||||
useClass: SsoLoginService,
|
||||
deps: [StateProvider],
|
||||
},
|
||||
{
|
||||
provide: StateServiceAbstraction,
|
||||
useClass: StateService,
|
||||
|
||||
Reference in New Issue
Block a user