1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-10-09 05:57:40 +02:00
bitwarden-browser/apps/web/src/app/auth/sso.component.ts
Jake Fink 2111b37c32
[PM-5404, PM-3518] Migrate user decryption options to new service (#7344)
* create new user decryption options service

* rename new service to user decryption options

* add hasMasterPassword to user decryption options service

* migrate device trust service to new user decryption options service

* add migration for user-decryption-options

* migrate sync service and calls to trust-device-service

* rename abstraction file

* migrate two factor component

* migrate two factor spec

* migrate sso component

* migrate set-password component

* migrate base login decryption component

* migrate organization options component

* fix component imports

* add missing imports
- remove state service calls
- add update user decryption options method

* remove acct decryption options from account

* lint

* fix tests and linting

* fix browser

* fix desktop

* add user decryption options service to cli

* remove default value from migration

* bump migration number

* fix merge conflict

* fix vault timeout settings

* fix cli

* more fixes

* add user decryption options service to deps of vault timeout settings service

* update login strategy service with user decryption options

* remove early return from sync bandaid for user decryption options

* move user decryption options service to lib/auth

* move user decryption options to libs/auth

* fix reference

* fix browser

* check user decryption options after 2fa check

* update migration and revert tsconfig changes

* add more documentation

* clear user decryption options on logout

* fix tests by creating helper for user decryption options

* fix tests

* pr feedback

* fix factory

* update migration

* add tests

* update missed migration num in test
2024-03-21 00:33:57 +00:00

138 lines
5.7 KiB
TypeScript

import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";
import { SsoComponent as BaseSsoComponent } from "@bitwarden/angular/auth/components/sso.component";
import {
LoginStrategyServiceAbstraction,
UserDecryptionOptionsServiceAbstraction,
} from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrgDomainApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization-domain/org-domain-api.service.abstraction";
import { OrganizationDomainSsoDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-domain/responses/organization-domain-sso-details.response";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { HttpStatusCode } from "@bitwarden/common/enums";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
@Component({
selector: "app-sso",
templateUrl: "sso.component.html",
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class SsoComponent extends BaseSsoComponent {
constructor(
ssoLoginService: SsoLoginServiceAbstraction,
loginStrategyService: LoginStrategyServiceAbstraction,
router: Router,
i18nService: I18nService,
route: ActivatedRoute,
stateService: StateService,
platformUtilsService: PlatformUtilsService,
apiService: ApiService,
cryptoFunctionService: CryptoFunctionService,
environmentService: EnvironmentService,
passwordGenerationService: PasswordGenerationServiceAbstraction,
logService: LogService,
private orgDomainApiService: OrgDomainApiServiceAbstraction,
private validationService: ValidationService,
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
configService: ConfigServiceAbstraction,
) {
super(
ssoLoginService,
loginStrategyService,
router,
i18nService,
route,
stateService,
platformUtilsService,
apiService,
cryptoFunctionService,
environmentService,
passwordGenerationService,
logService,
userDecryptionOptionsService,
configService,
);
this.redirectUri = window.location.origin + "/sso-connector.html";
this.clientId = "web";
}
async ngOnInit() {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
super.ngOnInit();
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
if (qParams.identifier != null) {
// SSO Org Identifier in query params takes precedence over claimed domains
this.identifier = qParams.identifier;
} else {
// Note: this flow is written for web but both browser and desktop
// redirect here on SSO button click.
// Check if email matches any claimed domains
if (qParams.email) {
// show loading spinner
this.loggingIn = true;
try {
const response: OrganizationDomainSsoDetailsResponse =
await this.orgDomainApiService.getClaimedOrgDomainByEmail(qParams.email);
if (response?.ssoAvailable) {
this.identifier = response.organizationIdentifier;
await this.submit();
return;
}
} catch (error) {
this.handleGetClaimedDomainByEmailError(error);
}
this.loggingIn = false;
}
// Fallback to state svc if domain is unclaimed
const storedIdentifier = await this.ssoLoginService.getOrganizationSsoIdentifier();
if (storedIdentifier != null) {
this.identifier = storedIdentifier;
}
}
});
}
private handleGetClaimedDomainByEmailError(error: any): void {
if (error instanceof ErrorResponse) {
const errorResponse: ErrorResponse = error as ErrorResponse;
switch (errorResponse.statusCode) {
case HttpStatusCode.NotFound:
//this is a valid case for a domain not found
return;
default:
this.validationService.showError(errorResponse);
break;
}
}
}
async submit() {
await this.ssoLoginService.setOrganizationSsoIdentifier(this.identifier);
if (this.clientId === "browser") {
document.cookie = `ssoHandOffMessage=${this.i18nService.t("ssoHandOff")};SameSite=strict`;
}
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
super.submit();
}
}