mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
feat(CLI-SSO-Login): [Auth/PM-21116] CLI - SSO Login - Add SSO Org Identifier option (#14605)
* Add --identifier option for SSO on CLI * Add option for identifier * Moved auto-submit after the setting of client arguments * Adjusted comment * Changed to pass in as SSO option * Renamed to orgSsoIdentifier for clarity * Added more changes to orgSsoIdentifier.
This commit is contained in:
@@ -106,6 +106,8 @@ export class LoginCommand {
|
||||
return Response.badRequest("client_secret is required.");
|
||||
}
|
||||
} else if (options.sso != null && this.canInteract) {
|
||||
// If the optional Org SSO Identifier isn't provided, the option value is `true`.
|
||||
const orgSsoIdentifier = options.sso === true ? null : options.sso;
|
||||
const passwordOptions: any = {
|
||||
type: "password",
|
||||
length: 64,
|
||||
@@ -119,7 +121,7 @@ export class LoginCommand {
|
||||
const codeVerifierHash = await this.cryptoFunctionService.hash(ssoCodeVerifier, "sha256");
|
||||
const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
|
||||
try {
|
||||
const ssoParams = await this.openSsoPrompt(codeChallenge, state);
|
||||
const ssoParams = await this.openSsoPrompt(codeChallenge, state, orgSsoIdentifier);
|
||||
ssoCode = ssoParams.ssoCode;
|
||||
orgIdentifier = ssoParams.orgIdentifier;
|
||||
} catch {
|
||||
@@ -664,6 +666,7 @@ export class LoginCommand {
|
||||
private async openSsoPrompt(
|
||||
codeChallenge: string,
|
||||
state: string,
|
||||
orgSsoIdentifier: string,
|
||||
): Promise<{ ssoCode: string; orgIdentifier: string }> {
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
|
||||
@@ -712,6 +715,8 @@ export class LoginCommand {
|
||||
this.ssoRedirectUri,
|
||||
state,
|
||||
codeChallenge,
|
||||
null,
|
||||
orgSsoIdentifier,
|
||||
);
|
||||
this.platformUtilsService.launchUri(webAppSsoUrl);
|
||||
});
|
||||
|
||||
@@ -118,7 +118,10 @@ export class Program extends BaseProgram {
|
||||
.description("Log into a user account.")
|
||||
.option("--method <method>", "Two-step login method.")
|
||||
.option("--code <code>", "Two-step login code.")
|
||||
.option("--sso", "Log in with Single-Sign On.")
|
||||
.option(
|
||||
"--sso [identifier]",
|
||||
"Log in with Single-Sign On with optional organization identifier.",
|
||||
)
|
||||
.option("--apikey", "Log in with an Api Key.")
|
||||
.option("--passwordenv <passwordenv>", "Environment variable storing your password")
|
||||
.option(
|
||||
|
||||
@@ -155,7 +155,14 @@ export class SsoComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
|
||||
// Detect if we have landed here but only have an SSO identifier in the URL.
|
||||
// Detect if we are on the first portion of the SSO flow
|
||||
// and have been sent here from another client with the info in query params.
|
||||
// If so, we want to initialize the SSO flow with those values.
|
||||
if (this.hasParametersFromOtherClientRedirect(qParams)) {
|
||||
this.initializeFromRedirectFromOtherClient(qParams);
|
||||
}
|
||||
|
||||
// Detect if we have landed here with an SSO identifier in the URL.
|
||||
// This is used by integrations that want to "short-circuit" the login to send users
|
||||
// directly to their IdP to simulate IdP-initiated SSO, so we submit automatically.
|
||||
if (qParams.identifier != null) {
|
||||
@@ -165,13 +172,6 @@ export class SsoComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
|
||||
// Detect if we are on the first portion of the SSO flow
|
||||
// and have been sent here from another client with the info in query params.
|
||||
// If so, we want to initialize the SSO flow with those values.
|
||||
if (this.hasParametersFromOtherClientRedirect(qParams)) {
|
||||
this.initializeFromRedirectFromOtherClient(qParams);
|
||||
}
|
||||
|
||||
// Try to determine the identifier using claimed domain or local state
|
||||
// persisted from the user's last login attempt.
|
||||
await this.initializeIdentifierFromEmailOrStorage();
|
||||
|
||||
@@ -92,4 +92,27 @@ describe("SsoUrlService", () => {
|
||||
);
|
||||
expect(result).toBe(expectedUrl);
|
||||
});
|
||||
|
||||
it("should build CLI SSO URL with Org SSO Identifier 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 orgSsoIdentifier = "test-org";
|
||||
|
||||
const expectedUrl = `${baseUrl}/#/sso?clientId=cli&redirectUri=${encodeURIComponent(redirectUri)}&state=${state}&codeChallenge=${codeChallenge}&email=${encodeURIComponent(email)}&identifier=${encodeURIComponent(orgSsoIdentifier)}`;
|
||||
|
||||
const result = service.buildSsoUrl(
|
||||
baseUrl,
|
||||
clientType,
|
||||
redirectUri,
|
||||
state,
|
||||
codeChallenge,
|
||||
email,
|
||||
orgSsoIdentifier,
|
||||
);
|
||||
expect(result).toBe(expectedUrl);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,6 +11,7 @@ export class SsoUrlService {
|
||||
* @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
|
||||
* @param orgSsoIdentifier The optional SSO identifier of the org that is initiating SSO
|
||||
* @returns The URL for redirecting users to the web app SSO component
|
||||
*/
|
||||
buildSsoUrl(
|
||||
@@ -20,6 +21,7 @@ export class SsoUrlService {
|
||||
state: string,
|
||||
codeChallenge: string,
|
||||
email?: string,
|
||||
orgSsoIdentifier?: string,
|
||||
): string {
|
||||
let url =
|
||||
webAppUrl +
|
||||
@@ -36,6 +38,10 @@ export class SsoUrlService {
|
||||
url += "&email=" + encodeURIComponent(email);
|
||||
}
|
||||
|
||||
if (orgSsoIdentifier) {
|
||||
url += "&identifier=" + encodeURIComponent(orgSsoIdentifier);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user