1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-21 18:53:29 +00:00

[PM-17751] Store SSO email in state on web client (#13295)

* Moved saving of SSO email outside of browser/desktop code

* Clarified comments.

* Tests

* Refactored login component services to manage state

* Fixed input on login component

* Fixed tests

* Linting

* Moved web setting in state into web override

* updated tests

* Fixed typing.

* Fixed type safety issues.

* Added comments and renamed for clarity.

* Removed method parameters that weren't used

* Added clarifying comments

* Added more comments.

* Removed test that is not necessary on base

* Test cleanup

* More comments.

* Linting

* Fixed test.

* Fixed base URL

* Fixed typechecking.

* Type checking

* Moved setting of email state to default service

* Added comments.

* Consolidated SSO URL formatting

* Updated comment

* Fixed reference.

* Fixed missing parameter.

* Initialized service.

* Added comments

* Added initialization of new service

* Made email optional due to CLI.

* Fixed comment on handleSsoClick.

* Added SSO email persistence to v1 component.

---------

Co-authored-by: Bernd Schoolmann <mail@quexten.com>
This commit is contained in:
Todd Martin
2025-02-21 17:09:50 -05:00
committed by GitHub
parent 9dd2033081
commit 077e0f89cc
26 changed files with 534 additions and 245 deletions

View File

@@ -6,3 +6,4 @@ export * from "./auth-request/auth-request.service";
export * from "./auth-request/auth-request-api.service";
export * from "./accounts/lock.service";
export * from "./login-success-handler/default-login-success-handler.service";
export * from "./sso-redirect/sso-url.service";

View File

@@ -0,0 +1,95 @@
import { ClientType } from "@bitwarden/common/enums";
import { DESKTOP_SSO_CALLBACK, SsoUrlService } from "./sso-url.service";
describe("SsoUrlService", () => {
let service: SsoUrlService;
beforeEach(() => {
service = new SsoUrlService();
});
it("should build Desktop SSO URL correctly", () => {
const baseUrl = "https://web-vault.bitwarden.com";
const clientType = ClientType.Desktop;
const redirectUri = DESKTOP_SSO_CALLBACK;
const state = "abc123";
const codeChallenge = "xyz789";
const email = "test@bitwarden.com";
const expectedUrl = `${baseUrl}/#/sso?clientId=desktop&redirectUri=${encodeURIComponent(redirectUri)}&state=${state}&codeChallenge=${codeChallenge}&email=${encodeURIComponent(email)}`;
const result = service.buildSsoUrl(
baseUrl,
clientType,
redirectUri,
state,
codeChallenge,
email,
);
expect(result).toBe(expectedUrl);
});
it("should build Desktop localhost callback SSO URL correctly", () => {
const baseUrl = "https://web-vault.bitwarden.com";
const clientType = ClientType.Desktop;
const redirectUri = `https://localhost:1000`;
const state = "abc123";
const codeChallenge = "xyz789";
const email = "test@bitwarden.com";
const expectedUrl = `${baseUrl}/#/sso?clientId=desktop&redirectUri=${encodeURIComponent(redirectUri)}&state=${state}&codeChallenge=${codeChallenge}&email=${encodeURIComponent(email)}`;
const result = service.buildSsoUrl(
baseUrl,
clientType,
redirectUri,
state,
codeChallenge,
email,
);
expect(result).toBe(expectedUrl);
});
it("should build Extension SSO URL correctly", () => {
const baseUrl = "https://web-vault.bitwarden.com";
const clientType = ClientType.Browser;
const redirectUri = baseUrl + "/sso-connector.html";
const state = "abc123";
const codeChallenge = "xyz789";
const email = "test@bitwarden.com";
const expectedUrl = `${baseUrl}/#/sso?clientId=browser&redirectUri=${encodeURIComponent(redirectUri)}&state=${state}&codeChallenge=${codeChallenge}&email=${encodeURIComponent(email)}`;
const result = service.buildSsoUrl(
baseUrl,
clientType,
redirectUri,
state,
codeChallenge,
email,
);
expect(result).toBe(expectedUrl);
});
it("should build CLI SSO URL correctly", () => {
const baseUrl = "https://web-vault.bitwarden.com";
const clientType = ClientType.Cli;
const redirectUri = "https://localhost:1000";
const state = "abc123";
const codeChallenge = "xyz789";
const email = "test@bitwarden.com";
const expectedUrl = `${baseUrl}/#/sso?clientId=cli&redirectUri=${encodeURIComponent(redirectUri)}&state=${state}&codeChallenge=${codeChallenge}&email=${encodeURIComponent(email)}`;
const result = service.buildSsoUrl(
baseUrl,
clientType,
redirectUri,
state,
codeChallenge,
email,
);
expect(result).toBe(expectedUrl);
});
});

View File

@@ -0,0 +1,41 @@
import { ClientType } from "@bitwarden/common/enums";
export const DESKTOP_SSO_CALLBACK: string = "bitwarden://sso-callback";
export class SsoUrlService {
/**
* Builds a URL for redirecting users to the web app SSO component to complete SSO
* @param webAppUrl The URL of the web app
* @param clientType The client type that is initiating SSO, which will drive how the response is handled
* @param redirectUri The redirect URI or callback that will receive the SSO code after authentication
* @param state A state value that will be peristed through the SSO flow
* @param codeChallenge A challenge value that will be used to verify the SSO code after authentication
* @param email The optional email adddress of the user initiating SSO, which will be used to look up the org SSO identifier
* @returns The URL for redirecting users to the web app SSO component
*/
buildSsoUrl(
webAppUrl: string,
clientType: ClientType,
redirectUri: string,
state: string,
codeChallenge: string,
email?: string,
): string {
let url =
webAppUrl +
"/#/sso?clientId=" +
clientType +
"&redirectUri=" +
encodeURIComponent(redirectUri) +
"&state=" +
state +
"&codeChallenge=" +
codeChallenge;
if (email) {
url += "&email=" + encodeURIComponent(email);
}
return url;
}
}