1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-03 17:13:47 +00:00
Files
browser/apps/desktop/src/auth/login/desktop-login-component.service.ts
Ike 0e277a411d [PM-1632] Redirect on SSO required response from connect/token (#17637)
* feat: add Identity Sso Required Response type as possible response from token endpoint.

* feat: consume sso organization identifier to redirect user

* feat: add get requiresSso to AuthResult for more ergonomic code.

* feat: sso-redirect on sso-required for CLI and Desktop

* chore: fixing type errors

* test: fix and add tests for new sso method

* docs: fix misspelling

* fix: get email from AuthResult instead of the FormGroup

* fix:claude: when email is not available for SSO login show error toast.

* fix:claude: add null safety check
2025-12-10 10:31:28 -05:00

101 lines
3.8 KiB
TypeScript

// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Injectable } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { DefaultLoginComponentService, LoginComponentService } from "@bitwarden/auth/angular";
import { DESKTOP_SSO_CALLBACK, SsoUrlService } from "@bitwarden/auth/common";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
@Injectable()
export class DesktopLoginComponentService
extends DefaultLoginComponentService
implements LoginComponentService
{
constructor(
protected cryptoFunctionService: CryptoFunctionService,
protected environmentService: EnvironmentService,
protected passwordGenerationService: PasswordGenerationServiceAbstraction,
protected platformUtilsService: PlatformUtilsService,
protected ssoLoginService: SsoLoginServiceAbstraction,
protected i18nService: I18nService,
protected toastService: ToastService,
protected ssoUrlService: SsoUrlService,
) {
super(
cryptoFunctionService,
environmentService,
passwordGenerationService,
platformUtilsService,
ssoLoginService,
);
}
/**
* On the desktop, redirecting to the SSO login page is done via a new browser window, opened
* to the SSO component on the web client.
* @param email the email of the user trying to log in, used to look up the org SSO identifier
* @param state the state that will be used to verify the SSO login, which needs to be passed to the IdP
* @param codeChallenge the challenge that will be verified after the code is returned from the IdP, which needs to be passed to the IdP
*/
protected override async redirectToSso(
email: string,
state: string,
codeChallenge: string,
orgSsoIdentifier?: string,
): Promise<void> {
// For platforms that cannot support a protocol-based (e.g. bitwarden://) callback, we use a localhost callback
// Otherwise, we launch the SSO component in a browser window and wait for the callback
if (ipc.platform.isAppImage || ipc.platform.isDev) {
await this.initiateSsoThroughLocalhostCallback(email, state, codeChallenge, orgSsoIdentifier);
} else {
const env = await firstValueFrom(this.environmentService.environment$);
const webVaultUrl = env.getWebVaultUrl();
const redirectUri = DESKTOP_SSO_CALLBACK;
const ssoWebAppUrl = this.ssoUrlService.buildSsoUrl(
webVaultUrl,
this.clientType,
redirectUri,
state,
codeChallenge,
email,
orgSsoIdentifier,
);
this.platformUtilsService.launchUri(ssoWebAppUrl);
}
}
private async initiateSsoThroughLocalhostCallback(
email: string,
state: string,
challenge: string,
orgSsoIdentifier?: string,
): Promise<void> {
try {
await ipc.platform.localhostCallbackService.openSsoPrompt(
challenge,
state,
email,
orgSsoIdentifier,
);
// FIXME: Remove when updating file. Eslint update
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (err) {
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("ssoError"),
});
}
}
}