diff --git a/apps/browser/src/auth/popup/login/extension-login.service.ts b/apps/browser/src/auth/popup/login/extension-login.service.ts index c9f0594cee3..9b902084dfa 100644 --- a/apps/browser/src/auth/popup/login/extension-login.service.ts +++ b/apps/browser/src/auth/popup/login/extension-login.service.ts @@ -1,9 +1,73 @@ +import { inject } from "@angular/core"; +import { firstValueFrom } from "rxjs"; + import { DefaultLoginService, LoginService } from "@bitwarden/auth/angular"; +import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; +import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { flagEnabled } from "../../../platform/flags"; // TODO-rr-bw: do I need a client specific `flagEnabled()` fn? export class ExtensionLoginService extends DefaultLoginService implements LoginService { + ssoLoginService = inject(SsoLoginServiceAbstraction); + passwordGenerationService = inject(PasswordGenerationServiceAbstraction); + cryptoFunctionService = inject(CryptoFunctionService); + environmentService = inject(EnvironmentService); + platformUtilsService = inject(PlatformUtilsService); + getShowPasswordlessFlag(): boolean { return flagEnabled("showPasswordless"); } + + async launchSsoBrowserWindow(email: string): Promise { + // Save off email for SSO + await this.ssoLoginService.setSsoEmail(email); + + // Generate necessary sso params + const passwordOptions: any = { + type: "password", + length: 64, + uppercase: true, + lowercase: true, + numbers: true, + special: false, + }; + + const state = + (await this.passwordGenerationService.generatePassword(passwordOptions)) + + ":clientId=browser"; + const codeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions); + const codeVerifierHash = await this.cryptoFunctionService.hash(codeVerifier, "sha256"); + const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash); + + await this.ssoLoginService.setCodeVerifier(codeVerifier); + await this.ssoLoginService.setSsoState(state); + + const env = await firstValueFrom(this.environmentService.environment$); + + let url = env.getWebVaultUrl(); + + if (url == null) { + url = "https://vault.bitwarden.com"; + } + + const redirectUri = url + "/sso-connector.html"; + + // Launch browser + this.platformUtilsService.launchUri( + url + + "/#/sso?clientId=browser" + + "&redirectUri=" + + encodeURIComponent(redirectUri) + + "&state=" + + state + + "&codeChallenge=" + + codeChallenge + + "&email=" + + encodeURIComponent(email), + ); + } } diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 098c6eb91ce..34f9e691bf1 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -16,7 +16,7 @@ import { CLIENT_TYPE, } from "@bitwarden/angular/services/injection-tokens"; import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module"; -import { AnonLayoutWrapperDataService } from "@bitwarden/auth/angular"; +import { AnonLayoutWrapperDataService, LoginService } from "@bitwarden/auth/angular"; import { PinServiceAbstraction } from "@bitwarden/auth/common"; import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service"; import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; @@ -92,6 +92,7 @@ import { DialogService, ToastService } from "@bitwarden/components"; import { PasswordRepromptService } from "@bitwarden/vault"; import { ExtensionAnonLayoutWrapperDataService } from "../../auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper-data.service"; +import { ExtensionLoginService } from "../../auth/popup/login/extension-login.service"; import { AutofillService as AutofillServiceAbstraction } from "../../autofill/services/abstractions/autofill.service"; import AutofillService from "../../autofill/services/autofill.service"; import MainBackground from "../../background/main.background"; @@ -560,6 +561,11 @@ const safeProviders: SafeProvider[] = [ useClass: ExtensionAnonLayoutWrapperDataService, deps: [], }), + safeProvider({ + provide: LoginService, + useClass: ExtensionLoginService, + deps: [I18nServiceAbstraction, ToastService], + }), ]; @NgModule({ diff --git a/libs/auth/src/angular/login/default-login.service.ts b/libs/auth/src/angular/login/default-login.service.ts index 08eca726410..674c08e7cf0 100644 --- a/libs/auth/src/angular/login/default-login.service.ts +++ b/libs/auth/src/angular/login/default-login.service.ts @@ -11,6 +11,10 @@ export class DefaultLoginService implements LoginService { protected toastService: ToastService, ) {} + async launchSsoBrowserWindow(email: string): Promise { + return null; + } + getShowPasswordlessFlag(): boolean { return null; } diff --git a/libs/auth/src/angular/login/login.component.html b/libs/auth/src/angular/login/login.component.html index 75e8f1de86a..a52ca84f843 100644 --- a/libs/auth/src/angular/login/login.component.html +++ b/libs/auth/src/angular/login/login.component.html @@ -150,7 +150,13 @@ - diff --git a/libs/auth/src/angular/login/login.component.ts b/libs/auth/src/angular/login/login.component.ts index 01f9c23855f..f6dc4ccc317 100644 --- a/libs/auth/src/angular/login/login.component.ts +++ b/libs/auth/src/angular/login/login.component.ts @@ -16,14 +16,12 @@ import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.d import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; -import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; import { CaptchaIFrame } from "@bitwarden/common/auth/captcha-iframe"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { ClientType } from "@bitwarden/common/enums"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; -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 { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -40,7 +38,6 @@ import { IconButtonModule, ToastService, } from "@bitwarden/components"; -import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { AnonLayoutWrapperDataService } from "../anon-layout/anon-layout-wrapper-data.service"; import { WaveIcon } from "../icons"; @@ -120,7 +117,6 @@ export class LoginComponent implements OnInit, OnDestroy { private anonLayoutWrapperDataService: AnonLayoutWrapperDataService, private appIdService: AppIdService, private broadcasterService: BroadcasterService, - private cryptoFunctionService: CryptoFunctionService, private devicesApiService: DevicesApiServiceAbstraction, private environmentService: EnvironmentService, private formBuilder: FormBuilder, @@ -130,13 +126,11 @@ export class LoginComponent implements OnInit, OnDestroy { private loginStrategyService: LoginStrategyServiceAbstraction, private messagingService: MessagingService, private ngZone: NgZone, - private passwordGenerationService: PasswordGenerationServiceAbstraction, private passwordStrengthService: PasswordStrengthServiceAbstraction, private platformUtilsService: PlatformUtilsService, private policyService: InternalPolicyService, private registerRouteService: RegisterRouteService, private router: Router, - private ssoLoginService: SsoLoginServiceAbstraction, private syncService: SyncService, private toastService: ToastService, ) { @@ -285,51 +279,8 @@ export class LoginComponent implements OnInit, OnDestroy { } } - protected async launchSsoBrowser() { - // Save off email for SSO - await this.ssoLoginService.setSsoEmail(this.formGroup.value.email); - - // Generate necessary sso params - const passwordOptions: any = { - type: "password", - length: 64, - uppercase: true, - lowercase: true, - numbers: true, - special: false, - }; - - const state = - (await this.passwordGenerationService.generatePassword(passwordOptions)) + - ":clientId=browser"; - const codeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions); - const codeVerifierHash = await this.cryptoFunctionService.hash(codeVerifier, "sha256"); - const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash); - - await this.ssoLoginService.setCodeVerifier(codeVerifier); - await this.ssoLoginService.setSsoState(state); - - const env = await firstValueFrom(this.environmentService.environment$); - let url = env.getWebVaultUrl(); - if (url == null) { - url = "https://vault.bitwarden.com"; - } - - const redirectUri = url + "/sso-connector.html"; - - // Launch browser - this.platformUtilsService.launchUri( - url + - "/#/sso?clientId=browser" + - "&redirectUri=" + - encodeURIComponent(redirectUri) + - "&state=" + - state + - "&codeChallenge=" + - codeChallenge + - "&email=" + - encodeURIComponent(this.formGroup.controls.email.value), - ); + protected async launchSsoBrowserWindow(): Promise { + await this.loginService.launchSsoBrowserWindow(this.loggedEmail); } protected async goAfterLogIn(userId: UserId): Promise { diff --git a/libs/auth/src/angular/login/login.service.ts b/libs/auth/src/angular/login/login.service.ts index 9c9e59d5ddc..331ac92839f 100644 --- a/libs/auth/src/angular/login/login.service.ts +++ b/libs/auth/src/angular/login/login.service.ts @@ -10,7 +10,10 @@ export interface PasswordPolicies { } export abstract class LoginService { - // Web specific + // Browser/Desktop + launchSsoBrowserWindow: (email: string) => Promise; + + // Web getShowPasswordlessFlag: () => boolean; getOrgPolicies: () => Promise; setPreviousUrl: (route: UrlTree) => void | null;