diff --git a/src/background.html b/src/background.html
index a95f36bb47..8ab1e3135a 100644
--- a/src/background.html
+++ b/src/background.html
@@ -4,9 +4,7 @@
-
-
diff --git a/src/background.js b/src/background.js
index 91bae6e395..bec397329b 100644
--- a/src/background.js
+++ b/src/background.js
@@ -1,4 +1,5 @@
// Service imports
+import ApiService from './services/api.service';
import AppIdService from './services/appId.service';
import ConstantsService from './services/constants.service';
import CryptoService from './services/crypto.service';
@@ -20,6 +21,7 @@ import { SecureNoteData } from './models/data/secureNoteData';
import { CipherString } from './models/domain/cipherString';
+import { CipherRequest } from './models/request/cipherRequest';
import { DeviceRequest } from './models/request/deviceRequest';
import { DeviceTokenRequest } from './models/request/deviceTokenRequest';
import { FolderRequest } from './models/request/folderRequest';
@@ -78,7 +80,7 @@ var bg_isBackground = true,
window.bg_cryptoService = bg_cryptoService = new CryptoService();
window.bg_tokenService = bg_tokenService = new TokenService();
window.bg_appIdService = bg_appIdService = new AppIdService();
- window.bg_apiService = bg_apiService = new ApiService(bg_tokenService, bg_appIdService, bg_utilsService, bg_constantsService, logout);
+ window.bg_apiService = bg_apiService = new ApiService(bg_tokenService, logout);
window.bg_environmentService = bg_environmentService = new EnvironmentService(bg_constantsService, bg_apiService);
window.bg_userService = bg_userService = new UserService(bg_tokenService, bg_apiService, bg_cryptoService, bg_utilsService);
window.bg_settingsService = bg_settingsService = new SettingsService(bg_userService, bg_utilsService);
@@ -907,7 +909,9 @@ var bg_isBackground = true,
});
setIcon();
refreshBadgeAndMenu();
- callback();
+ if (callback) {
+ callback();
+ }
});
});
});
diff --git a/src/enums/cipherType.enum.ts b/src/enums/cipherType.enum.ts
new file mode 100644
index 0000000000..c081fb2d8c
--- /dev/null
+++ b/src/enums/cipherType.enum.ts
@@ -0,0 +1,6 @@
+export enum CipherType {
+ Login = 1,
+ SecureNote = 2,
+ Card = 3,
+ Identity = 4,
+}
diff --git a/src/enums/fieldType.enum.ts b/src/enums/fieldType.enum.ts
new file mode 100644
index 0000000000..c28b26c1da
--- /dev/null
+++ b/src/enums/fieldType.enum.ts
@@ -0,0 +1,5 @@
+export enum FieldType {
+ Text = 0,
+ Hidden = 1,
+ Boolean = 2,
+}
diff --git a/src/models/api/requestModels.js b/src/models/api/requestModels.js
deleted file mode 100644
index 866b949eaf..0000000000
--- a/src/models/api/requestModels.js
+++ /dev/null
@@ -1,70 +0,0 @@
-window.CipherRequest = function (cipher) {
- this.type = cipher.type;
- this.folderId = cipher.folderId;
- this.organizationId = cipher.organizationId;
- this.name = cipher.name ? cipher.name.encryptedString : null;
- this.notes = cipher.notes ? cipher.notes.encryptedString : null;
- this.favorite = cipher.favorite;
-
- var constantsService = chrome.extension.getBackgroundPage().bg_constantsService;
- switch (this.type) {
- case constantsService.cipherType.login:
- this.login = {
- uri: cipher.login.uri ? cipher.login.uri.encryptedString : null,
- username: cipher.login.username ? cipher.login.username.encryptedString : null,
- password: cipher.login.password ? cipher.login.password.encryptedString : null,
- totp: cipher.login.totp ? cipher.login.totp.encryptedString : null
- };
- break;
- case constantsService.cipherType.secureNote:
- this.secureNote = {
- type: cipher.secureNote.type
- };
- break;
- case constantsService.cipherType.card:
- this.card = {
- cardholderName: cipher.card.cardholderName ? cipher.card.cardholderName.encryptedString : null,
- brand: cipher.card.brand ? cipher.card.brand.encryptedString : null,
- number: cipher.card.number ? cipher.card.number.encryptedString : null,
- expMonth: cipher.card.expMonth ? cipher.card.expMonth.encryptedString : null,
- expYear: cipher.card.expYear ? cipher.card.expYear.encryptedString : null,
- code: cipher.card.code ? cipher.card.code.encryptedString : null
- };
- break;
- case constantsService.cipherType.identity:
- this.identity = {
- title: cipher.identity.title ? cipher.identity.title.encryptedString : null,
- firstName: cipher.identity.firstName ? cipher.identity.firstName.encryptedString : null,
- middleName: cipher.identity.middleName ? cipher.identity.middleName.encryptedString : null,
- lastName: cipher.identity.lastName ? cipher.identity.lastName.encryptedString : null,
- address1: cipher.identity.address1 ? cipher.identity.address1.encryptedString : null,
- address2: cipher.identity.address2 ? cipher.identity.address2.encryptedString : null,
- address3: cipher.identity.address3 ? cipher.identity.address3.encryptedString : null,
- city: cipher.identity.city ? cipher.identity.city.encryptedString : null,
- state: cipher.identity.state ? cipher.identity.state.encryptedString : null,
- postalCode: cipher.identity.postalCode ? cipher.identity.postalCode.encryptedString : null,
- country: cipher.identity.country ? cipher.identity.country.encryptedString : null,
- company: cipher.identity.company ? cipher.identity.company.encryptedString : null,
- email: cipher.identity.email ? cipher.identity.email.encryptedString : null,
- phone: cipher.identity.phone ? cipher.identity.phone.encryptedString : null,
- ssn: cipher.identity.ssn ? cipher.identity.ssn.encryptedString : null,
- username: cipher.identity.username ? cipher.identity.username.encryptedString : null,
- passportNumber: cipher.identity.passportNumber ? cipher.identity.passportNumber.encryptedString : null,
- licenseNumber: cipher.identity.licenseNumber ? cipher.identity.licenseNumber.encryptedString : null
- };
- break;
- default:
- break;
- }
-
- if (cipher.fields) {
- this.fields = [];
- for (var i = 0; i < cipher.fields.length; i++) {
- this.fields.push({
- type: cipher.fields[i].type,
- name: cipher.fields[i].name ? cipher.fields[i].name.encryptedString : null,
- value: cipher.fields[i].value ? cipher.fields[i].value.encryptedString : null,
- });
- }
- }
-};
diff --git a/src/models/domain/environmentUrls.ts b/src/models/domain/environmentUrls.ts
new file mode 100644
index 0000000000..0eec60a114
--- /dev/null
+++ b/src/models/domain/environmentUrls.ts
@@ -0,0 +1,5 @@
+export default class EnvironmentUrls {
+ base: string;
+ api: string;
+ identity: string;
+}
diff --git a/src/models/domainModels.js b/src/models/domainModels.js
index 04d60ce349..4e50ee2a2d 100644
--- a/src/models/domainModels.js
+++ b/src/models/domainModels.js
@@ -1,4 +1,4 @@
-var Cipher = function (obj, alreadyEncrypted, localData) {
+var Cipher = window.Cipher = function (obj, alreadyEncrypted, localData) {
this.constantsService = chrome.extension.getBackgroundPage().bg_constantsService;
this.utilsService = chrome.extension.getBackgroundPage().bg_utilsService;
@@ -55,7 +55,7 @@ var Cipher = function (obj, alreadyEncrypted, localData) {
}
};
-var Login2 = function (obj, alreadyEncrypted) {
+var Login2 = window.Login2 = function (obj, alreadyEncrypted) {
buildDomainModel(this, obj, {
uri: null,
username: null,
@@ -64,7 +64,7 @@ var Login2 = function (obj, alreadyEncrypted) {
}, alreadyEncrypted, []);
};
-var Identity = function (obj, alreadyEncrypted) {
+var Identity = window.Identity = function (obj, alreadyEncrypted) {
buildDomainModel(this, obj, {
title: null,
firstName: null,
@@ -87,7 +87,7 @@ var Identity = function (obj, alreadyEncrypted) {
}, alreadyEncrypted, []);
};
-var Card = function (obj, alreadyEncrypted) {
+var Card = window.Card = function (obj, alreadyEncrypted) {
buildDomainModel(this, obj, {
cardholderName: null,
brand: null,
@@ -98,11 +98,11 @@ var Card = function (obj, alreadyEncrypted) {
}, alreadyEncrypted, []);
};
-var SecureNote = function (obj, alreadyEncrypted) {
+var SecureNote = window.SecureNote = function (obj, alreadyEncrypted) {
this.type = obj.type;
};
-var Field = function (obj, alreadyEncrypted) {
+var Field = window.Field = function (obj, alreadyEncrypted) {
this.type = obj.type;
buildDomainModel(this, obj, {
name: null,
@@ -110,7 +110,7 @@ var Field = function (obj, alreadyEncrypted) {
}, alreadyEncrypted, []);
};
-var Attachment = function (obj, alreadyEncrypted) {
+var Attachment = window.Attachment = function (obj, alreadyEncrypted) {
this.size = obj.size;
buildDomainModel(this, obj, {
id: null,
@@ -120,7 +120,7 @@ var Attachment = function (obj, alreadyEncrypted) {
}, alreadyEncrypted, ['id', 'url', 'sizeName']);
};
-var Folder = function (obj, alreadyEncrypted) {
+var Folder = window.Folder = function (obj, alreadyEncrypted) {
buildDomainModel(this, obj, {
id: null,
name: null
diff --git a/src/models/request/cipherRequest.ts b/src/models/request/cipherRequest.ts
new file mode 100644
index 0000000000..e037f53aae
--- /dev/null
+++ b/src/models/request/cipherRequest.ts
@@ -0,0 +1,89 @@
+import { CipherType } from '../../enums/cipherType.enum';
+
+class CipherRequest {
+ type: CipherType;
+ folderId: string;
+ organizationId: string;
+ name: string;
+ notes: string;
+ favorite: boolean;
+ login: any;
+ secureNote: any;
+ card: any;
+ identity: any;
+ fields: any[];
+
+ constructor(cipher: any) {
+ this.type = cipher.type;
+ this.folderId = cipher.folderId;
+ this.organizationId = cipher.organizationId;
+ this.name = cipher.name ? cipher.name.encryptedString : null;
+ this.notes = cipher.notes ? cipher.notes.encryptedString : null;
+ this.favorite = cipher.favorite;
+
+ switch (this.type) {
+ case CipherType.Login:
+ this.login = {
+ uri: cipher.login.uri ? cipher.login.uri.encryptedString : null,
+ username: cipher.login.username ? cipher.login.username.encryptedString : null,
+ password: cipher.login.password ? cipher.login.password.encryptedString : null,
+ totp: cipher.login.totp ? cipher.login.totp.encryptedString : null,
+ };
+ break;
+ case CipherType.SecureNote:
+ this.secureNote = {
+ type: cipher.secureNote.type,
+ };
+ break;
+ case CipherType.Card:
+ this.card = {
+ cardholderName: cipher.card.cardholderName ? cipher.card.cardholderName.encryptedString : null,
+ brand: cipher.card.brand ? cipher.card.brand.encryptedString : null,
+ number: cipher.card.number ? cipher.card.number.encryptedString : null,
+ expMonth: cipher.card.expMonth ? cipher.card.expMonth.encryptedString : null,
+ expYear: cipher.card.expYear ? cipher.card.expYear.encryptedString : null,
+ code: cipher.card.code ? cipher.card.code.encryptedString : null,
+ };
+ break;
+ case CipherType.Identity:
+ this.identity = {
+ title: cipher.identity.title ? cipher.identity.title.encryptedString : null,
+ firstName: cipher.identity.firstName ? cipher.identity.firstName.encryptedString : null,
+ middleName: cipher.identity.middleName ? cipher.identity.middleName.encryptedString : null,
+ lastName: cipher.identity.lastName ? cipher.identity.lastName.encryptedString : null,
+ address1: cipher.identity.address1 ? cipher.identity.address1.encryptedString : null,
+ address2: cipher.identity.address2 ? cipher.identity.address2.encryptedString : null,
+ address3: cipher.identity.address3 ? cipher.identity.address3.encryptedString : null,
+ city: cipher.identity.city ? cipher.identity.city.encryptedString : null,
+ state: cipher.identity.state ? cipher.identity.state.encryptedString : null,
+ postalCode: cipher.identity.postalCode ? cipher.identity.postalCode.encryptedString : null,
+ country: cipher.identity.country ? cipher.identity.country.encryptedString : null,
+ company: cipher.identity.company ? cipher.identity.company.encryptedString : null,
+ email: cipher.identity.email ? cipher.identity.email.encryptedString : null,
+ phone: cipher.identity.phone ? cipher.identity.phone.encryptedString : null,
+ ssn: cipher.identity.ssn ? cipher.identity.ssn.encryptedString : null,
+ username: cipher.identity.username ? cipher.identity.username.encryptedString : null,
+ passportNumber: cipher.identity.passportNumber ?
+ cipher.identity.passportNumber.encryptedString : null,
+ licenseNumber: cipher.identity.licenseNumber ? cipher.identity.licenseNumber.encryptedString : null,
+ };
+ break;
+ default:
+ break;
+ }
+
+ if (cipher.fields) {
+ this.fields = [];
+ for (const field of cipher.fields) {
+ this.fields.push({
+ type: field.type,
+ name: field.name ? field.name.encryptedString : null,
+ value: field.value ? field.value.encryptedString : null,
+ });
+ }
+ }
+ }
+}
+
+export { CipherRequest };
+(window as any).CipherRequest = CipherRequest;
diff --git a/src/models/response/errorResponse.ts b/src/models/response/errorResponse.ts
index 6426e44849..03dfda14b0 100644
--- a/src/models/response/errorResponse.ts
+++ b/src/models/response/errorResponse.ts
@@ -3,21 +3,21 @@ class ErrorResponse {
validationErrors: { [key: string]: string[]; };
statusCode: number;
- constructor(response: any, identityResponse?: boolean) {
+ constructor(response: any, status: number, identityResponse?: boolean) {
let errorModel = null;
- if (identityResponse && response.responseJSON && response.responseJSON.ErrorModel) {
- errorModel = response.responseJSON.ErrorModel;
- } else if (response.responseJSON) {
- errorModel = response.responseJSON;
- } else if (response.responseText && response.responseText.indexOf('{') === 0) {
- errorModel = JSON.parse(response.responseText);
+ if (identityResponse && response && response.ErrorModel) {
+ errorModel = response.ErrorModel;
+ } else if (response) {
+ errorModel = response;
+ //} else if (response.responseText && response.responseText.indexOf('{') === 0) {
+ // errorModel = JSON.parse(response.responseText);
}
if (errorModel) {
this.message = errorModel.Message;
this.validationErrors = errorModel.ValidationErrors;
}
- this.statusCode = response.status;
+ this.statusCode = status;
}
}
diff --git a/src/popup/app/accounts/accountsHintController.js b/src/popup/app/accounts/accountsHintController.js
index d49899e3b4..e1d877205a 100644
--- a/src/popup/app/accounts/accountsHintController.js
+++ b/src/popup/app/accounts/accountsHintController.js
@@ -1,4 +1,4 @@
-angular
+angular
.module('bit.accounts')
.controller('accountsHintController', function ($scope, $state, apiService, toastr, $q, utilsService,
@@ -31,13 +31,11 @@
function hintPromise(request) {
return $q(function (resolve, reject) {
- apiService.postPasswordHint(request,
- function () {
- resolve();
- },
- function (error) {
- reject(error);
- });
+ apiService.postPasswordHint(request).then(function () {
+ resolve();
+ }, function (error) {
+ reject(error);
+ });
});
}
});
diff --git a/src/popup/app/accounts/accountsRegisterController.js b/src/popup/app/accounts/accountsRegisterController.js
index adfe07ea3e..a0cf9d0880 100644
--- a/src/popup/app/accounts/accountsRegisterController.js
+++ b/src/popup/app/accounts/accountsRegisterController.js
@@ -45,17 +45,17 @@ angular
function registerPromise(key, masterPassword, email, hint) {
var deferred = $q.defer();
- cryptoService.makeEncKey(key).then(function (encKey) {
+ var encKey;
+ cryptoService.makeEncKey(key).then(function (theEncKey) {
+ encKey = theEncKey;
return cryptoService.hashPassword(masterPassword, key);
}).then(function (hashedPassword) {
var request = new RegisterRequest(email, hashedPassword, hint, encKey.encryptedString);
- apiService.postRegister(request,
- function () {
- deferred.resolve();
- },
- function (error) {
- deferred.reject(error);
- });
+ apiService.postRegister(request).then(function () {
+ deferred.resolve();
+ }, function (error) {
+ deferred.reject(error);
+ });
});
return deferred.promise;
}
diff --git a/src/popup/app/app.js b/src/popup/app/app.js
index ab54740d63..c33cf3eb73 100644
--- a/src/popup/app/app.js
+++ b/src/popup/app/app.js
@@ -21,7 +21,6 @@ require('../../scripts/analytics.js');
require('../../scripts/duo.js');
require('../../scripts/u2f.js');
-require('../../models/api/requestModels.js');
require('../../models/domainModels.js');
require('../less/libs.less');
@@ -42,6 +41,7 @@ import { SecureNoteData } from '../../models/data/secureNoteData';
import { CipherString } from '../../models/domain/cipherString';
+import { CipherRequest } from '../../models/request/cipherRequest';
import { DeviceRequest } from '../../models/request/deviceRequest';
import { DeviceTokenRequest } from '../../models/request/deviceTokenRequest';
import { FolderRequest } from '../../models/request/folderRequest';
diff --git a/src/popup/app/services/authService.js b/src/popup/app/services/authService.js
index 2758f6c66e..1164961c94 100644
--- a/src/popup/app/services/authService.js
+++ b/src/popup/app/services/authService.js
@@ -10,7 +10,8 @@ angular
var key = cryptoService.makeKey(masterPassword, email),
deferred = $q.defer(),
deviceRequest = null,
- twoFactorRememberedToken;
+ twoFactorRememberedToken,
+ hashedPassword;
appIdService.getAppId().then(function (appId) {
deviceRequest = new DeviceRequest(appId, utilsService);
@@ -18,7 +19,8 @@ angular
}).then(function (theTwoFactorRememberedToken) {
twoFactorRememberedToken = theTwoFactorRememberedToken;
return cryptoService.hashPassword(masterPassword, key);
- }).then(function (hashedPassword) {
+ }).then(function (theHashedPassword) {
+ hashedPassword = theHashedPassword;
var request;
if (twoFactorToken && typeof (twoFactorProvider) !== 'undefined' && twoFactorProvider !== null) {
@@ -33,45 +35,48 @@ angular
request = new TokenRequest(email, hashedPassword, null, null, false, deviceRequest);
}
- apiService.postIdentityToken(request, function (response) {
- // success
- if (!response || !response.accessToken) {
- return;
- }
+ return apiService.postIdentityToken(request);
+ }).then(function (response) {
+ if (!response) {
+ return;
+ }
- if (response.twoFactorToken) {
- tokenService.setTwoFactorToken(response.twoFactorToken, email);
- }
-
- tokenService.setTokens(response.accessToken, response.refreshToken).then(function () {
- return cryptoService.setKey(key);
- }).then(function () {
- return cryptoService.setKeyHash(hashedPassword);
- }).then(function () {
- userService.setUserIdAndEmail(tokenService.getUserId(), tokenService.getEmail(),
- function () {
- cryptoService.setEncKey(response.key).then(function () {
- return cryptoService.setEncPrivateKey(response.privateKey);
- }).then(function () {
- chrome.runtime.sendMessage({ command: 'loggedIn' });
- deferred.resolve({
- twoFactor: false,
- twoFactorProviders: null
- });
- });
- });
- });
- }, function (providers) {
+ if (!response.accessToken) {
// two factor required
deferred.resolve({
twoFactor: true,
- twoFactorProviders: providers
+ twoFactorProviders: response
});
- }, function (error) {
- // error
- deferred.reject(error);
+ return;
+ }
+
+ if (response.twoFactorToken) {
+ tokenService.setTwoFactorToken(response.twoFactorToken, email);
+ }
+
+ return tokenService.setTokens(response.accessToken, response.refreshToken).then(function () {
+ return cryptoService.setKey(key);
+ }).then(function () {
+ return cryptoService.setKeyHash(hashedPassword);
+ }).then(function () {
+ userService.setUserIdAndEmail(tokenService.getUserId(), tokenService.getEmail(),
+ function () {
+ cryptoService.setEncKey(response.key).then(function () {
+ return cryptoService.setEncPrivateKey(response.privateKey);
+ }).then(function () {
+ chrome.runtime.sendMessage({ command: 'loggedIn' });
+ deferred.resolve({
+ twoFactor: false,
+ twoFactorProviders: null
+ });
+ });
+ });
});
+ }, function (error) {
+ // error
+ deferred.reject(error);
});
+
return deferred.promise;
};
diff --git a/src/popup/app/settings/settingsPremiumController.js b/src/popup/app/settings/settingsPremiumController.js
index 7170296305..da5fabb63f 100644
--- a/src/popup/app/settings/settingsPremiumController.js
+++ b/src/popup/app/settings/settingsPremiumController.js
@@ -1,4 +1,4 @@
-angular
+angular
.module('bit.settings')
.controller('settingsPremiumController', function ($scope, i18nService, tokenService, apiService, toastr, SweetAlert,
@@ -8,7 +8,7 @@
$scope.price = '$10';
$scope.refresh = function () {
- apiService.refreshIdentityToken(function () {
+ apiService.refreshIdentityToken().then(function () {
toastr.success(i18nService.refreshComplete);
$timeout(function () {
$scope.isPremium = tokenService.getPremium();
diff --git a/src/services/api.service.ts b/src/services/api.service.ts
new file mode 100644
index 0000000000..805ec5d45f
--- /dev/null
+++ b/src/services/api.service.ts
@@ -0,0 +1,429 @@
+import AppIdService from './appId.service';
+import ConstantsService from './constants.service';
+import TokenService from './token.service';
+import UtilsService from './utils.service';
+
+import EnvironmentUrls from '../models/domain/environmentUrls';
+
+import { CipherRequest } from '../models/request/cipherRequest';
+import { DeviceRequest } from '../models/request/deviceRequest';
+import { DeviceTokenRequest } from '../models/request/deviceTokenRequest';
+import { FolderRequest } from '../models/request/folderRequest';
+import { PasswordHintRequest } from '../models/request/passwordHintRequest';
+import { RegisterRequest } from '../models/request/registerRequest';
+import { TokenRequest } from '../models/request/tokenRequest';
+import { TwoFactorEmailRequest } from '../models/request/twoFactorEmailRequest';
+
+import { AttachmentResponse } from '../models/response/attachmentResponse';
+import { CipherResponse } from '../models/response/cipherResponse';
+import { DeviceResponse } from '../models/response/deviceResponse';
+import { DomainsResponse } from '../models/response/domainsResponse';
+import { ErrorResponse } from '../models/response/errorResponse';
+import { FolderResponse } from '../models/response/folderResponse';
+import { GlobalDomainResponse } from '../models/response/globalDomainResponse';
+import { IdentityTokenResponse } from '../models/response/identityTokenResponse';
+import { KeysResponse } from '../models/response/keysResponse';
+import { ListResponse } from '../models/response/listResponse';
+import { ProfileOrganizationResponse } from '../models/response/profileOrganizationResponse';
+import { ProfileResponse } from '../models/response/profileResponse';
+import { SyncResponse } from '../models/response/syncResponse';
+
+export default class ApiService {
+ urlsSet: boolean = false;
+ baseUrl: string;
+ identityBaseUrl: string;
+ logoutCallback: Function;
+
+ constructor(private tokenService: TokenService, logoutCallback: Function) {
+ this.logoutCallback = logoutCallback;
+ }
+
+ setUrls(urls: EnvironmentUrls) {
+ this.urlsSet = true;
+
+ if (urls.base != null) {
+ this.baseUrl = urls.base + '/api';
+ this.identityBaseUrl = urls.base + '/identity';
+ return;
+ }
+
+ if (urls.api != null && urls.identity != null) {
+ this.baseUrl = urls.api;
+ this.identityBaseUrl = urls.identity;
+ return;
+ }
+
+ /* tslint:disable */
+ // Desktop
+ //this.baseUrl = 'http://localhost:4000';
+ //this.identityBaseUrl = 'http://localhost:33656';
+
+ // Desktop HTTPS
+ //this.baseUrl = 'https://localhost:44377';
+ //this.identityBaseUrl = 'https://localhost:44392';
+
+ // Desktop external
+ //this.baseUrl = 'http://192.168.1.3:4000';
+ //this.identityBaseUrl = 'http://192.168.1.3:33656';
+
+ // Preview
+ //this.baseUrl = 'https://preview-api.bitwarden.com';
+ //this.identityBaseUrl = 'https://preview-identity.bitwarden.com';
+
+ // Production
+ this.baseUrl = 'https://api.bitwarden.com';
+ this.identityBaseUrl = 'https://identity.bitwarden.com';
+ /* tslint:enable */
+ }
+
+ // Auth APIs
+
+ async postIdentityToken(request: TokenRequest): Promise {
+ const response = await fetch(new Request(this.identityBaseUrl + '/connect/token', {
+ body: this.qsStringify(request.toIdentityToken()),
+ cache: 'no-cache',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
+ 'Accept': 'application/json',
+ },
+ method: 'POST',
+ }));
+
+ const responseJson = await response.json();
+ if (response.status === 200) {
+ return new IdentityTokenResponse(responseJson);
+ } else if (response.status === 400 && responseJson && responseJson.TwoFactorProviders2 &&
+ Object.keys(responseJson.TwoFactorProviders2).length) {
+ await this.tokenService.clearTwoFactorToken(request.email);
+ return responseJson.TwoFactorProviders2;
+ } else {
+ return Promise.reject(new ErrorResponse(responseJson, response.status, true));
+ }
+ }
+
+ async refreshIdentityToken(): Promise {
+ const response = await this.doRefreshToken();
+ if (response.status !== 200) {
+ return Promise.reject(null);
+ }
+ }
+
+ // Two Factor APIs
+
+ async postTwoFactorEmail(request: TwoFactorEmailRequest): Promise {
+ const response = await fetch(new Request(this.baseUrl + '/two-factor/send-email-login', {
+ body: JSON.stringify(request),
+ cache: 'no-cache',
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8',
+ },
+ method: 'POST',
+ }));
+
+ if (response.status !== 200) {
+ const error = await this.handleError(response, false);
+ return Promise.reject(error);
+ }
+ }
+
+ // Account APIs
+
+ async getAccountRevisionDate(): Promise {
+ const authHeader = await this.handleTokenState();
+ const response = await fetch(new Request(this.baseUrl + '/accounts/revision-date', {
+ cache: 'no-cache',
+ headers: {
+ Accept: 'application/json',
+ Authorization: authHeader,
+ },
+ }));
+
+ if (response.status === 200) {
+ return (await response.json() as number);
+ } else {
+ const error = await this.handleError(response, false);
+ return Promise.reject(error);
+ }
+ }
+
+ async postPasswordHint(request: PasswordHintRequest): Promise {
+ const response = await fetch(new Request(this.baseUrl + '/accounts/password-hint', {
+ body: JSON.stringify(request),
+ cache: 'no-cache',
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8',
+ },
+ method: 'POST',
+ }));
+
+ if (response.status !== 200) {
+ const error = await this.handleError(response, false);
+ return Promise.reject(error);
+ }
+ }
+
+ async postRegister(request: RegisterRequest): Promise {
+ const response = await fetch(new Request(this.baseUrl + '/accounts/register', {
+ body: JSON.stringify(request),
+ cache: 'no-cache',
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8',
+ },
+ method: 'POST',
+ }));
+
+ if (response.status !== 200) {
+ const error = await this.handleError(response, false);
+ return Promise.reject(error);
+ }
+ }
+
+ // Folder APIs
+
+ async postFolder(request: FolderRequest): Promise {
+ const authHeader = await this.handleTokenState();
+ const response = await fetch(new Request(this.baseUrl + '/folders', {
+ body: JSON.stringify(request),
+ cache: 'no-cache',
+ headers: {
+ 'Accept': 'application/json',
+ 'Authorization': authHeader,
+ 'Content-Type': 'application/json; charset=utf-8',
+ },
+ method: 'POST',
+ }));
+
+ if (response.status === 200) {
+ const responseJson = await response.json();
+ return new FolderResponse(responseJson);
+ } else {
+ const error = await this.handleError(response, false);
+ return Promise.reject(error);
+ }
+ }
+
+ async putFolder(id: string, request: FolderRequest): Promise {
+ const authHeader = await this.handleTokenState();
+ const response = await fetch(new Request(this.baseUrl + '/folders/' + id, {
+ body: JSON.stringify(request),
+ cache: 'no-cache',
+ headers: {
+ 'Accept': 'application/json',
+ 'Authorization': authHeader,
+ 'Content-Type': 'application/json; charset=utf-8',
+ },
+ method: 'PUT',
+ }));
+
+ if (response.status === 200) {
+ const responseJson = await response.json();
+ return new FolderResponse(responseJson);
+ } else {
+ const error = await this.handleError(response, false);
+ return Promise.reject(error);
+ }
+ }
+
+ async deleteFolder(id: string): Promise {
+ const authHeader = await this.handleTokenState();
+ const response = await fetch(new Request(this.baseUrl + '/folders/' + id, {
+ cache: 'no-cache',
+ headers: {
+ Authorization: authHeader,
+ },
+ method: 'DELETE',
+ }));
+
+ if (response.status !== 200) {
+ const error = await this.handleError(response, false);
+ return Promise.reject(error);
+ }
+ }
+
+ // Cipher APIs
+
+ async postCipher(request: CipherRequest): Promise {
+ const authHeader = await this.handleTokenState();
+ const response = await fetch(new Request(this.baseUrl + '/ciphers', {
+ body: JSON.stringify(request),
+ cache: 'no-cache',
+ headers: {
+ 'Accept': 'application/json',
+ 'Authorization': authHeader,
+ 'Content-Type': 'application/json; charset=utf-8',
+ },
+ method: 'POST',
+ }));
+
+ if (response.status === 200) {
+ const responseJson = await response.json();
+ return new CipherResponse(responseJson);
+ } else {
+ const error = await this.handleError(response, false);
+ return Promise.reject(error);
+ }
+ }
+
+ async putCipher(id: string, request: CipherRequest): Promise {
+ const authHeader = await this.handleTokenState();
+ const response = await fetch(new Request(this.baseUrl + '/ciphers/' + id, {
+ body: JSON.stringify(request),
+ cache: 'no-cache',
+ headers: {
+ 'Accept': 'application/json',
+ 'Authorization': authHeader,
+ 'Content-Type': 'application/json; charset=utf-8',
+ },
+ method: 'PUT',
+ }));
+
+ if (response.status === 200) {
+ const responseJson = await response.json();
+ return new CipherResponse(responseJson);
+ } else {
+ const error = await this.handleError(response, false);
+ return Promise.reject(error);
+ }
+ }
+
+ async deleteCipher(id: string): Promise {
+ const authHeader = await this.handleTokenState();
+ const response = await fetch(new Request(this.baseUrl + '/ciphers/' + id, {
+ cache: 'no-cache',
+ headers: {
+ Authorization: authHeader,
+ },
+ method: 'DELETE',
+ }));
+
+ if (response.status !== 200) {
+ const error = await this.handleError(response, false);
+ return Promise.reject(error);
+ }
+ }
+
+ // Attachments APIs
+
+ async postCipherAttachment(id: string, data: FormData): Promise {
+ const authHeader = await this.handleTokenState();
+ const response = await fetch(new Request(this.baseUrl + '/ciphers/' + id + '/attachment', {
+ body: data,
+ cache: 'no-cache',
+ headers: {
+ Accept: 'application/json',
+ Authorization: authHeader,
+ },
+ method: 'POST',
+ }));
+
+ if (response.status === 200) {
+ const responseJson = await response.json();
+ return new CipherResponse(responseJson);
+ } else {
+ const error = await this.handleError(response, false);
+ return Promise.reject(error);
+ }
+ }
+
+ async deleteCipherAttachment(id: string, attachmentId: string): Promise {
+ const authHeader = await this.handleTokenState();
+ const response = await fetch(new Request(this.baseUrl + '/ciphers/' + id + '/attachment/' + attachmentId, {
+ cache: 'no-cache',
+ headers: {
+ Authorization: authHeader,
+ },
+ method: 'DELETE',
+ }));
+
+ if (response.status !== 200) {
+ const error = await this.handleError(response, false);
+ return Promise.reject(error);
+ }
+ }
+
+ // Sync APIs
+
+ async getSync(): Promise {
+ const authHeader = await this.handleTokenState();
+ const response = await fetch(new Request(this.baseUrl + '/sync', {
+ cache: 'no-cache',
+ headers: {
+ Accept: 'application/json',
+ Authorization: authHeader,
+ },
+ }));
+
+ if (response.status === 200) {
+ const responseJson = await response.json();
+ return new SyncResponse(responseJson);
+ } else {
+ const error = await this.handleError(response, false);
+ return Promise.reject(error);
+ }
+ }
+
+ // Helpers
+
+ private async handleError(response: Response, tokenError: boolean): Promise {
+ if (response != null && (tokenError && response.status === 400) ||
+ response.status === 401 || response.status === 403) {
+ if (this.logoutCallback) {
+ this.logoutCallback(true);
+ } else {
+ chrome.runtime.sendMessage({ command: 'logout', expired: true });
+ }
+ return null;
+ }
+
+ const responseJson = await response.json();
+ return new ErrorResponse(responseJson, response.status);
+ }
+
+ private async handleTokenState(): Promise {
+ const accessToken = await this.tokenService.getToken();
+ if (!this.tokenService.tokenNeedsRefresh()) {
+ return 'Bearer ' + accessToken;
+ }
+
+ const response = await this.doRefreshToken();
+ const responseJson = await response.json();
+ const tokenResponse = new IdentityTokenResponse(responseJson);
+ await this.tokenService.setTokens(tokenResponse.accessToken, tokenResponse.refreshToken);
+ return 'Bearer ' + tokenResponse.accessToken;
+
+ // TODO: handle error
+ }
+
+ private async doRefreshToken(): Promise {
+ const refreshToken = await this.tokenService.getRefreshToken();
+ if (refreshToken == null || refreshToken === '') {
+ throw new Error();
+ }
+
+ const response = await fetch(new Request(this.identityBaseUrl + '/connect/token', {
+ body: this.qsStringify({
+ grant_type: 'refresh_token',
+ client_id: 'browser',
+ refresh_token: refreshToken,
+ }),
+ cache: 'no-cache',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
+ 'Accept': 'application/json',
+ },
+ method: 'POST',
+ }));
+
+ if (response.status === 200) {
+ return response;
+ } else {
+ return Promise.reject(response);
+ }
+ }
+
+ private qsStringify(params: any): string {
+ return Object.keys(params).map((key) => {
+ return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
+ }).join('&');
+ }
+}
diff --git a/src/services/syncService.js b/src/services/syncService.js
index 6afcb9e6af..88405848b2 100644
--- a/src/services/syncService.js
+++ b/src/services/syncService.js
@@ -1,4 +1,4 @@
-function SyncService(cipherService, folderService, userService, apiService, settingsService,
+function SyncService(cipherService, folderService, userService, apiService, settingsService,
cryptoService, logoutCallback) {
this.cipherService = cipherService;
this.folderService = folderService;
@@ -45,7 +45,7 @@ function initSyncService() {
}
self.userService.getUserId(function (userId) {
- self.apiService.getSync(function (response) {
+ self.apiService.getSync().then(function (response) {
syncProfile(self, response.profile).then(function () {
return syncFolders(self, userId, response.folders);
}).then(function () {
@@ -80,7 +80,7 @@ function initSyncService() {
return;
}
- self.apiService.getAccountRevisionDate(function (response) {
+ self.apiService.getAccountRevisionDate().then(function (response) {
var accountRevisionDate = new Date(response);
self.getLastSync(function (lastSync) {
if (lastSync && accountRevisionDate <= lastSync) {
@@ -90,7 +90,7 @@ function initSyncService() {
callback(true, false);
});
- }, function () {
+ }, function (error) {
callback(false, true);
});
}