mirror of
https://github.com/bitwarden/browser
synced 2025-12-21 18:53:29 +00:00
Auth/ps 2298 reorg auth (#4564)
* Move auth service factories to Auth team * Move authentication componenets to Auth team * Move auth guard services to Auth team * Move Duo content script to Auth team * Move auth CLI commands to Auth team * Move Desktop Account components to Auth Team * Move Desktop guards to Auth team * Move two-factor provider images to Auth team * Move web Accounts components to Auth Team * Move web settings components to Auth Team * Move web two factor images to Auth Team * Fix missed import changes for Auth Team * Fix Linting errors * Fix missed CLI imports * Fix missed Desktop imports * Revert images move * Fix missed imports in Web * Move angular lib components to Auth Team * Move angular auth guards to Auth team * Move strategy specs to Auth team * Update .eslintignore for new paths * Move lib common abstractions to Auth team * Move services to Auth team * Move common lib enums to Auth team * Move webauthn iframe to Auth team * Move lib common domain models to Auth team * Move common lib requests to Auth team * Move response models to Auth team * Clean up whitelist * Move bit web components to Auth team * Move SSO and SCIM files to Auth team * Revert move SCIM to Auth team SCIM belongs to Admin Console team * Move captcha to Auth team * Move key connector to Auth team * Move emergency access to auth team * Delete extra file * linter fixes * Move kdf config to auth team * Fix whitelist * Fix duo autoformat * Complete two factor provider request move * Fix whitelist names * Fix login capitalization * Revert hint dependency reordering * Revert hint dependency reordering * Revert hint component This components is being picked up as a move between clients * Move web hint component to Auth team * Move new files to auth team * Fix desktop build * Fix browser build
This commit is contained in:
@@ -1,26 +0,0 @@
|
||||
import { AccountApiService } from "../../abstractions/account/account-api.service";
|
||||
import { InternalAccountService } from "../../abstractions/account/account.service";
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { LogService } from "../../abstractions/log.service";
|
||||
import { UserVerificationService } from "../../abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { Verification } from "../../types/verification";
|
||||
|
||||
export class AccountApiServiceImplementation implements AccountApiService {
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private userVerificationService: UserVerificationService,
|
||||
private logService: LogService,
|
||||
private accountService: InternalAccountService
|
||||
) {}
|
||||
|
||||
async deleteAccount(verification: Verification): Promise<void> {
|
||||
try {
|
||||
const verificationRequest = await this.userVerificationService.buildRequest(verification);
|
||||
await this.apiService.send("DELETE", "/accounts", verificationRequest, true, false);
|
||||
this.accountService.delete();
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { InternalAccountService } from "../../abstractions/account/account.service";
|
||||
import { LogService } from "../../abstractions/log.service";
|
||||
import { MessagingService } from "../../abstractions/messaging.service";
|
||||
|
||||
export class AccountServiceImplementation implements InternalAccountService {
|
||||
constructor(private messagingService: MessagingService, private logService: LogService) {}
|
||||
|
||||
async delete(): Promise<void> {
|
||||
try {
|
||||
this.messagingService.send("logout");
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,9 @@ import {
|
||||
import { MessagePackHubProtocol } from "@microsoft/signalr-protocol-msgpack";
|
||||
|
||||
import { AnonymousHubService as AnonymousHubServiceAbstraction } from "../abstractions/anonymousHub.service";
|
||||
import { AuthService } from "../abstractions/auth.service";
|
||||
import { EnvironmentService } from "../abstractions/environment.service";
|
||||
import { LogService } from "../abstractions/log.service";
|
||||
import { AuthService } from "../auth/abstractions/auth.service";
|
||||
|
||||
import {
|
||||
AuthRequestPushNotification,
|
||||
|
||||
@@ -2,41 +2,79 @@ import { ApiService as ApiServiceAbstraction } from "../abstractions/api.service
|
||||
import { AppIdService } from "../abstractions/appId.service";
|
||||
import { EnvironmentService } from "../abstractions/environment.service";
|
||||
import { PlatformUtilsService } from "../abstractions/platformUtils.service";
|
||||
import { TokenService } from "../abstractions/token.service";
|
||||
import { TokenService } from "../auth/abstractions/token.service";
|
||||
import { DeviceVerificationRequest } from "../auth/models/request/device-verification.request";
|
||||
import { EmailTokenRequest } from "../auth/models/request/email-token.request";
|
||||
import { EmailRequest } from "../auth/models/request/email.request";
|
||||
import { EmergencyAccessAcceptRequest } from "../auth/models/request/emergency-access-accept.request";
|
||||
import { EmergencyAccessConfirmRequest } from "../auth/models/request/emergency-access-confirm.request";
|
||||
import { EmergencyAccessInviteRequest } from "../auth/models/request/emergency-access-invite.request";
|
||||
import { EmergencyAccessPasswordRequest } from "../auth/models/request/emergency-access-password.request";
|
||||
import { EmergencyAccessUpdateRequest } from "../auth/models/request/emergency-access-update.request";
|
||||
import { DeviceRequest } from "../auth/models/request/identity-token/device.request";
|
||||
import { PasswordTokenRequest } from "../auth/models/request/identity-token/password-token.request";
|
||||
import { SsoTokenRequest } from "../auth/models/request/identity-token/sso-token.request";
|
||||
import { TokenTwoFactorRequest } from "../auth/models/request/identity-token/token-two-factor.request";
|
||||
import { UserApiTokenRequest } from "../auth/models/request/identity-token/user-api-token.request";
|
||||
import { KeyConnectorUserKeyRequest } from "../auth/models/request/key-connector-user-key.request";
|
||||
import { PasswordHintRequest } from "../auth/models/request/password-hint.request";
|
||||
import { PasswordRequest } from "../auth/models/request/password.request";
|
||||
import { PasswordlessAuthRequest } from "../auth/models/request/passwordless-auth.request";
|
||||
import { PasswordlessCreateAuthRequest } from "../auth/models/request/passwordless-create-auth.request";
|
||||
import { SecretVerificationRequest } from "../auth/models/request/secret-verification.request";
|
||||
import { SetKeyConnectorKeyRequest } from "../auth/models/request/set-key-connector-key.request";
|
||||
import { SetPasswordRequest } from "../auth/models/request/set-password.request";
|
||||
import { TwoFactorEmailRequest } from "../auth/models/request/two-factor-email.request";
|
||||
import { TwoFactorProviderRequest } from "../auth/models/request/two-factor-provider.request";
|
||||
import { TwoFactorRecoveryRequest } from "../auth/models/request/two-factor-recovery.request";
|
||||
import { UpdateProfileRequest } from "../auth/models/request/update-profile.request";
|
||||
import { UpdateTwoFactorAuthenticatorRequest } from "../auth/models/request/update-two-factor-authenticator.request";
|
||||
import { UpdateTwoFactorDuoRequest } from "../auth/models/request/update-two-factor-duo.request";
|
||||
import { UpdateTwoFactorEmailRequest } from "../auth/models/request/update-two-factor-email.request";
|
||||
import { UpdateTwoFactorWebAuthnDeleteRequest } from "../auth/models/request/update-two-factor-web-authn-delete.request";
|
||||
import { UpdateTwoFactorWebAuthnRequest } from "../auth/models/request/update-two-factor-web-authn.request";
|
||||
import { UpdateTwoFactorYubioOtpRequest } from "../auth/models/request/update-two-factor-yubio-otp.request";
|
||||
import { ApiKeyResponse } from "../auth/models/response/api-key.response";
|
||||
import { AuthRequestResponse } from "../auth/models/response/auth-request.response";
|
||||
import { DeviceVerificationResponse } from "../auth/models/response/device-verification.response";
|
||||
import {
|
||||
EmergencyAccessGranteeDetailsResponse,
|
||||
EmergencyAccessGrantorDetailsResponse,
|
||||
EmergencyAccessTakeoverResponse,
|
||||
EmergencyAccessViewResponse,
|
||||
} from "../auth/models/response/emergency-access.response";
|
||||
import { IdentityCaptchaResponse } from "../auth/models/response/identity-captcha.response";
|
||||
import { IdentityTokenResponse } from "../auth/models/response/identity-token.response";
|
||||
import { IdentityTwoFactorResponse } from "../auth/models/response/identity-two-factor.response";
|
||||
import { KeyConnectorUserKeyResponse } from "../auth/models/response/key-connector-user-key.response";
|
||||
import { PreloginResponse } from "../auth/models/response/prelogin.response";
|
||||
import { RegisterResponse } from "../auth/models/response/register.response";
|
||||
import { SsoPreValidateResponse } from "../auth/models/response/sso-pre-validate.response";
|
||||
import { TwoFactorAuthenticatorResponse } from "../auth/models/response/two-factor-authenticator.response";
|
||||
import { TwoFactorDuoResponse } from "../auth/models/response/two-factor-duo.response";
|
||||
import { TwoFactorEmailResponse } from "../auth/models/response/two-factor-email.response";
|
||||
import { TwoFactorProviderResponse } from "../auth/models/response/two-factor-provider.response";
|
||||
import { TwoFactorRecoverResponse } from "../auth/models/response/two-factor-recover.response";
|
||||
import {
|
||||
ChallengeResponse,
|
||||
TwoFactorWebAuthnResponse,
|
||||
} from "../auth/models/response/two-factor-web-authn.response";
|
||||
import { TwoFactorYubiKeyResponse } from "../auth/models/response/two-factor-yubi-key.response";
|
||||
import { DeviceType } from "../enums/deviceType";
|
||||
import { OrganizationConnectionType } from "../enums/organizationConnectionType";
|
||||
import { Utils } from "../misc/utils";
|
||||
import { SetKeyConnectorKeyRequest } from "../models/request/account/set-key-connector-key.request";
|
||||
import { BitPayInvoiceRequest } from "../models/request/bit-pay-invoice.request";
|
||||
import { CollectionBulkDeleteRequest } from "../models/request/collection-bulk-delete.request";
|
||||
import { CollectionRequest } from "../models/request/collection.request";
|
||||
import { DeleteRecoverRequest } from "../models/request/delete-recover.request";
|
||||
import { DeviceVerificationRequest } from "../models/request/device-verification.request";
|
||||
import { DeviceRequest } from "../models/request/device.request";
|
||||
import { EmailTokenRequest } from "../models/request/email-token.request";
|
||||
import { EmailRequest } from "../models/request/email.request";
|
||||
import { EmergencyAccessAcceptRequest } from "../models/request/emergency-access-accept.request";
|
||||
import { EmergencyAccessConfirmRequest } from "../models/request/emergency-access-confirm.request";
|
||||
import { EmergencyAccessInviteRequest } from "../models/request/emergency-access-invite.request";
|
||||
import { EmergencyAccessPasswordRequest } from "../models/request/emergency-access-password.request";
|
||||
import { EmergencyAccessUpdateRequest } from "../models/request/emergency-access-update.request";
|
||||
import { EventRequest } from "../models/request/event.request";
|
||||
import { IapCheckRequest } from "../models/request/iap-check.request";
|
||||
import { PasswordTokenRequest } from "../models/request/identity-token/password-token.request";
|
||||
import { SsoTokenRequest } from "../models/request/identity-token/sso-token.request";
|
||||
import { TokenTwoFactorRequest } from "../models/request/identity-token/token-two-factor.request";
|
||||
import { UserApiTokenRequest } from "../models/request/identity-token/user-api-token.request";
|
||||
import { KdfRequest } from "../models/request/kdf.request";
|
||||
import { KeyConnectorUserKeyRequest } from "../models/request/key-connector-user-key.request";
|
||||
import { KeysRequest } from "../models/request/keys.request";
|
||||
import { OrganizationConnectionRequest } from "../models/request/organization-connection.request";
|
||||
import { OrganizationImportRequest } from "../models/request/organization-import.request";
|
||||
import { OrganizationSponsorshipCreateRequest } from "../models/request/organization/organization-sponsorship-create.request";
|
||||
import { OrganizationSponsorshipRedeemRequest } from "../models/request/organization/organization-sponsorship-redeem.request";
|
||||
import { PasswordHintRequest } from "../models/request/password-hint.request";
|
||||
import { PasswordRequest } from "../models/request/password.request";
|
||||
import { PasswordlessAuthRequest } from "../models/request/passwordless-auth.request";
|
||||
import { PasswordlessCreateAuthRequest } from "../models/request/passwordless-create-auth.request";
|
||||
import { PaymentRequest } from "../models/request/payment.request";
|
||||
import { PreloginRequest } from "../models/request/prelogin.request";
|
||||
import { ProviderAddOrganizationRequest } from "../models/request/provider/provider-add-organization.request";
|
||||
@@ -50,32 +88,17 @@ import { ProviderUserConfirmRequest } from "../models/request/provider/provider-
|
||||
import { ProviderUserInviteRequest } from "../models/request/provider/provider-user-invite.request";
|
||||
import { ProviderUserUpdateRequest } from "../models/request/provider/provider-user-update.request";
|
||||
import { RegisterRequest } from "../models/request/register.request";
|
||||
import { SecretVerificationRequest } from "../models/request/secret-verification.request";
|
||||
import { SelectionReadOnlyRequest } from "../models/request/selection-read-only.request";
|
||||
import { SendAccessRequest } from "../models/request/send-access.request";
|
||||
import { SendRequest } from "../models/request/send.request";
|
||||
import { SetPasswordRequest } from "../models/request/set-password.request";
|
||||
import { StorageRequest } from "../models/request/storage.request";
|
||||
import { TaxInfoUpdateRequest } from "../models/request/tax-info-update.request";
|
||||
import { TwoFactorEmailRequest } from "../models/request/two-factor-email.request";
|
||||
import { TwoFactorProviderRequest } from "../models/request/two-factor-provider.request";
|
||||
import { TwoFactorRecoveryRequest } from "../models/request/two-factor-recovery.request";
|
||||
import { UpdateAvatarRequest } from "../models/request/update-avatar.request";
|
||||
import { UpdateDomainsRequest } from "../models/request/update-domains.request";
|
||||
import { UpdateKeyRequest } from "../models/request/update-key.request";
|
||||
import { UpdateProfileRequest } from "../models/request/update-profile.request";
|
||||
import { UpdateTempPasswordRequest } from "../models/request/update-temp-password.request";
|
||||
import { UpdateTwoFactorAuthenticatorRequest } from "../models/request/update-two-factor-authenticator.request";
|
||||
import { UpdateTwoFactorDuoRequest } from "../models/request/update-two-factor-duo.request";
|
||||
import { UpdateTwoFactorEmailRequest } from "../models/request/update-two-factor-email.request";
|
||||
import { UpdateTwoFactorWebAuthnDeleteRequest } from "../models/request/update-two-factor-web-authn-delete.request";
|
||||
import { UpdateTwoFactorWebAuthnRequest } from "../models/request/update-two-factor-web-authn.request";
|
||||
import { UpdateTwoFactorYubioOtpRequest } from "../models/request/update-two-factor-yubio-otp.request";
|
||||
import { VerifyDeleteRecoverRequest } from "../models/request/verify-delete-recover.request";
|
||||
import { VerifyEmailRequest } from "../models/request/verify-email.request";
|
||||
import { ApiKeyResponse } from "../models/response/api-key.response";
|
||||
import { AuthRequestResponse } from "../models/response/auth-request.response";
|
||||
import { RegisterResponse } from "../models/response/authentication/register.response";
|
||||
import { BillingHistoryResponse } from "../models/response/billing-history.response";
|
||||
import { BillingPaymentResponse } from "../models/response/billing-payment.response";
|
||||
import { BreachAccountResponse } from "../models/response/breach-account.response";
|
||||
@@ -83,20 +106,9 @@ import {
|
||||
CollectionAccessDetailsResponse,
|
||||
CollectionResponse,
|
||||
} from "../models/response/collection.response";
|
||||
import { DeviceVerificationResponse } from "../models/response/device-verification.response";
|
||||
import { DomainsResponse } from "../models/response/domains.response";
|
||||
import {
|
||||
EmergencyAccessGranteeDetailsResponse,
|
||||
EmergencyAccessGrantorDetailsResponse,
|
||||
EmergencyAccessTakeoverResponse,
|
||||
EmergencyAccessViewResponse,
|
||||
} from "../models/response/emergency-access.response";
|
||||
import { ErrorResponse } from "../models/response/error.response";
|
||||
import { EventResponse } from "../models/response/event.response";
|
||||
import { IdentityCaptchaResponse } from "../models/response/identity-captcha.response";
|
||||
import { IdentityTokenResponse } from "../models/response/identity-token.response";
|
||||
import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response";
|
||||
import { KeyConnectorUserKeyResponse } from "../models/response/key-connector-user-key.response";
|
||||
import { ListResponse } from "../models/response/list.response";
|
||||
import {
|
||||
OrganizationConnectionConfigApis,
|
||||
@@ -107,7 +119,6 @@ import { OrganizationSponsorshipSyncStatusResponse } from "../models/response/or
|
||||
import { PaymentResponse } from "../models/response/payment.response";
|
||||
import { PlanResponse } from "../models/response/plan.response";
|
||||
import { PolicyResponse } from "../models/response/policy.response";
|
||||
import { PreloginResponse } from "../models/response/prelogin.response";
|
||||
import { ProfileResponse } from "../models/response/profile.response";
|
||||
import {
|
||||
ProviderOrganizationOrganizationDetailsResponse,
|
||||
@@ -125,20 +136,9 @@ import { SendAccessResponse } from "../models/response/send-access.response";
|
||||
import { SendFileDownloadDataResponse } from "../models/response/send-file-download-data.response";
|
||||
import { SendFileUploadDataResponse } from "../models/response/send-file-upload-data.response";
|
||||
import { SendResponse } from "../models/response/send.response";
|
||||
import { SsoPreValidateResponse } from "../models/response/sso-pre-validate.response";
|
||||
import { SubscriptionResponse } from "../models/response/subscription.response";
|
||||
import { TaxInfoResponse } from "../models/response/tax-info.response";
|
||||
import { TaxRateResponse } from "../models/response/tax-rate.response";
|
||||
import { TwoFactorAuthenticatorResponse } from "../models/response/two-factor-authenticator.response";
|
||||
import { TwoFactorDuoResponse } from "../models/response/two-factor-duo.response";
|
||||
import { TwoFactorEmailResponse } from "../models/response/two-factor-email.response";
|
||||
import { TwoFactorProviderResponse } from "../models/response/two-factor-provider.response";
|
||||
import { TwoFactorRecoverResponse } from "../models/response/two-factor-recover.response";
|
||||
import {
|
||||
ChallengeResponse,
|
||||
TwoFactorWebAuthnResponse,
|
||||
} from "../models/response/two-factor-web-authn.response";
|
||||
import { TwoFactorYubiKeyResponse } from "../models/response/two-factor-yubi-key.response";
|
||||
import { UserKeyResponse } from "../models/response/user-key.response";
|
||||
import { SendAccessView } from "../models/view/send-access.view";
|
||||
import { AttachmentRequest } from "../vault/models/request/attachment.request";
|
||||
|
||||
@@ -1,334 +0,0 @@
|
||||
import { Observable, Subject } from "rxjs";
|
||||
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
import { AppIdService } from "../abstractions/appId.service";
|
||||
import { AuthService as AuthServiceAbstraction } from "../abstractions/auth.service";
|
||||
import { CryptoService } from "../abstractions/crypto.service";
|
||||
import { EncryptService } from "../abstractions/encrypt.service";
|
||||
import { EnvironmentService } from "../abstractions/environment.service";
|
||||
import { I18nService } from "../abstractions/i18n.service";
|
||||
import { KeyConnectorService } from "../abstractions/keyConnector.service";
|
||||
import { LogService } from "../abstractions/log.service";
|
||||
import { MessagingService } from "../abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "../abstractions/platformUtils.service";
|
||||
import { StateService } from "../abstractions/state.service";
|
||||
import { TokenService } from "../abstractions/token.service";
|
||||
import { TwoFactorService } from "../abstractions/twoFactor.service";
|
||||
import { AuthenticationStatus } from "../enums/authenticationStatus";
|
||||
import { AuthenticationType } from "../enums/authenticationType";
|
||||
import { KdfType } from "../enums/kdfType";
|
||||
import { KeySuffixOptions } from "../enums/keySuffixOptions";
|
||||
import { PasswordLogInStrategy } from "../misc/logInStrategies/passwordLogin.strategy";
|
||||
import { PasswordlessLogInStrategy } from "../misc/logInStrategies/passwordlessLogin.strategy";
|
||||
import { SsoLogInStrategy } from "../misc/logInStrategies/ssoLogin.strategy";
|
||||
import { UserApiLogInStrategy } from "../misc/logInStrategies/user-api-login.strategy";
|
||||
import { Utils } from "../misc/utils";
|
||||
import { AuthResult } from "../models/domain/auth-result";
|
||||
import { KdfConfig } from "../models/domain/kdf-config";
|
||||
import {
|
||||
UserApiLogInCredentials,
|
||||
PasswordLogInCredentials,
|
||||
SsoLogInCredentials,
|
||||
PasswordlessLogInCredentials,
|
||||
} from "../models/domain/log-in-credentials";
|
||||
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
|
||||
import { TokenTwoFactorRequest } from "../models/request/identity-token/token-two-factor.request";
|
||||
import { PasswordlessAuthRequest } from "../models/request/passwordless-auth.request";
|
||||
import { PreloginRequest } from "../models/request/prelogin.request";
|
||||
import { AuthRequestResponse } from "../models/response/auth-request.response";
|
||||
import { ErrorResponse } from "../models/response/error.response";
|
||||
import { AuthRequestPushNotification } from "../models/response/notification.response";
|
||||
|
||||
const sessionTimeoutLength = 2 * 60 * 1000; // 2 minutes
|
||||
|
||||
export class AuthService implements AuthServiceAbstraction {
|
||||
get email(): string {
|
||||
if (
|
||||
this.logInStrategy instanceof PasswordLogInStrategy ||
|
||||
this.logInStrategy instanceof PasswordlessLogInStrategy
|
||||
) {
|
||||
return this.logInStrategy.email;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
get masterPasswordHash(): string {
|
||||
return this.logInStrategy instanceof PasswordLogInStrategy
|
||||
? this.logInStrategy.masterPasswordHash
|
||||
: null;
|
||||
}
|
||||
|
||||
get accessCode(): string {
|
||||
return this.logInStrategy instanceof PasswordlessLogInStrategy
|
||||
? this.logInStrategy.accessCode
|
||||
: null;
|
||||
}
|
||||
|
||||
get authRequestId(): string {
|
||||
return this.logInStrategy instanceof PasswordlessLogInStrategy
|
||||
? this.logInStrategy.authRequestId
|
||||
: null;
|
||||
}
|
||||
|
||||
private logInStrategy:
|
||||
| UserApiLogInStrategy
|
||||
| PasswordLogInStrategy
|
||||
| SsoLogInStrategy
|
||||
| PasswordlessLogInStrategy;
|
||||
private sessionTimeout: any;
|
||||
|
||||
private pushNotificationSubject = new Subject<string>();
|
||||
|
||||
constructor(
|
||||
protected cryptoService: CryptoService,
|
||||
protected apiService: ApiService,
|
||||
protected tokenService: TokenService,
|
||||
protected appIdService: AppIdService,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected messagingService: MessagingService,
|
||||
protected logService: LogService,
|
||||
protected keyConnectorService: KeyConnectorService,
|
||||
protected environmentService: EnvironmentService,
|
||||
protected stateService: StateService,
|
||||
protected twoFactorService: TwoFactorService,
|
||||
protected i18nService: I18nService,
|
||||
protected encryptService: EncryptService
|
||||
) {}
|
||||
|
||||
async logIn(
|
||||
credentials:
|
||||
| UserApiLogInCredentials
|
||||
| PasswordLogInCredentials
|
||||
| SsoLogInCredentials
|
||||
| PasswordlessLogInCredentials
|
||||
): Promise<AuthResult> {
|
||||
this.clearState();
|
||||
|
||||
let strategy:
|
||||
| UserApiLogInStrategy
|
||||
| PasswordLogInStrategy
|
||||
| SsoLogInStrategy
|
||||
| PasswordlessLogInStrategy;
|
||||
|
||||
switch (credentials.type) {
|
||||
case AuthenticationType.Password:
|
||||
strategy = new PasswordLogInStrategy(
|
||||
this.cryptoService,
|
||||
this.apiService,
|
||||
this.tokenService,
|
||||
this.appIdService,
|
||||
this.platformUtilsService,
|
||||
this.messagingService,
|
||||
this.logService,
|
||||
this.stateService,
|
||||
this.twoFactorService,
|
||||
this
|
||||
);
|
||||
break;
|
||||
case AuthenticationType.Sso:
|
||||
strategy = new SsoLogInStrategy(
|
||||
this.cryptoService,
|
||||
this.apiService,
|
||||
this.tokenService,
|
||||
this.appIdService,
|
||||
this.platformUtilsService,
|
||||
this.messagingService,
|
||||
this.logService,
|
||||
this.stateService,
|
||||
this.twoFactorService,
|
||||
this.keyConnectorService
|
||||
);
|
||||
break;
|
||||
case AuthenticationType.UserApi:
|
||||
strategy = new UserApiLogInStrategy(
|
||||
this.cryptoService,
|
||||
this.apiService,
|
||||
this.tokenService,
|
||||
this.appIdService,
|
||||
this.platformUtilsService,
|
||||
this.messagingService,
|
||||
this.logService,
|
||||
this.stateService,
|
||||
this.twoFactorService,
|
||||
this.environmentService,
|
||||
this.keyConnectorService
|
||||
);
|
||||
break;
|
||||
case AuthenticationType.Passwordless:
|
||||
strategy = new PasswordlessLogInStrategy(
|
||||
this.cryptoService,
|
||||
this.apiService,
|
||||
this.tokenService,
|
||||
this.appIdService,
|
||||
this.platformUtilsService,
|
||||
this.messagingService,
|
||||
this.logService,
|
||||
this.stateService,
|
||||
this.twoFactorService,
|
||||
this
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
const result = await strategy.logIn(credentials as any);
|
||||
|
||||
if (result?.requiresTwoFactor) {
|
||||
this.saveState(strategy);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async logInTwoFactor(
|
||||
twoFactor: TokenTwoFactorRequest,
|
||||
captchaResponse: string
|
||||
): Promise<AuthResult> {
|
||||
if (this.logInStrategy == null) {
|
||||
throw new Error(this.i18nService.t("sessionTimeout"));
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await this.logInStrategy.logInTwoFactor(twoFactor, captchaResponse);
|
||||
|
||||
// Only clear state if 2FA token has been accepted, otherwise we need to be able to try again
|
||||
if (!result.requiresTwoFactor && !result.requiresCaptcha) {
|
||||
this.clearState();
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
// API exceptions are okay, but if there are any unhandled client-side errors then clear state to be safe
|
||||
if (!(e instanceof ErrorResponse)) {
|
||||
this.clearState();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
logOut(callback: () => void) {
|
||||
callback();
|
||||
this.messagingService.send("loggedOut");
|
||||
}
|
||||
|
||||
authingWithUserApiKey(): boolean {
|
||||
return this.logInStrategy instanceof UserApiLogInStrategy;
|
||||
}
|
||||
|
||||
authingWithSso(): boolean {
|
||||
return this.logInStrategy instanceof SsoLogInStrategy;
|
||||
}
|
||||
|
||||
authingWithPassword(): boolean {
|
||||
return this.logInStrategy instanceof PasswordLogInStrategy;
|
||||
}
|
||||
|
||||
authingWithPasswordless(): boolean {
|
||||
return this.logInStrategy instanceof PasswordlessLogInStrategy;
|
||||
}
|
||||
|
||||
async getAuthStatus(userId?: string): Promise<AuthenticationStatus> {
|
||||
const isAuthenticated = await this.stateService.getIsAuthenticated({ userId: userId });
|
||||
if (!isAuthenticated) {
|
||||
return AuthenticationStatus.LoggedOut;
|
||||
}
|
||||
|
||||
// Keys aren't stored for a device that is locked or logged out
|
||||
// Make sure we're logged in before checking this, otherwise we could mix up those states
|
||||
const neverLock =
|
||||
(await this.cryptoService.hasKeyStored(KeySuffixOptions.Auto, userId)) &&
|
||||
!(await this.stateService.getEverBeenUnlocked({ userId: userId }));
|
||||
if (neverLock) {
|
||||
// TODO: This also _sets_ the key so when we check memory in the next line it finds a key.
|
||||
// We should refactor here.
|
||||
await this.cryptoService.getKey(KeySuffixOptions.Auto, userId);
|
||||
}
|
||||
|
||||
const hasKeyInMemory = await this.cryptoService.hasKeyInMemory(userId);
|
||||
if (!hasKeyInMemory) {
|
||||
return AuthenticationStatus.Locked;
|
||||
}
|
||||
|
||||
return AuthenticationStatus.Unlocked;
|
||||
}
|
||||
|
||||
async makePreloginKey(masterPassword: string, email: string): Promise<SymmetricCryptoKey> {
|
||||
email = email.trim().toLowerCase();
|
||||
let kdf: KdfType = null;
|
||||
let kdfConfig: KdfConfig = null;
|
||||
try {
|
||||
const preloginResponse = await this.apiService.postPrelogin(new PreloginRequest(email));
|
||||
if (preloginResponse != null) {
|
||||
kdf = preloginResponse.kdf;
|
||||
kdfConfig = new KdfConfig(
|
||||
preloginResponse.kdfIterations,
|
||||
preloginResponse.kdfMemory,
|
||||
preloginResponse.kdfParallelism
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e == null || e.statusCode !== 404) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return this.cryptoService.makeKey(masterPassword, email, kdf, kdfConfig);
|
||||
}
|
||||
|
||||
async authResponsePushNotifiction(notification: AuthRequestPushNotification): Promise<any> {
|
||||
this.pushNotificationSubject.next(notification.id);
|
||||
}
|
||||
|
||||
getPushNotifcationObs$(): Observable<any> {
|
||||
return this.pushNotificationSubject.asObservable();
|
||||
}
|
||||
|
||||
async passwordlessLogin(
|
||||
id: string,
|
||||
key: string,
|
||||
requestApproved: boolean
|
||||
): Promise<AuthRequestResponse> {
|
||||
const pubKey = Utils.fromB64ToArray(key);
|
||||
const encryptedKey = await this.cryptoService.rsaEncrypt(
|
||||
(
|
||||
await this.cryptoService.getKey()
|
||||
).encKey,
|
||||
pubKey.buffer
|
||||
);
|
||||
const encryptedMasterPassword = await this.cryptoService.rsaEncrypt(
|
||||
Utils.fromUtf8ToArray(await this.stateService.getKeyHash()),
|
||||
pubKey.buffer
|
||||
);
|
||||
const request = new PasswordlessAuthRequest(
|
||||
encryptedKey.encryptedString,
|
||||
encryptedMasterPassword.encryptedString,
|
||||
await this.appIdService.getAppId(),
|
||||
requestApproved
|
||||
);
|
||||
return await this.apiService.putAuthRequest(id, request);
|
||||
}
|
||||
|
||||
private saveState(
|
||||
strategy:
|
||||
| UserApiLogInStrategy
|
||||
| PasswordLogInStrategy
|
||||
| SsoLogInStrategy
|
||||
| PasswordlessLogInStrategy
|
||||
) {
|
||||
this.logInStrategy = strategy;
|
||||
this.startSessionTimeout();
|
||||
}
|
||||
|
||||
private clearState() {
|
||||
this.logInStrategy = null;
|
||||
this.clearSessionTimeout();
|
||||
}
|
||||
|
||||
private startSessionTimeout() {
|
||||
this.clearSessionTimeout();
|
||||
this.sessionTimeout = setTimeout(() => this.clearState(), sessionTimeoutLength);
|
||||
}
|
||||
|
||||
private clearSessionTimeout() {
|
||||
if (this.sessionTimeout != null) {
|
||||
clearTimeout(this.sessionTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { EncryptService } from "../abstractions/encrypt.service";
|
||||
import { LogService } from "../abstractions/log.service";
|
||||
import { PlatformUtilsService } from "../abstractions/platformUtils.service";
|
||||
import { StateService } from "../abstractions/state.service";
|
||||
import { KdfConfig } from "../auth/models/domain/kdf-config";
|
||||
import { EncryptionType } from "../enums/encryptionType";
|
||||
import { HashPurpose } from "../enums/hashPurpose";
|
||||
import {
|
||||
@@ -22,7 +23,6 @@ import { EncryptedOrganizationKeyData } from "../models/data/encrypted-organizat
|
||||
import { EncArrayBuffer } from "../models/domain/enc-array-buffer";
|
||||
import { EncString } from "../models/domain/enc-string";
|
||||
import { BaseEncryptedOrganizationKey } from "../models/domain/encrypted-organization-key";
|
||||
import { KdfConfig } from "../models/domain/kdf-config";
|
||||
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
|
||||
import { ProfileOrganizationResponse } from "../models/response/profile-organization.response";
|
||||
import { ProfileProviderOrganizationResponse } from "../models/response/profile-provider-organization.response";
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
Urls,
|
||||
} from "../abstractions/environment.service";
|
||||
import { StateService } from "../abstractions/state.service";
|
||||
import { EnvironmentUrls } from "../models/domain/environment-urls";
|
||||
import { EnvironmentUrls } from "../auth/models/domain/environment-urls";
|
||||
|
||||
export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
private readonly urlsSubject = new Subject<Urls>();
|
||||
|
||||
@@ -7,11 +7,11 @@ import {
|
||||
ExportFormat,
|
||||
ExportService as ExportServiceAbstraction,
|
||||
} from "../abstractions/export.service";
|
||||
import { KdfConfig } from "../auth/models/domain/kdf-config";
|
||||
import { DEFAULT_PBKDF2_ITERATIONS, KdfType } from "../enums/kdfType";
|
||||
import { Utils } from "../misc/utils";
|
||||
import { CollectionData } from "../models/data/collection.data";
|
||||
import { Collection } from "../models/domain/collection";
|
||||
import { KdfConfig } from "../models/domain/kdf-config";
|
||||
import { CipherWithIdExport as CipherExport } from "../models/export/cipher-with-ids.export";
|
||||
import { CollectionWithIdExport as CollectionExport } from "../models/export/collection-with-id.export";
|
||||
import { EventExport } from "../models/export/event.export";
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
import { CryptoService } from "../abstractions/crypto.service";
|
||||
import { CryptoFunctionService } from "../abstractions/cryptoFunction.service";
|
||||
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "../abstractions/keyConnector.service";
|
||||
import { LogService } from "../abstractions/log.service";
|
||||
import { OrganizationService } from "../abstractions/organization/organization.service.abstraction";
|
||||
import { StateService } from "../abstractions/state.service";
|
||||
import { TokenService } from "../abstractions/token.service";
|
||||
import { OrganizationUserType } from "../enums/organizationUserType";
|
||||
import { Utils } from "../misc/utils";
|
||||
import { KdfConfig } from "../models/domain/kdf-config";
|
||||
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
|
||||
import { SetKeyConnectorKeyRequest } from "../models/request/account/set-key-connector-key.request";
|
||||
import { KeyConnectorUserKeyRequest } from "../models/request/key-connector-user-key.request";
|
||||
import { KeysRequest } from "../models/request/keys.request";
|
||||
import { IdentityTokenResponse } from "../models/response/identity-token.response";
|
||||
|
||||
export class KeyConnectorService implements KeyConnectorServiceAbstraction {
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private cryptoService: CryptoService,
|
||||
private apiService: ApiService,
|
||||
private tokenService: TokenService,
|
||||
private logService: LogService,
|
||||
private organizationService: OrganizationService,
|
||||
private cryptoFunctionService: CryptoFunctionService,
|
||||
private logoutCallback: (expired: boolean, userId?: string) => void
|
||||
) {}
|
||||
|
||||
setUsesKeyConnector(usesKeyConnector: boolean) {
|
||||
return this.stateService.setUsesKeyConnector(usesKeyConnector);
|
||||
}
|
||||
|
||||
async getUsesKeyConnector(): Promise<boolean> {
|
||||
return await this.stateService.getUsesKeyConnector();
|
||||
}
|
||||
|
||||
async userNeedsMigration() {
|
||||
const loggedInUsingSso = await this.tokenService.getIsExternal();
|
||||
const requiredByOrganization = (await this.getManagingOrganization()) != null;
|
||||
const userIsNotUsingKeyConnector = !(await this.getUsesKeyConnector());
|
||||
|
||||
return loggedInUsingSso && requiredByOrganization && userIsNotUsingKeyConnector;
|
||||
}
|
||||
|
||||
async migrateUser() {
|
||||
const organization = await this.getManagingOrganization();
|
||||
const key = await this.cryptoService.getKey();
|
||||
const keyConnectorRequest = new KeyConnectorUserKeyRequest(key.encKeyB64);
|
||||
|
||||
try {
|
||||
await this.apiService.postUserKeyToKeyConnector(
|
||||
organization.keyConnectorUrl,
|
||||
keyConnectorRequest
|
||||
);
|
||||
} catch (e) {
|
||||
this.handleKeyConnectorError(e);
|
||||
}
|
||||
|
||||
await this.apiService.postConvertToKeyConnector();
|
||||
}
|
||||
|
||||
async getAndSetKey(url: string) {
|
||||
try {
|
||||
const userKeyResponse = await this.apiService.getUserKeyFromKeyConnector(url);
|
||||
const keyArr = Utils.fromB64ToArray(userKeyResponse.key);
|
||||
const k = new SymmetricCryptoKey(keyArr);
|
||||
await this.cryptoService.setKey(k);
|
||||
} catch (e) {
|
||||
this.handleKeyConnectorError(e);
|
||||
}
|
||||
}
|
||||
|
||||
async getManagingOrganization() {
|
||||
const orgs = await this.organizationService.getAll();
|
||||
return orgs.find(
|
||||
(o) =>
|
||||
o.keyConnectorEnabled &&
|
||||
o.type !== OrganizationUserType.Admin &&
|
||||
o.type !== OrganizationUserType.Owner &&
|
||||
!o.isProviderUser
|
||||
);
|
||||
}
|
||||
|
||||
async convertNewSsoUserToKeyConnector(tokenResponse: IdentityTokenResponse, orgId: string) {
|
||||
const { kdf, kdfIterations, kdfMemory, kdfParallelism, keyConnectorUrl } = tokenResponse;
|
||||
const password = await this.cryptoFunctionService.randomBytes(64);
|
||||
const kdfConfig = new KdfConfig(kdfIterations, kdfMemory, kdfParallelism);
|
||||
|
||||
const k = await this.cryptoService.makeKey(
|
||||
Utils.fromBufferToB64(password),
|
||||
await this.tokenService.getEmail(),
|
||||
kdf,
|
||||
kdfConfig
|
||||
);
|
||||
const keyConnectorRequest = new KeyConnectorUserKeyRequest(k.encKeyB64);
|
||||
await this.cryptoService.setKey(k);
|
||||
|
||||
const encKey = await this.cryptoService.makeEncKey(k);
|
||||
await this.cryptoService.setEncKey(encKey[1].encryptedString);
|
||||
|
||||
const [pubKey, privKey] = await this.cryptoService.makeKeyPair();
|
||||
|
||||
try {
|
||||
await this.apiService.postUserKeyToKeyConnector(keyConnectorUrl, keyConnectorRequest);
|
||||
} catch (e) {
|
||||
this.handleKeyConnectorError(e);
|
||||
}
|
||||
|
||||
const keys = new KeysRequest(pubKey, privKey.encryptedString);
|
||||
const setPasswordRequest = new SetKeyConnectorKeyRequest(
|
||||
encKey[1].encryptedString,
|
||||
kdf,
|
||||
kdfConfig,
|
||||
orgId,
|
||||
keys
|
||||
);
|
||||
await this.apiService.postSetKeyConnectorKey(setPasswordRequest);
|
||||
}
|
||||
|
||||
async setConvertAccountRequired(status: boolean) {
|
||||
await this.stateService.setConvertAccountToKeyConnector(status);
|
||||
}
|
||||
|
||||
async getConvertAccountRequired(): Promise<boolean> {
|
||||
return await this.stateService.getConvertAccountToKeyConnector();
|
||||
}
|
||||
|
||||
async removeConvertAccountRequired() {
|
||||
await this.stateService.setConvertAccountToKeyConnector(null);
|
||||
}
|
||||
|
||||
async clear() {
|
||||
await this.removeConvertAccountRequired();
|
||||
}
|
||||
|
||||
private handleKeyConnectorError(e: any) {
|
||||
this.logService.error(e);
|
||||
if (this.logoutCallback != null) {
|
||||
this.logoutCallback(false);
|
||||
}
|
||||
throw new Error("Key Connector error");
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import { LoginService as LoginServiceAbstraction } from "../abstractions/login.service";
|
||||
import { StateService } from "../abstractions/state.service";
|
||||
|
||||
export class LoginService implements LoginServiceAbstraction {
|
||||
private _email: string;
|
||||
private _rememberEmail: boolean;
|
||||
|
||||
constructor(private stateService: StateService) {}
|
||||
|
||||
getEmail() {
|
||||
return this._email;
|
||||
}
|
||||
|
||||
getRememberEmail() {
|
||||
return this._rememberEmail;
|
||||
}
|
||||
|
||||
setEmail(value: string) {
|
||||
this._email = value;
|
||||
}
|
||||
|
||||
setRememberEmail(value: boolean) {
|
||||
this._rememberEmail = value;
|
||||
}
|
||||
|
||||
clearValues() {
|
||||
this._email = null;
|
||||
this._rememberEmail = null;
|
||||
}
|
||||
|
||||
async saveEmailSettings() {
|
||||
await this.stateService.setRememberedEmail(this._rememberEmail ? this._email : null);
|
||||
this.clearValues();
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,13 @@ import * as signalRMsgPack from "@microsoft/signalr-protocol-msgpack";
|
||||
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
import { AppIdService } from "../abstractions/appId.service";
|
||||
import { AuthService } from "../abstractions/auth.service";
|
||||
import { EnvironmentService } from "../abstractions/environment.service";
|
||||
import { LogService } from "../abstractions/log.service";
|
||||
import { MessagingService } from "../abstractions/messaging.service";
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from "../abstractions/notifications.service";
|
||||
import { StateService } from "../abstractions/state.service";
|
||||
import { AuthenticationStatus } from "../enums/authenticationStatus";
|
||||
import { AuthService } from "../auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "../auth/enums/authentication-status";
|
||||
import { NotificationType } from "../enums/notificationType";
|
||||
import {
|
||||
NotificationResponse,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { OrganizationApiServiceAbstraction } from "../../abstractions/organization/organization-api.service.abstraction";
|
||||
import { OrganizationApiKeyType } from "../../enums/organizationApiKeyType";
|
||||
import { OrganizationApiKeyType } from "../../auth/enums/organization-api-key-type";
|
||||
import { OrganizationSsoRequest } from "../../auth/models/request/organization-sso.request";
|
||||
import { SecretVerificationRequest } from "../../auth/models/request/secret-verification.request";
|
||||
import { ApiKeyResponse } from "../../auth/models/response/api-key.response";
|
||||
import { OrganizationSsoResponse } from "../../auth/models/response/organization-sso.response";
|
||||
import { ImportDirectoryRequest } from "../../models/request/import-directory.request";
|
||||
import { OrganizationApiKeyRequest } from "../../models/request/organization-api-key.request";
|
||||
import { OrganizationCreateRequest } from "../../models/request/organization-create.request";
|
||||
@@ -9,13 +13,10 @@ import { OrganizationSubscriptionUpdateRequest } from "../../models/request/orga
|
||||
import { OrganizationTaxInfoUpdateRequest } from "../../models/request/organization-tax-info-update.request";
|
||||
import { OrganizationUpdateRequest } from "../../models/request/organization-update.request";
|
||||
import { OrganizationUpgradeRequest } from "../../models/request/organization-upgrade.request";
|
||||
import { OrganizationSsoRequest } from "../../models/request/organization/organization-sso.request";
|
||||
import { PaymentRequest } from "../../models/request/payment.request";
|
||||
import { SeatRequest } from "../../models/request/seat.request";
|
||||
import { SecretVerificationRequest } from "../../models/request/secret-verification.request";
|
||||
import { StorageRequest } from "../../models/request/storage.request";
|
||||
import { VerifyBankRequest } from "../../models/request/verify-bank.request";
|
||||
import { ApiKeyResponse } from "../../models/response/api-key.response";
|
||||
import { BillingResponse } from "../../models/response/billing.response";
|
||||
import { ListResponse } from "../../models/response/list.response";
|
||||
import { OrganizationApiKeyInformationResponse } from "../../models/response/organization-api-key-information.response";
|
||||
@@ -23,7 +24,6 @@ import { OrganizationAutoEnrollStatusResponse } from "../../models/response/orga
|
||||
import { OrganizationKeysResponse } from "../../models/response/organization-keys.response";
|
||||
import { OrganizationSubscriptionResponse } from "../../models/response/organization-subscription.response";
|
||||
import { OrganizationResponse } from "../../models/response/organization.response";
|
||||
import { OrganizationSsoResponse } from "../../models/response/organization/organization-sso.response";
|
||||
import { PaymentResponse } from "../../models/response/payment.response";
|
||||
import { TaxInfoResponse } from "../../models/response/tax-info.response";
|
||||
import { SyncService } from "../../vault/abstractions/sync/sync.service.abstraction";
|
||||
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
AbstractMemoryStorageService,
|
||||
AbstractStorageService,
|
||||
} from "../abstractions/storage.service";
|
||||
import { EnvironmentUrls } from "../auth/models/domain/environment-urls";
|
||||
import { KdfConfig } from "../auth/models/domain/kdf-config";
|
||||
import { HtmlStorageLocation } from "../enums/htmlStorageLocation";
|
||||
import { KdfType } from "../enums/kdfType";
|
||||
import { StorageLocation } from "../enums/storageLocation";
|
||||
@@ -30,10 +32,8 @@ import {
|
||||
AccountSettingsSettings,
|
||||
} from "../models/domain/account";
|
||||
import { EncString } from "../models/domain/enc-string";
|
||||
import { EnvironmentUrls } from "../models/domain/environment-urls";
|
||||
import { GeneratedPasswordHistory } from "../models/domain/generated-password-history";
|
||||
import { GlobalState } from "../models/domain/global-state";
|
||||
import { KdfConfig } from "../models/domain/kdf-config";
|
||||
import { Policy } from "../models/domain/policy";
|
||||
import { State } from "../models/domain/state";
|
||||
import { StorageOptions } from "../models/domain/storage-options";
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { AbstractStorageService } from "../abstractions/storage.service";
|
||||
import { EnvironmentUrls } from "../auth/models/domain/environment-urls";
|
||||
import { TokenService } from "../auth/services/token.service";
|
||||
import { HtmlStorageLocation } from "../enums/htmlStorageLocation";
|
||||
import { KdfType } from "../enums/kdfType";
|
||||
import { StateVersion } from "../enums/stateVersion";
|
||||
@@ -17,15 +19,12 @@ import {
|
||||
EncryptionPair,
|
||||
} from "../models/domain/account";
|
||||
import { EncString } from "../models/domain/enc-string";
|
||||
import { EnvironmentUrls } from "../models/domain/environment-urls";
|
||||
import { GeneratedPasswordHistory } from "../models/domain/generated-password-history";
|
||||
import { GlobalState } from "../models/domain/global-state";
|
||||
import { StorageOptions } from "../models/domain/storage-options";
|
||||
import { CipherData } from "../vault/models/data/cipher.data";
|
||||
import { FolderData } from "../vault/models/data/folder.data";
|
||||
|
||||
import { TokenService } from "./token.service";
|
||||
|
||||
// Originally (before January 2022) storage was handled as a flat key/value pair store.
|
||||
// With the move to a typed object for state storage these keys should no longer be in use anywhere outside of this migration.
|
||||
const v1Keys: { [key: string]: string } = {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { AuthService } from "../abstractions/auth.service";
|
||||
import { MessagingService } from "../abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "../abstractions/platformUtils.service";
|
||||
import { StateService } from "../abstractions/state.service";
|
||||
import { SystemService as SystemServiceAbstraction } from "../abstractions/system.service";
|
||||
import { AuthenticationStatus } from "../enums/authenticationStatus";
|
||||
import { AuthService } from "../auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "../auth/enums/authentication-status";
|
||||
import { Utils } from "../misc/utils";
|
||||
|
||||
export class SystemService implements SystemServiceAbstraction {
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
import { StateService } from "../abstractions/state.service";
|
||||
import { TokenService as TokenServiceAbstraction } from "../abstractions/token.service";
|
||||
import { Utils } from "../misc/utils";
|
||||
import { IdentityTokenResponse } from "../models/response/identity-token.response";
|
||||
|
||||
export class TokenService implements TokenServiceAbstraction {
|
||||
static decodeToken(token: string): Promise<any> {
|
||||
if (token == null) {
|
||||
throw new Error("Token not provided.");
|
||||
}
|
||||
|
||||
const parts = token.split(".");
|
||||
if (parts.length !== 3) {
|
||||
throw new Error("JWT must have 3 parts");
|
||||
}
|
||||
|
||||
const decoded = Utils.fromUrlB64ToUtf8(parts[1]);
|
||||
if (decoded == null) {
|
||||
throw new Error("Cannot decode the token");
|
||||
}
|
||||
|
||||
const decodedToken = JSON.parse(decoded);
|
||||
return decodedToken;
|
||||
}
|
||||
|
||||
constructor(private stateService: StateService) {}
|
||||
|
||||
async setTokens(
|
||||
accessToken: string,
|
||||
refreshToken: string,
|
||||
clientIdClientSecret: [string, string]
|
||||
): Promise<any> {
|
||||
await this.setToken(accessToken);
|
||||
await this.setRefreshToken(refreshToken);
|
||||
if (clientIdClientSecret != null) {
|
||||
await this.setClientId(clientIdClientSecret[0]);
|
||||
await this.setClientSecret(clientIdClientSecret[1]);
|
||||
}
|
||||
}
|
||||
|
||||
async setClientId(clientId: string): Promise<any> {
|
||||
return await this.stateService.setApiKeyClientId(clientId);
|
||||
}
|
||||
|
||||
async getClientId(): Promise<string> {
|
||||
return await this.stateService.getApiKeyClientId();
|
||||
}
|
||||
|
||||
async setClientSecret(clientSecret: string): Promise<any> {
|
||||
return await this.stateService.setApiKeyClientSecret(clientSecret);
|
||||
}
|
||||
|
||||
async getClientSecret(): Promise<string> {
|
||||
return await this.stateService.getApiKeyClientSecret();
|
||||
}
|
||||
|
||||
async setToken(token: string): Promise<void> {
|
||||
await this.stateService.setAccessToken(token);
|
||||
}
|
||||
|
||||
async getToken(): Promise<string> {
|
||||
return await this.stateService.getAccessToken();
|
||||
}
|
||||
|
||||
async setRefreshToken(refreshToken: string): Promise<any> {
|
||||
return await this.stateService.setRefreshToken(refreshToken);
|
||||
}
|
||||
|
||||
async getRefreshToken(): Promise<string> {
|
||||
return await this.stateService.getRefreshToken();
|
||||
}
|
||||
|
||||
async setTwoFactorToken(tokenResponse: IdentityTokenResponse): Promise<any> {
|
||||
return await this.stateService.setTwoFactorToken(tokenResponse.twoFactorToken);
|
||||
}
|
||||
|
||||
async getTwoFactorToken(): Promise<string> {
|
||||
return await this.stateService.getTwoFactorToken();
|
||||
}
|
||||
|
||||
async clearTwoFactorToken(): Promise<any> {
|
||||
return await this.stateService.setTwoFactorToken(null);
|
||||
}
|
||||
|
||||
async clearToken(userId?: string): Promise<any> {
|
||||
await this.stateService.setAccessToken(null, { userId: userId });
|
||||
await this.stateService.setRefreshToken(null, { userId: userId });
|
||||
await this.stateService.setApiKeyClientId(null, { userId: userId });
|
||||
await this.stateService.setApiKeyClientSecret(null, { userId: userId });
|
||||
}
|
||||
|
||||
// jwthelper methods
|
||||
// ref https://github.com/auth0/angular-jwt/blob/master/src/angularJwt/services/jwt.js
|
||||
|
||||
async decodeToken(token?: string): Promise<any> {
|
||||
token = token ?? (await this.stateService.getAccessToken());
|
||||
|
||||
if (token == null) {
|
||||
throw new Error("Token not found.");
|
||||
}
|
||||
|
||||
return TokenService.decodeToken(token);
|
||||
}
|
||||
|
||||
async getTokenExpirationDate(): Promise<Date> {
|
||||
const decoded = await this.decodeToken();
|
||||
if (typeof decoded.exp === "undefined") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const d = new Date(0); // The 0 here is the key, which sets the date to the epoch
|
||||
d.setUTCSeconds(decoded.exp);
|
||||
return d;
|
||||
}
|
||||
|
||||
async tokenSecondsRemaining(offsetSeconds = 0): Promise<number> {
|
||||
const d = await this.getTokenExpirationDate();
|
||||
if (d == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const msRemaining = d.valueOf() - (new Date().valueOf() + offsetSeconds * 1000);
|
||||
return Math.round(msRemaining / 1000);
|
||||
}
|
||||
|
||||
async tokenNeedsRefresh(minutes = 5): Promise<boolean> {
|
||||
const sRemaining = await this.tokenSecondsRemaining();
|
||||
return sRemaining < 60 * minutes;
|
||||
}
|
||||
|
||||
async getUserId(): Promise<string> {
|
||||
const decoded = await this.decodeToken();
|
||||
if (typeof decoded.sub === "undefined") {
|
||||
throw new Error("No user id found");
|
||||
}
|
||||
|
||||
return decoded.sub as string;
|
||||
}
|
||||
|
||||
async getEmail(): Promise<string> {
|
||||
const decoded = await this.decodeToken();
|
||||
if (typeof decoded.email === "undefined") {
|
||||
throw new Error("No email found");
|
||||
}
|
||||
|
||||
return decoded.email as string;
|
||||
}
|
||||
|
||||
async getEmailVerified(): Promise<boolean> {
|
||||
const decoded = await this.decodeToken();
|
||||
if (typeof decoded.email_verified === "undefined") {
|
||||
throw new Error("No email verification found");
|
||||
}
|
||||
|
||||
return decoded.email_verified as boolean;
|
||||
}
|
||||
|
||||
async getName(): Promise<string> {
|
||||
const decoded = await this.decodeToken();
|
||||
if (typeof decoded.name === "undefined") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return decoded.name as string;
|
||||
}
|
||||
|
||||
async getIssuer(): Promise<string> {
|
||||
const decoded = await this.decodeToken();
|
||||
if (typeof decoded.iss === "undefined") {
|
||||
throw new Error("No issuer found");
|
||||
}
|
||||
|
||||
return decoded.iss as string;
|
||||
}
|
||||
|
||||
async getIsExternal(): Promise<boolean> {
|
||||
const decoded = await this.decodeToken();
|
||||
|
||||
return Array.isArray(decoded.amr) && decoded.amr.includes("external");
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
import { I18nService } from "../abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "../abstractions/platformUtils.service";
|
||||
import {
|
||||
TwoFactorProviderDetails,
|
||||
TwoFactorService as TwoFactorServiceAbstraction,
|
||||
} from "../abstractions/twoFactor.service";
|
||||
import { TwoFactorProviderType } from "../enums/twoFactorProviderType";
|
||||
import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response";
|
||||
|
||||
export const TwoFactorProviders: Partial<Record<TwoFactorProviderType, TwoFactorProviderDetails>> =
|
||||
{
|
||||
[TwoFactorProviderType.Authenticator]: {
|
||||
type: TwoFactorProviderType.Authenticator,
|
||||
name: null as string,
|
||||
description: null as string,
|
||||
priority: 1,
|
||||
sort: 1,
|
||||
premium: false,
|
||||
},
|
||||
[TwoFactorProviderType.Yubikey]: {
|
||||
type: TwoFactorProviderType.Yubikey,
|
||||
name: null as string,
|
||||
description: null as string,
|
||||
priority: 3,
|
||||
sort: 2,
|
||||
premium: true,
|
||||
},
|
||||
[TwoFactorProviderType.Duo]: {
|
||||
type: TwoFactorProviderType.Duo,
|
||||
name: "Duo",
|
||||
description: null as string,
|
||||
priority: 2,
|
||||
sort: 3,
|
||||
premium: true,
|
||||
},
|
||||
[TwoFactorProviderType.OrganizationDuo]: {
|
||||
type: TwoFactorProviderType.OrganizationDuo,
|
||||
name: "Duo (Organization)",
|
||||
description: null as string,
|
||||
priority: 10,
|
||||
sort: 4,
|
||||
premium: false,
|
||||
},
|
||||
[TwoFactorProviderType.Email]: {
|
||||
type: TwoFactorProviderType.Email,
|
||||
name: null as string,
|
||||
description: null as string,
|
||||
priority: 0,
|
||||
sort: 6,
|
||||
premium: false,
|
||||
},
|
||||
[TwoFactorProviderType.WebAuthn]: {
|
||||
type: TwoFactorProviderType.WebAuthn,
|
||||
name: null as string,
|
||||
description: null as string,
|
||||
priority: 4,
|
||||
sort: 5,
|
||||
premium: true,
|
||||
},
|
||||
};
|
||||
|
||||
export class TwoFactorService implements TwoFactorServiceAbstraction {
|
||||
private twoFactorProvidersData: Map<TwoFactorProviderType, { [key: string]: string }>;
|
||||
private selectedTwoFactorProviderType: TwoFactorProviderType = null;
|
||||
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService
|
||||
) {}
|
||||
|
||||
init() {
|
||||
TwoFactorProviders[TwoFactorProviderType.Email].name = this.i18nService.t("emailTitle");
|
||||
TwoFactorProviders[TwoFactorProviderType.Email].description = this.i18nService.t("emailDesc");
|
||||
|
||||
TwoFactorProviders[TwoFactorProviderType.Authenticator].name =
|
||||
this.i18nService.t("authenticatorAppTitle");
|
||||
TwoFactorProviders[TwoFactorProviderType.Authenticator].description =
|
||||
this.i18nService.t("authenticatorAppDesc");
|
||||
|
||||
TwoFactorProviders[TwoFactorProviderType.Duo].description = this.i18nService.t("duoDesc");
|
||||
|
||||
TwoFactorProviders[TwoFactorProviderType.OrganizationDuo].name =
|
||||
"Duo (" + this.i18nService.t("organization") + ")";
|
||||
TwoFactorProviders[TwoFactorProviderType.OrganizationDuo].description =
|
||||
this.i18nService.t("duoOrganizationDesc");
|
||||
|
||||
TwoFactorProviders[TwoFactorProviderType.WebAuthn].name = this.i18nService.t("webAuthnTitle");
|
||||
TwoFactorProviders[TwoFactorProviderType.WebAuthn].description =
|
||||
this.i18nService.t("webAuthnDesc");
|
||||
|
||||
TwoFactorProviders[TwoFactorProviderType.Yubikey].name = this.i18nService.t("yubiKeyTitle");
|
||||
TwoFactorProviders[TwoFactorProviderType.Yubikey].description =
|
||||
this.i18nService.t("yubiKeyDesc");
|
||||
}
|
||||
|
||||
getSupportedProviders(win: Window): TwoFactorProviderDetails[] {
|
||||
const providers: any[] = [];
|
||||
if (this.twoFactorProvidersData == null) {
|
||||
return providers;
|
||||
}
|
||||
|
||||
if (
|
||||
this.twoFactorProvidersData.has(TwoFactorProviderType.OrganizationDuo) &&
|
||||
this.platformUtilsService.supportsDuo()
|
||||
) {
|
||||
providers.push(TwoFactorProviders[TwoFactorProviderType.OrganizationDuo]);
|
||||
}
|
||||
|
||||
if (this.twoFactorProvidersData.has(TwoFactorProviderType.Authenticator)) {
|
||||
providers.push(TwoFactorProviders[TwoFactorProviderType.Authenticator]);
|
||||
}
|
||||
|
||||
if (this.twoFactorProvidersData.has(TwoFactorProviderType.Yubikey)) {
|
||||
providers.push(TwoFactorProviders[TwoFactorProviderType.Yubikey]);
|
||||
}
|
||||
|
||||
if (
|
||||
this.twoFactorProvidersData.has(TwoFactorProviderType.Duo) &&
|
||||
this.platformUtilsService.supportsDuo()
|
||||
) {
|
||||
providers.push(TwoFactorProviders[TwoFactorProviderType.Duo]);
|
||||
}
|
||||
|
||||
if (
|
||||
this.twoFactorProvidersData.has(TwoFactorProviderType.WebAuthn) &&
|
||||
this.platformUtilsService.supportsWebAuthn(win)
|
||||
) {
|
||||
providers.push(TwoFactorProviders[TwoFactorProviderType.WebAuthn]);
|
||||
}
|
||||
|
||||
if (this.twoFactorProvidersData.has(TwoFactorProviderType.Email)) {
|
||||
providers.push(TwoFactorProviders[TwoFactorProviderType.Email]);
|
||||
}
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
getDefaultProvider(webAuthnSupported: boolean): TwoFactorProviderType {
|
||||
if (this.twoFactorProvidersData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
this.selectedTwoFactorProviderType != null &&
|
||||
this.twoFactorProvidersData.has(this.selectedTwoFactorProviderType)
|
||||
) {
|
||||
return this.selectedTwoFactorProviderType;
|
||||
}
|
||||
|
||||
let providerType: TwoFactorProviderType = null;
|
||||
let providerPriority = -1;
|
||||
this.twoFactorProvidersData.forEach((_value, type) => {
|
||||
const provider = (TwoFactorProviders as any)[type];
|
||||
if (provider != null && provider.priority > providerPriority) {
|
||||
if (type === TwoFactorProviderType.WebAuthn && !webAuthnSupported) {
|
||||
return;
|
||||
}
|
||||
|
||||
providerType = type;
|
||||
providerPriority = provider.priority;
|
||||
}
|
||||
});
|
||||
|
||||
return providerType;
|
||||
}
|
||||
|
||||
setSelectedProvider(type: TwoFactorProviderType) {
|
||||
this.selectedTwoFactorProviderType = type;
|
||||
}
|
||||
|
||||
clearSelectedProvider() {
|
||||
this.selectedTwoFactorProviderType = null;
|
||||
}
|
||||
|
||||
setProviders(response: IdentityTwoFactorResponse) {
|
||||
this.twoFactorProvidersData = response.twoFactorProviders2;
|
||||
}
|
||||
|
||||
clearProviders() {
|
||||
this.twoFactorProvidersData = null;
|
||||
}
|
||||
|
||||
getProviders() {
|
||||
return this.twoFactorProvidersData;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { UserVerificationApiServiceAbstraction } from "../../abstractions/userVerification/userVerification-api.service.abstraction";
|
||||
import { VerifyOTPRequest } from "../../models/request/account/verify-otp.request";
|
||||
|
||||
export class UserVerificationApiService implements UserVerificationApiServiceAbstraction {
|
||||
constructor(private apiService: ApiService) {}
|
||||
|
||||
postAccountVerifyOTP(request: VerifyOTPRequest): Promise<void> {
|
||||
return this.apiService.send("POST", "/accounts/verify-otp", request, true, false);
|
||||
}
|
||||
async postAccountRequestOTP(): Promise<void> {
|
||||
return this.apiService.send("POST", "/accounts/request-otp", null, true, false);
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import { CryptoService } from "../../abstractions/crypto.service";
|
||||
import { I18nService } from "../../abstractions/i18n.service";
|
||||
import { UserVerificationApiServiceAbstraction } from "../../abstractions/userVerification/userVerification-api.service.abstraction";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "../../abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { VerificationType } from "../../enums/verificationType";
|
||||
import { VerifyOTPRequest } from "../../models/request/account/verify-otp.request";
|
||||
import { SecretVerificationRequest } from "../../models/request/secret-verification.request";
|
||||
import { Verification } from "../../types/verification";
|
||||
|
||||
/**
|
||||
* Used for general-purpose user verification throughout the app.
|
||||
* Use it to verify the input collected by UserVerificationComponent.
|
||||
*/
|
||||
export class UserVerificationService implements UserVerificationServiceAbstraction {
|
||||
constructor(
|
||||
private cryptoService: CryptoService,
|
||||
private i18nService: I18nService,
|
||||
private userVerificationApiService: UserVerificationApiServiceAbstraction
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Create a new request model to be used for server-side verification
|
||||
* @param verification User-supplied verification data (Master Password or OTP)
|
||||
* @param requestClass The request model to create
|
||||
* @param alreadyHashed Whether the master password is already hashed
|
||||
*/
|
||||
async buildRequest<T extends SecretVerificationRequest>(
|
||||
verification: Verification,
|
||||
requestClass?: new () => T,
|
||||
alreadyHashed?: boolean
|
||||
) {
|
||||
this.validateInput(verification);
|
||||
|
||||
const request =
|
||||
requestClass != null ? new requestClass() : (new SecretVerificationRequest() as T);
|
||||
|
||||
if (verification.type === VerificationType.OTP) {
|
||||
request.otp = verification.secret;
|
||||
} else {
|
||||
request.masterPasswordHash = alreadyHashed
|
||||
? verification.secret
|
||||
: await this.cryptoService.hashPassword(verification.secret, null);
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to verify the Master Password client-side, or send the OTP to the server for verification (with no other data)
|
||||
* Generally used for client-side verification only.
|
||||
* @param verification User-supplied verification data (Master Password or OTP)
|
||||
*/
|
||||
async verifyUser(verification: Verification): Promise<boolean> {
|
||||
this.validateInput(verification);
|
||||
|
||||
if (verification.type === VerificationType.OTP) {
|
||||
const request = new VerifyOTPRequest(verification.secret);
|
||||
try {
|
||||
await this.userVerificationApiService.postAccountVerifyOTP(request);
|
||||
} catch (e) {
|
||||
throw new Error(this.i18nService.t("invalidVerificationCode"));
|
||||
}
|
||||
} else {
|
||||
const passwordValid = await this.cryptoService.compareAndUpdateKeyHash(
|
||||
verification.secret,
|
||||
null
|
||||
);
|
||||
if (!passwordValid) {
|
||||
throw new Error(this.i18nService.t("invalidMasterPassword"));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async requestOTP() {
|
||||
await this.userVerificationApiService.postAccountRequestOTP();
|
||||
}
|
||||
|
||||
private validateInput(verification: Verification) {
|
||||
if (verification?.secret == null || verification.secret === "") {
|
||||
if (verification.type === VerificationType.OTP) {
|
||||
throw new Error(this.i18nService.t("verificationCodeRequired"));
|
||||
} else {
|
||||
throw new Error(this.i18nService.t("masterPasswordRequired"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { AuthService } from "../../abstractions/auth.service";
|
||||
import { CollectionService } from "../../abstractions/collection.service";
|
||||
import { CryptoService } from "../../abstractions/crypto.service";
|
||||
import { KeyConnectorService } from "../../abstractions/keyConnector.service";
|
||||
import { MessagingService } from "../../abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "../../abstractions/platformUtils.service";
|
||||
import { SearchService } from "../../abstractions/search.service";
|
||||
import { StateService } from "../../abstractions/state.service";
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "../../abstractions/vaultTimeout/vaultTimeout.service";
|
||||
import { VaultTimeoutSettingsService } from "../../abstractions/vaultTimeout/vaultTimeoutSettings.service";
|
||||
import { AuthenticationStatus } from "../../enums/authenticationStatus";
|
||||
import { AuthService } from "../../auth/abstractions/auth.service";
|
||||
import { KeyConnectorService } from "../../auth/abstractions/key-connector.service";
|
||||
import { AuthenticationStatus } from "../../auth/enums/authentication-status";
|
||||
import { CipherService } from "../../vault/abstractions/cipher.service";
|
||||
import { FolderService } from "../../vault/abstractions/folder/folder.service.abstraction";
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { CryptoService } from "../../abstractions/crypto.service";
|
||||
import { PolicyService } from "../../abstractions/policy/policy.service.abstraction";
|
||||
import { StateService } from "../../abstractions/state.service";
|
||||
import { TokenService } from "../../abstractions/token.service";
|
||||
import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "../../abstractions/vaultTimeout/vaultTimeoutSettings.service";
|
||||
import { TokenService } from "../../auth/abstractions/token.service";
|
||||
import { PolicyType } from "../../enums/policyType";
|
||||
|
||||
export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceAbstraction {
|
||||
|
||||
Reference in New Issue
Block a user