From 182716c04ab9bcdeacb2b9e63b8d5a9dcd191c5e Mon Sep 17 00:00:00 2001 From: Dave <3836813+enmande@users.noreply.github.com> Date: Wed, 8 Apr 2026 14:11:32 -0400 Subject: [PATCH] fix(desktop-app) [PM-34257]: Exclude custom SSO callback URLs from handling if SSO is not ongoing or state mismatch. (#19689) --- apps/desktop/src/app/app.component.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index a90f0266380..9d6749d4f11 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -40,6 +40,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; @@ -182,6 +183,7 @@ export class AppComponent implements OnInit, OnDestroy { private pendingAuthRequestsState: PendingAuthRequestsStateService, private authRequestService: AuthRequestServiceAbstraction, private authRequestAnsweringService: AuthRequestAnsweringService, + private ssoLoginService: SsoLoginServiceAbstraction, ) { this.deviceTrustToastService.setupListeners$.pipe(takeUntilDestroyed()).subscribe(); @@ -333,6 +335,24 @@ export class AppComponent implements OnInit, OnDestroy { } break; case "ssoCallback": { + const storedState = await this.ssoLoginService.getSsoState(); + const storedVerifier = await this.ssoLoginService.getCodeVerifier(); + + if (!storedState || !storedVerifier) { + this.logService.warning( + "[App Component] SSO callback rejected: no active SSO flow in progress", + ); + break; + } + + const storedStatePrefix = storedState.split("_identifier=")[0]; + const receivedStatePrefix = (message.state ?? "").split("_identifier=")[0]; + + if (storedStatePrefix !== receivedStatePrefix) { + this.logService.warning("[App Component] SSO callback rejected: state mismatch"); + break; + } + const queryParams = { code: message.code, state: message.state,