1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +00:00

Merge remote-tracking branch 'origin/master' into Feature.Web.534.AllowMultiSelectInOrgVault

Grabbing latest
This commit is contained in:
Addison Beck
2020-07-21 16:12:08 -05:00
22 changed files with 260 additions and 39 deletions

View File

@@ -1 +1,62 @@
Code contributions are welcome! Please commit any pull requests against the `master` branch.
# Setting up your Local Dev environment for jslib
In order to easily test, check and develop against local changes to jslib across each of the TypeScript/JavaScript clients it is recommended to use symlinks for the submodule so that you only have to make the change once and don't need to x-copy or wait for a commit+merge to checkout, pull and test against your other repos.
## Prerequisites
1. git bash or other git command line
## Clone Repos
In order for this to work well, you need to use a consistent relative directory structure. Repos should be cloned in the following way:
* `./<your project(s) directory>`; we'll call this `/dev` ('cause why not)
* jslib - `git clone https://github.com/bitwarden/jslib.git` (/dev/jslib)
* web - `git clone --recurse-submodules https://github.com/bitwarden/web.git` (/dev/web)
* desktop - `git clone --recurse-submodules https://github.com/bitwarden/desktop.git` (/dev/desktop)
* browser - `git clone --recurse-submodules https://github.com/bitwarden/browser.git` (/dev/browser)
* cli - `git clone --recurse-submodules https://github.com/bitwarden/cli` (/dev/cli)
You should notice web, desktop, browser and cli each reference jslib as a git submodule. If you've already cloned the repos but didn't use `--recurse-submodules` then you'll need to init those:
`npm run sub:init`
## Configure Symlinks
Be aware that using git clone will make symlinks added to your repo be seen by git as plain text file paths, lets make sure this is set to true to prevent that. In the project root run, `git config core.symlinks true`.
For each project other than jslib, run the following:
For macOS/Linux: `npm run symlink:mac`
For Windows: `npm run symlink:win`
## Updates and Cleanup
* Need to update parent repo that has jslib as a submodule to latest from actual jslib repo?
* Create branch from master (`git checkout -b update-jslib`)
* From new local branch: `npm run sub:pull` (`git submodule foreach git pull origin master`)
* Follow Pull Request notes for commit/push instructions
* Once merged, pull master, rebase your feature branch and then do npm run sub:update to catch your submodule up
* Discard changes made to a submodule
* `git submodule foreach git reset —hard`
## Merge Conflicts
At times when you need to perform a `git merge master` into your feature or local branch, and there are conflicting version references to the *jslib* repo from your other clients, you will not be able to use the traditional merge or stage functions you would normally use for a file.
To resolve you must use either `git reset` or update the index directly using `git update-index`. You can use (depending on whether you have symlink'd jslib) one of the following:
```bash
git reset master -- jslib
git reset master@{upstream} -- jslib
git reset HEAD -- jslib
git reset MERGE_HEAD -- jslib
```
Those should automatically stage the change and reset the jslib submodule back to where it needs to be (generally at the latest version from `master`).
The other option is to update the index directly using the plumbing command git update-index. To do that, you need to know that an object of type gitlink (i.e., directory entry in a parent repository that points to a submodule) is 0160000. You can figure it out from `git ls-files -s` or the following reference (see "1110 (gitlink)" under 4-bit object type): https://github.com/gitster/git/blob/master/Documentation/technical/index-format.txt
To use that approach, figure out the hash you want to set the submodule to, then run, e.g.:
`git update-index --cacheinfo 0160000,533da4ea00703f4ad6d5518e1ce81d20261c40c0,jslib`
see: [https://stackoverflow.com/questions/26617838/how-to-resolve-git-submodule-conflict-if-submodule-is-not-initialized](https://stackoverflow.com/questions/26617838/how-to-resolve-git-submodule-conflict-if-submodule-is-not-initialized)

View File

@@ -141,6 +141,7 @@ export abstract class ApiService {
postAccountKeys: (request: KeysRequest) => Promise<any>;
postAccountVerifyEmail: () => Promise<any>;
postAccountVerifyEmailToken: (request: VerifyEmailRequest) => Promise<any>;
postAccountVerifyPassword: (request: PasswordVerificationRequest) => Promise<any>;
postAccountRecoverDelete: (request: DeleteRecoverRequest) => Promise<any>;
postAccountRecoverDeleteToken: (request: VerifyDeleteRecoverRequest) => Promise<any>;
postAccountKdf: (request: KdfRequest) => Promise<any>;

View File

@@ -6,10 +6,14 @@ import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
export abstract class AuthService {
email: string;
masterPasswordHash: string;
code: string;
codeVerifier: string;
ssoRedirectUrl: string;
twoFactorProvidersData: Map<TwoFactorProviderType, { [key: string]: string; }>;
selectedTwoFactorProviderType: TwoFactorProviderType;
logIn: (email: string, masterPassword: string) => Promise<AuthResult>;
logInSso: (code: string, codeVerifier: string, redirectUrl: string) => Promise<AuthResult>;
logInTwoFactor: (twoFactorProvider: TwoFactorProviderType, twoFactorToken: string,
remember?: boolean) => Promise<AuthResult>;
logInComplete: (email: string, masterPassword: string, twoFactorProvider: TwoFactorProviderType,
@@ -18,4 +22,6 @@ export abstract class AuthService {
getSupportedTwoFactorProviders: (win: Window) => any[];
getDefaultTwoFactorProvider: (u2fSupported: boolean) => TwoFactorProviderType;
makePreloginKey: (masterPassword: string, email: string) => Promise<SymmetricCryptoKey>;
authingWithSso: () => boolean;
authingWithPassword: () => boolean;
}

View File

@@ -78,6 +78,7 @@ export class AddEditComponent implements OnInit {
addFieldTypeOptions: any[];
uriMatchOptions: any[];
ownershipOptions: any[] = [];
currentDate = new Date();
protected writeableCollections: CollectionView[];
private previousCipherId: string;

View File

@@ -1,6 +1,7 @@
import { OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { ApiService } from '../../abstractions/api.service';
import { CryptoService } from '../../abstractions/crypto.service';
import { EnvironmentService } from '../../abstractions/environment.service';
import { I18nService } from '../../abstractions/i18n.service';
@@ -16,6 +17,8 @@ import { ConstantsService } from '../../services/constants.service';
import { CipherString } from '../../models/domain/cipherString';
import { SymmetricCryptoKey } from '../../models/domain/symmetricCryptoKey';
import { PasswordVerificationRequest } from '../../models/request/passwordVerificationRequest';
import { Utils } from '../../misc/utils';
export class LockComponent implements OnInit {
@@ -25,6 +28,7 @@ export class LockComponent implements OnInit {
email: string;
pinLock: boolean = false;
webVaultHostname: string = '';
formPromise: Promise<any>;
protected successRoute: string = 'vault';
protected onSuccessfulSubmit: () => void;
@@ -36,7 +40,8 @@ export class LockComponent implements OnInit {
protected platformUtilsService: PlatformUtilsService, protected messagingService: MessagingService,
protected userService: UserService, protected cryptoService: CryptoService,
protected storageService: StorageService, protected vaultTimeoutService: VaultTimeoutService,
protected environmentService: EnvironmentService, protected stateService: StateService) { }
protected environmentService: EnvironmentService, protected stateService: StateService,
protected apiService: ApiService) { }
async ngOnInit() {
this.pinSet = await this.vaultTimeoutService.isPinLockSet();
@@ -98,9 +103,26 @@ export class LockComponent implements OnInit {
} else {
const key = await this.cryptoService.makeKey(this.masterPassword, this.email, kdf, kdfIterations);
const keyHash = await this.cryptoService.hashPassword(this.masterPassword, key);
const storedKeyHash = await this.cryptoService.getKeyHash();
if (storedKeyHash != null && keyHash != null && storedKeyHash === keyHash) {
let passwordValid = false;
if (keyHash != null) {
const storedKeyHash = await this.cryptoService.getKeyHash();
if (storedKeyHash != null) {
passwordValid = storedKeyHash === keyHash;
} else {
const request = new PasswordVerificationRequest();
request.masterPasswordHash = keyHash;
try {
this.formPromise = this.apiService.postAccountVerifyPassword(request);
await this.formPromise;
passwordValid = true;
await this.cryptoService.setKeyHash(keyHash);
} catch { }
}
}
if (passwordValid) {
if (this.pinSet[0]) {
const protectedPin = await this.storageService.get<string>(ConstantsService.protectedPin);
const encKey = await this.cryptoService.getEncKey(key);

View File

@@ -1,6 +1,7 @@
import { Router } from '@angular/router';
import { KeysRequest } from '../../models/request/keysRequest';
import { ReferenceEventRequest } from '../../models/request/referenceEventRequest';
import { RegisterRequest } from '../../models/request/registerRequest';
import { ApiService } from '../../abstractions/api.service';
@@ -22,7 +23,7 @@ export class RegisterComponent {
showPassword: boolean = false;
formPromise: Promise<any>;
masterPasswordScore: number;
referenceId: string;
referenceData: ReferenceEventRequest;
protected successRoute = 'login';
private masterPasswordStrengthTimeout: any;
@@ -111,7 +112,7 @@ export class RegisterComponent {
const hashedPassword = await this.cryptoService.hashPassword(this.masterPassword, key);
const keys = await this.cryptoService.makeKeyPair(encKey[0]);
const request = new RegisterRequest(this.email, this.name, hashedPassword,
this.hint, encKey[1].encryptedString, kdf, kdfIterations, this.referenceId);
this.hint, encKey[1].encryptedString, kdf, kdfIterations, this.referenceData);
request.keys = new KeysRequest(keys[0], keys[1].encryptedString);
const orgInvite = await this.stateService.get<any>('orgInvitation');
if (orgInvite != null && orgInvite.token != null && orgInvite.organizationUserId != null) {

View File

@@ -52,12 +52,16 @@ export class TwoFactorComponent implements OnInit, OnDestroy {
}
async ngOnInit() {
if (this.authService.email == null || this.authService.masterPasswordHash == null ||
if ((!this.authService.authingWithSso() && !this.authService.authingWithPassword()) ||
this.authService.twoFactorProvidersData == null) {
this.router.navigate([this.loginRoute]);
return;
}
if (this.authService.authingWithSso()) {
this.successRoute = 'lock';
}
if (this.initU2f && this.win != null && this.u2fSupported) {
let customWebVaultUrl: string = null;
if (this.environmentService.baseUrl != null) {

View File

@@ -14,10 +14,11 @@ export class FirefoxCsvImporter extends BaseImporter implements Importer {
results.forEach((value) => {
const cipher = this.initLoginCipher();
cipher.name = this.getValueOrDefault(this.nameFromUrl(value.hostname), '--');
const url = this.getValueOrDefault(value.url, this.getValueOrDefault(value.hostname));
cipher.name = this.getValueOrDefault(this.nameFromUrl(url), '--');
cipher.login.username = this.getValueOrDefault(value.username);
cipher.login.password = this.getValueOrDefault(value.password);
cipher.login.uris = this.makeUriArray(value.hostname);
cipher.login.uris = this.makeUriArray(url);
this.cleanupCipher(cipher);
result.ciphers.push(cipher);
});

View File

@@ -68,6 +68,8 @@ export class KeePass2XmlImporter extends BaseImporter implements Importer {
cipher.login.username = value;
} else if (key === 'Password') {
cipher.login.password = value;
} else if (key === 'otp') {
cipher.login.totp = value.replace('key=', '');
} else if (key === 'Title') {
cipher.name = value;
} else if (key === 'Notes') {

View File

@@ -89,6 +89,14 @@ export class Utils {
}
}
static fromBufferToUrlB64(buffer: ArrayBuffer): string {
const output = this.fromBufferToB64(buffer)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
return output;
}
static fromBufferToUtf8(buffer: ArrayBuffer): string {
if (Utils.isNode || Utils.isNativeScript) {
return Buffer.from(buffer).toString('utf8');
@@ -204,9 +212,14 @@ export class Utils {
} catch (e) { }
}
const domain = tldjs != null && tldjs.getDomain != null ? tldjs.getDomain(uriString) : null;
if (domain != null) {
return domain;
try {
const domain = tldjs != null && tldjs.getDomain != null ? tldjs.getDomain(uriString) : null;
if (domain != null) {
return domain;
}
} catch {
return null;
}
return null;

View File

@@ -16,6 +16,7 @@ export class OrganizationData {
useTotp: boolean;
use2fa: boolean;
useApi: boolean;
useBusinessPortal: boolean;
selfHost: boolean;
usersGetPremium: boolean;
seats: number;
@@ -35,6 +36,7 @@ export class OrganizationData {
this.useTotp = response.useTotp;
this.use2fa = response.use2fa;
this.useApi = response.useApi;
this.useBusinessPortal = response.useBusinessPortal;
this.selfHost = response.selfHost;
this.usersGetPremium = response.usersGetPremium;
this.seats = response.seats;

View File

@@ -2,5 +2,6 @@ import { TwoFactorProviderType } from '../../enums/twoFactorProviderType';
export class AuthResult {
twoFactor: boolean = false;
resetMasterPassword: boolean = false;
twoFactorProviders: Map<TwoFactorProviderType, { [key: string]: string; }> = null;
}

View File

@@ -16,6 +16,7 @@ export class Organization {
useTotp: boolean;
use2fa: boolean;
useApi: boolean;
useBusinessPortal: boolean;
selfHost: boolean;
usersGetPremium: boolean;
seats: number;
@@ -39,6 +40,7 @@ export class Organization {
this.useTotp = obj.useTotp;
this.use2fa = obj.use2fa;
this.useApi = obj.useApi;
this.useBusinessPortal = obj.useBusinessPortal;
this.selfHost = obj.selfHost;
this.usersGetPremium = obj.usersGetPremium;
this.seats = obj.seats;

View File

@@ -0,0 +1,5 @@
export class ReferenceEventRequest {
id: string;
layout: string;
flow: string;
}

View File

@@ -1,4 +1,5 @@
import { KeysRequest } from './keysRequest';
import { ReferenceEventRequest } from './referenceEventRequest';
import { KdfType } from '../../enums/kdfType';
@@ -13,10 +14,10 @@ export class RegisterRequest {
organizationUserId: string;
kdf: KdfType;
kdfIterations: number;
referenceId: string;
referenceData: ReferenceEventRequest;
constructor(email: string, name: string, masterPasswordHash: string, masterPasswordHint: string, key: string,
kdf: KdfType, kdfIterations: number, referenceId: string) {
kdf: KdfType, kdfIterations: number, referenceData: ReferenceEventRequest) {
this.name = name;
this.email = email;
this.masterPasswordHash = masterPasswordHash;
@@ -24,6 +25,6 @@ export class RegisterRequest {
this.key = key;
this.kdf = kdf;
this.kdfIterations = kdfIterations;
this.referenceId = referenceId;
this.referenceData = referenceData;
}
}

View File

@@ -5,15 +5,24 @@ import { DeviceRequest } from './deviceRequest';
export class TokenRequest {
email: string;
masterPasswordHash: string;
code: string;
codeVerifier: string;
redirectUri: string;
token: string;
provider: TwoFactorProviderType;
remember: boolean;
device?: DeviceRequest;
constructor(email: string, masterPasswordHash: string, provider: TwoFactorProviderType,
constructor(credentials: string[], codes: string[], provider: TwoFactorProviderType,
token: string, remember: boolean, device?: DeviceRequest) {
this.email = email;
this.masterPasswordHash = masterPasswordHash;
if (credentials != null && credentials.length > 1) {
this.email = credentials[0];
this.masterPasswordHash = credentials[1];
} else if (codes != null && codes.length > 2) {
this.code = codes[0];
this.codeVerifier = codes[1];
this.redirectUri = codes[2];
}
this.token = token;
this.provider = provider;
this.remember = remember;
@@ -22,13 +31,23 @@ export class TokenRequest {
toIdentityToken(clientId: string) {
const obj: any = {
grant_type: 'password',
username: this.email,
password: this.masterPasswordHash,
scope: 'api offline_access',
client_id: clientId,
};
if (this.masterPasswordHash != null && this.email != null) {
obj.grant_type = 'password';
obj.username = this.email;
obj.password = this.masterPasswordHash;
} else if (this.code != null && this.codeVerifier != null && this.redirectUri != null) {
obj.grant_type = 'authorization_code';
obj.code = this.code;
obj.code_verifier = this.codeVerifier;
obj.redirect_uri = this.redirectUri;
} else {
throw new Error('must provide credentials or codes');
}
if (this.device) {
obj.deviceType = this.device.type;
obj.deviceIdentifier = this.device.identifier;

View File

@@ -1,14 +1,19 @@
import { BaseResponse } from './baseResponse';
import { KdfType } from '../../enums/kdfType';
export class IdentityTokenResponse extends BaseResponse {
accessToken: string;
expiresIn: number;
refreshToken: string;
tokenType: string;
resetMasterPassword: boolean;
privateKey: string;
key: string;
twoFactorToken: string;
kdf: KdfType;
kdfIterations: number;
constructor(response: any) {
super(response);
@@ -17,8 +22,11 @@ export class IdentityTokenResponse extends BaseResponse {
this.refreshToken = response.refresh_token;
this.tokenType = response.token_type;
this.resetMasterPassword = this.getResponseProperty('ResetMasterPassword');
this.privateKey = this.getResponseProperty('PrivateKey');
this.key = this.getResponseProperty('Key');
this.twoFactorToken = this.getResponseProperty('TwoFactorToken');
this.kdf = this.getResponseProperty('Kdf');
this.kdfIterations = this.getResponseProperty('KdfIterations');
}
}

View File

@@ -13,6 +13,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
useTotp: boolean;
use2fa: boolean;
useApi: boolean;
useBusinessPortal: boolean;
selfHost: boolean;
usersGetPremium: boolean;
seats: number;
@@ -34,6 +35,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
this.useTotp = this.getResponseProperty('UseTotp');
this.use2fa = this.getResponseProperty('Use2fa');
this.useApi = this.getResponseProperty('UseApi');
this.useBusinessPortal = this.getResponseProperty('UseBusinessPortal');
this.selfHost = this.getResponseProperty('SelfHost');
this.usersGetPremium = this.getResponseProperty('UsersGetPremium');
this.seats = this.getResponseProperty('Seats');

View File

@@ -26,6 +26,7 @@ export class LoginUriView implements View {
private _uri: string = null;
private _domain: string = null;
private _hostname: string = null;
private _host: string = null;
private _canLaunch: boolean = null;
// tslint:enable
@@ -71,10 +72,28 @@ export class LoginUriView implements View {
return this._hostname;
}
get host(): string {
if (this.match === UriMatchType.RegularExpression) {
return null;
}
if (this._host == null && this.uri != null) {
this._host = Utils.getHost(this.uri);
if (this._host === '') {
this._host = null;
}
}
return this._host;
}
get hostnameOrUri(): string {
return this.hostname != null ? this.hostname : this.uri;
}
get hostOrUri(): string {
return this.host != null ? this.host : this.uri;
}
get isWebsite(): boolean {
return this.uri != null && (this.uri.indexOf('http://') === 0 || this.uri.indexOf('https://') === 0 ||
(this.uri.indexOf('://') < 0 && Utils.tldEndingRegex.test(this.uri)));

View File

@@ -321,6 +321,10 @@ export class ApiService implements ApiServiceAbstraction {
return this.send('POST', '/accounts/verify-email-token', request, false, false);
}
postAccountVerifyPassword(request: PasswordVerificationRequest): Promise<any> {
return this.send('POST', '/accounts/verify-password', request, true, false);
}
postAccountRecoverDelete(request: DeleteRecoverRequest): Promise<any> {
return this.send('POST', '/accounts/delete-recover', request, false, false);
}

View File

@@ -77,12 +77,13 @@ export const TwoFactorProviders = {
export class AuthService implements AuthServiceAbstraction {
email: string;
masterPasswordHash: string;
code: string;
codeVerifier: string;
ssoRedirectUrl: string;
twoFactorProvidersData: Map<TwoFactorProviderType, { [key: string]: string; }>;
selectedTwoFactorProviderType: TwoFactorProviderType = null;
private key: SymmetricCryptoKey;
private kdf: KdfType;
private kdfIterations: number;
constructor(private cryptoService: CryptoService, private apiService: ApiService,
private userService: UserService, private tokenService: TokenService,
@@ -116,13 +117,19 @@ export class AuthService implements AuthServiceAbstraction {
this.selectedTwoFactorProviderType = null;
const key = await this.makePreloginKey(masterPassword, email);
const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key);
return await this.logInHelper(email, hashedPassword, key);
return await this.logInHelper(email, hashedPassword, null, null, null, key,
null, null, null);
}
async logInSso(code: string, codeVerifier: string, redirectUrl: string): Promise<AuthResult> {
this.selectedTwoFactorProviderType = null;
return await this.logInHelper(null, null, code, codeVerifier, redirectUrl, null, null, null, null);
}
async logInTwoFactor(twoFactorProvider: TwoFactorProviderType, twoFactorToken: string,
remember?: boolean): Promise<AuthResult> {
return await this.logInHelper(this.email, this.masterPasswordHash, this.key, twoFactorProvider,
twoFactorToken, remember);
return await this.logInHelper(this.email, this.masterPasswordHash, this.code, this.codeVerifier,
this.ssoRedirectUrl, this.key, twoFactorProvider, twoFactorToken, remember);
}
async logInComplete(email: string, masterPassword: string, twoFactorProvider: TwoFactorProviderType,
@@ -130,7 +137,8 @@ export class AuthService implements AuthServiceAbstraction {
this.selectedTwoFactorProviderType = null;
const key = await this.makePreloginKey(masterPassword, email);
const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key);
return await this.logInHelper(email, hashedPassword, key, twoFactorProvider, twoFactorToken, remember);
return await this.logInHelper(email, hashedPassword, null, null, null, key, twoFactorProvider, twoFactorToken,
remember);
}
logOut(callback: Function) {
@@ -201,37 +209,59 @@ export class AuthService implements AuthServiceAbstraction {
async makePreloginKey(masterPassword: string, email: string): Promise<SymmetricCryptoKey> {
email = email.trim().toLowerCase();
this.kdf = null;
this.kdfIterations = null;
let kdf: KdfType = null;
let kdfIterations: number = null;
try {
const preloginResponse = await this.apiService.postPrelogin(new PreloginRequest(email));
if (preloginResponse != null) {
this.kdf = preloginResponse.kdf;
this.kdfIterations = preloginResponse.kdfIterations;
kdf = preloginResponse.kdf;
kdfIterations = preloginResponse.kdfIterations;
}
} catch (e) {
if (e == null || e.statusCode !== 404) {
throw e;
}
}
return this.cryptoService.makeKey(masterPassword, email, this.kdf, this.kdfIterations);
return this.cryptoService.makeKey(masterPassword, email, kdf, kdfIterations);
}
private async logInHelper(email: string, hashedPassword: string, key: SymmetricCryptoKey,
twoFactorProvider?: TwoFactorProviderType, twoFactorToken?: string, remember?: boolean): Promise<AuthResult> {
authingWithSso(): boolean {
return this.code != null && this.codeVerifier != null && this.ssoRedirectUrl != null;
}
authingWithPassword(): boolean {
return this.email != null && this.masterPasswordHash != null;
}
private async logInHelper(email: string, hashedPassword: string, code: string, codeVerifier: string,
redirectUrl: string, key: SymmetricCryptoKey, twoFactorProvider?: TwoFactorProviderType,
twoFactorToken?: string, remember?: boolean): Promise<AuthResult> {
const storedTwoFactorToken = await this.tokenService.getTwoFactorToken(email);
const appId = await this.appIdService.getAppId();
const deviceRequest = new DeviceRequest(appId, this.platformUtilsService);
let emailPassword: string[] = [];
let codeCodeVerifier: string[] = [];
if (email != null && hashedPassword != null) {
emailPassword = [email, hashedPassword];
} else {
emailPassword = null;
}
if (code != null && codeVerifier != null && redirectUrl != null) {
codeCodeVerifier = [code, codeVerifier, redirectUrl];
} else {
codeCodeVerifier = null;
}
let request: TokenRequest;
if (twoFactorToken != null && twoFactorProvider != null) {
request = new TokenRequest(email, hashedPassword, twoFactorProvider, twoFactorToken, remember,
request = new TokenRequest(emailPassword, codeCodeVerifier, twoFactorProvider, twoFactorToken, remember,
deviceRequest);
} else if (storedTwoFactorToken != null) {
request = new TokenRequest(email, hashedPassword, TwoFactorProviderType.Remember,
request = new TokenRequest(emailPassword, codeCodeVerifier, TwoFactorProviderType.Remember,
storedTwoFactorToken, false, deviceRequest);
} else {
request = new TokenRequest(email, hashedPassword, null, null, false, deviceRequest);
request = new TokenRequest(emailPassword, codeCodeVerifier, null, null, false, deviceRequest);
}
const response = await this.apiService.postIdentityToken(request);
@@ -245,6 +275,9 @@ export class AuthService implements AuthServiceAbstraction {
const twoFactorResponse = response as IdentityTwoFactorResponse;
this.email = email;
this.masterPasswordHash = hashedPassword;
this.code = code;
this.codeVerifier = codeVerifier;
this.ssoRedirectUrl = redirectUrl;
this.key = this.setCryptoKeys ? key : null;
this.twoFactorProvidersData = twoFactorResponse.twoFactorProviders2;
result.twoFactorProviders = twoFactorResponse.twoFactorProviders2;
@@ -252,16 +285,21 @@ export class AuthService implements AuthServiceAbstraction {
}
const tokenResponse = response as IdentityTokenResponse;
result.resetMasterPassword = tokenResponse.resetMasterPassword;
if (tokenResponse.twoFactorToken != null) {
await this.tokenService.setTwoFactorToken(tokenResponse.twoFactorToken, email);
}
await this.tokenService.setTokens(tokenResponse.accessToken, tokenResponse.refreshToken);
await this.userService.setInformation(this.tokenService.getUserId(), this.tokenService.getEmail(),
this.kdf, this.kdfIterations);
tokenResponse.kdf, tokenResponse.kdfIterations);
if (this.setCryptoKeys) {
await this.cryptoService.setKey(key);
await this.cryptoService.setKeyHash(hashedPassword);
if (key != null) {
await this.cryptoService.setKey(key);
}
if (hashedPassword != null) {
await this.cryptoService.setKeyHash(hashedPassword);
}
await this.cryptoService.setEncKey(tokenResponse.key);
// User doesn't have a key pair yet (old account), let's generate one for them
@@ -284,8 +322,12 @@ export class AuthService implements AuthServiceAbstraction {
}
private clearState(): void {
this.key = null;
this.email = null;
this.masterPasswordHash = null;
this.code = null;
this.codeVerifier = null;
this.ssoRedirectUrl = null;
this.twoFactorProvidersData = null;
this.selectedTwoFactorProviderType = null;
}

View File

@@ -23,6 +23,8 @@ export class ConstantsService {
static readonly protectedPin: string = 'protectedPin';
static readonly clearClipboardKey: string = 'clearClipboardKey';
static readonly eventCollectionKey: string = 'eventCollection';
static readonly ssoCodeVerifierKey: string = 'ssoCodeVerifier';
static readonly ssoStateKey: string = 'ssoState';
readonly environmentUrlsKey: string = ConstantsService.environmentUrlsKey;
readonly disableGaKey: string = ConstantsService.disableGaKey;
@@ -47,4 +49,6 @@ export class ConstantsService {
readonly protectedPin: string = ConstantsService.protectedPin;
readonly clearClipboardKey: string = ConstantsService.clearClipboardKey;
readonly eventCollectionKey: string = ConstantsService.eventCollectionKey;
readonly ssoCodeVerifierKey: string = ConstantsService.ssoCodeVerifierKey;
readonly ssoStateKey: string = ConstantsService.ssoStateKey;
}