import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AppIdService } from "@bitwarden/common/abstractions/appId.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { StateService } from "@bitwarden/common/abstractions/state.service"; import { TokenService } from "@bitwarden/common/abstractions/token.service"; import { TwoFactorService } from "@bitwarden/common/abstractions/twoFactor.service"; import { SsoLogInStrategy } from "@bitwarden/common/misc/logInStrategies/ssoLogin.strategy"; import { Utils } from "@bitwarden/common/misc/utils"; import { SsoLogInCredentials } from "@bitwarden/common/models/domain/logInCredentials"; import { identityTokenResponseFactory } from "./logIn.strategy.spec"; describe("SsoLogInStrategy", () => { let cryptoService: SubstituteOf; let apiService: SubstituteOf; let tokenService: SubstituteOf; let appIdService: SubstituteOf; let platformUtilsService: SubstituteOf; let messagingService: SubstituteOf; let logService: SubstituteOf; let keyConnectorService: SubstituteOf; let stateService: SubstituteOf; let twoFactorService: SubstituteOf; let ssoLogInStrategy: SsoLogInStrategy; let credentials: SsoLogInCredentials; const deviceId = Utils.newGuid(); const encKey = "ENC_KEY"; const privateKey = "PRIVATE_KEY"; const keyConnectorUrl = "KEY_CONNECTOR_URL"; const ssoCode = "SSO_CODE"; const ssoCodeVerifier = "SSO_CODE_VERIFIER"; const ssoRedirectUrl = "SSO_REDIRECT_URL"; const ssoOrgId = "SSO_ORG_ID"; beforeEach(async () => { cryptoService = Substitute.for(); apiService = Substitute.for(); tokenService = Substitute.for(); appIdService = Substitute.for(); platformUtilsService = Substitute.for(); messagingService = Substitute.for(); logService = Substitute.for(); stateService = Substitute.for(); keyConnectorService = Substitute.for(); twoFactorService = Substitute.for(); tokenService.getTwoFactorToken().resolves(null); appIdService.getAppId().resolves(deviceId); ssoLogInStrategy = new SsoLogInStrategy( cryptoService, apiService, tokenService, appIdService, platformUtilsService, messagingService, logService, stateService, twoFactorService, keyConnectorService ); credentials = new SsoLogInCredentials(ssoCode, ssoCodeVerifier, ssoRedirectUrl, ssoOrgId); }); it("sends SSO information to server", async () => { apiService.postIdentityToken(Arg.any()).resolves(identityTokenResponseFactory()); await ssoLogInStrategy.logIn(credentials); apiService.received(1).postIdentityToken( Arg.is((actual) => { const ssoTokenRequest = actual as any; return ( ssoTokenRequest.code === ssoCode && ssoTokenRequest.codeVerifier === ssoCodeVerifier && ssoTokenRequest.redirectUri === ssoRedirectUrl && ssoTokenRequest.device.identifier === deviceId && ssoTokenRequest.twoFactor.provider == null && ssoTokenRequest.twoFactor.token == null ); }) ); }); it("does not set keys for new SSO user flow", async () => { const tokenResponse = identityTokenResponseFactory(); tokenResponse.key = null; apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); await ssoLogInStrategy.logIn(credentials); cryptoService.didNotReceive().setEncPrivateKey(privateKey); cryptoService.didNotReceive().setEncKey(encKey); }); it("gets and sets KeyConnector key for enrolled user", async () => { const tokenResponse = identityTokenResponseFactory(); tokenResponse.keyConnectorUrl = keyConnectorUrl; apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); await ssoLogInStrategy.logIn(credentials); keyConnectorService.received(1).getAndSetKey(keyConnectorUrl); }); it("converts new SSO user to Key Connector on first login", async () => { const tokenResponse = identityTokenResponseFactory(); tokenResponse.keyConnectorUrl = keyConnectorUrl; tokenResponse.key = null; apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); await ssoLogInStrategy.logIn(credentials); keyConnectorService.received(1).convertNewSsoUserToKeyConnector(tokenResponse, ssoOrgId); }); });