insomnia/packages/insomnia-app/app/account/session.js
Gregory Schier 0a616fba6b
Version Control (beta) (#1439)
* VCS proof of concept underway!

* Stuff

* Some things

* Replace deprecated Electron makeSingleInstance

* Rename `window` variables so not to be confused with window object

* Don't unnecessarily update request when URL does not change

* Regenerate package-lock

* Fix tests + ESLint

* Publish

 - insomnia-app@1.0.49
 - insomnia-cookies@0.0.12
 - insomnia-httpsnippet@1.16.18
 - insomnia-importers@2.0.13
 - insomnia-libcurl@0.0.23
 - insomnia-prettify@0.1.7
 - insomnia-url@0.1.6
 - insomnia-xpath@1.0.9
 - insomnia-plugin-base64@1.0.6
 - insomnia-plugin-cookie-jar@1.0.8
 - insomnia-plugin-core-themes@1.0.5
 - insomnia-plugin-default-headers@1.1.9
 - insomnia-plugin-file@1.0.7
 - insomnia-plugin-hash@1.0.7
 - insomnia-plugin-jsonpath@1.0.12
 - insomnia-plugin-now@1.0.11
 - insomnia-plugin-os@1.0.13
 - insomnia-plugin-prompt@1.1.9
 - insomnia-plugin-request@1.0.18
 - insomnia-plugin-response@1.0.16
 - insomnia-plugin-uuid@1.0.10

* Broken but w/e

* Some tweaks

* Big refactor. Create local snapshots and push done

* POC merging and a lot of improvements

* Lots of work done on initial UI/UX

* Fix old tests

* Atomic writes and size-based batches

* Update StageEntry definition once again to be better

* Factor out GraphQL query logic

* Merge algorithm, history modal, other minor things

* Fix test

* Merge, checkout, revert w/ user changes now work

* Force UI to refresh when switching branches changes active request

* Rough draft pull() and some cleanup

* E2EE stuff and some refactoring

* Add ability to share project with team and fixed tests

* VCS now created in root component and better remote project handling

* Remove unused definition

* Publish

 - insomnia-account@0.0.2
 - insomnia-app@1.1.1
 - insomnia-cookies@0.0.14
 - insomnia-httpsnippet@1.16.20
 - insomnia-importers@2.0.15
 - insomnia-libcurl@0.0.25
 - insomnia-prettify@0.1.9
 - insomnia-sync@0.0.2
 - insomnia-url@0.1.8
 - insomnia-xpath@1.0.11
 - insomnia-plugin-base64@1.0.8
 - insomnia-plugin-cookie-jar@1.0.10
 - insomnia-plugin-core-themes@1.0.7
 - insomnia-plugin-file@1.0.9
 - insomnia-plugin-hash@1.0.9
 - insomnia-plugin-jsonpath@1.0.14
 - insomnia-plugin-now@1.0.13
 - insomnia-plugin-os@1.0.15
 - insomnia-plugin-prompt@1.1.11
 - insomnia-plugin-request@1.0.20
 - insomnia-plugin-response@1.0.18
 - insomnia-plugin-uuid@1.0.12

* Move some deps around

* Fix Flow errors

* Update package.json

* Fix eslint errors

* Fix tests

* Update deps

* bootstrap insomnia-sync

* TRy fixing appveyor

* Try something else

* Bump lerna

* try powershell

*  Try again

* Fix imports

* Fixed errors

* sync types refactor

* Show remote projects in workspace dropdown

* Improved pulling of non-local workspaces

* Loading indicators and some tweaks

* Clean up sync staging modal

* Some sync improvements:

- No longer store stage
- Upgrade Electron
- Sync UI/UX improvements

* Fix snyc tests

* Upgraded deps and hot loader tweaks (it's broken for some reason)

* Fix tests

* Branches dialog, network refactoring, some tweaks

* Fixed merging when other branch is empty

* A bunch of small fixes from real testing

* Fixed pull merge logic

* Fix tests

* Some bug fixes

* A few small tweaks

* Conflict resolution and other improvements

* Fix tests

* Add revert changes

* Deal with duplicate projects per workspace

* Some tweaks and accessibility improvements

* Tooltip accessibility

* Fix API endpoint

* Fix tests

* Remove jest dep from insomnia-importers
2019-04-17 17:50:03 -07:00

235 lines
5.4 KiB
JavaScript

import * as srp from 'srp-js';
import * as crypt from './crypt';
import * as fetch from './fetch';
const loginCallbacks = [];
function _callCallbacks() {
const loggedIn = isLoggedIn();
console.log('[session] Sync state changed loggedIn=' + loggedIn);
for (const cb of loginCallbacks) {
if (typeof cb === 'function') {
cb(loggedIn);
}
}
}
export function onLoginLogout(callback) {
loginCallbacks.push(callback);
}
/** Create a new session for the user */
export async function login(rawEmail, rawPassphrase) {
// ~~~~~~~~~~~~~~~ //
// Sanitize Inputs //
// ~~~~~~~~~~~~~~~ //
const email = _sanitizeEmail(rawEmail);
const passphrase = _sanitizePassphrase(rawPassphrase);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// Fetch Salt and Submit A To Server //
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
const { saltKey, saltAuth } = await fetch.post('/auth/login-s', { email }, null);
const authSecret = await crypt.deriveKey(passphrase, email, saltKey);
const secret1 = await crypt.srpGenKey();
const c = new srp.Client(
_getSrpParams(),
Buffer.from(saltAuth, 'hex'),
Buffer.from(email, 'utf8'),
Buffer.from(authSecret, 'hex'),
Buffer.from(secret1, 'hex'),
);
const srpA = c.computeA().toString('hex');
const { sessionStarterId, srpB } = await fetch.post(
'/auth/login-a',
{
srpA,
email,
},
null,
);
// ~~~~~~~~~~~~~~~~~~~~~ //
// Compute and Submit M1 //
// ~~~~~~~~~~~~~~~~~~~~~ //
c.setB(Buffer.from(srpB, 'hex'));
const srpM1 = c.computeM1().toString('hex');
const { srpM2 } = await fetch.post(
'/auth/login-m1',
{
srpM1,
sessionStarterId,
},
null,
);
// ~~~~~~~~~~~~~~~~~~~~~~~~~ //
// Verify Server Identity M2 //
// ~~~~~~~~~~~~~~~~~~~~~~~~~ //
c.checkM2(Buffer.from(srpM2, 'hex'));
// ~~~~~~~~~~~~~~~~~~~~~~ //
// Initialize the Session //
// ~~~~~~~~~~~~~~~~~~~~~~ //
// Compute K (used for session ID)
const sessionId = c.computeK().toString('hex');
// Get and store some extra info (salts and keys)
const {
publicKey,
encPrivateKey,
encSymmetricKey,
saltEnc,
accountId,
firstName,
lastName,
} = await _whoami(sessionId);
const derivedSymmetricKey = await crypt.deriveKey(passphrase, email, saltEnc);
const symmetricKeyStr = await crypt.decryptAES(derivedSymmetricKey, JSON.parse(encSymmetricKey));
// Store the information for later
setSessionData(
sessionId,
accountId,
firstName,
lastName,
email,
JSON.parse(symmetricKeyStr),
JSON.parse(publicKey),
JSON.parse(encPrivateKey),
);
_callCallbacks();
}
export function getPublicKey() {
return _getSessionData().publicKey;
}
export function getPrivateKey() {
const { symmetricKey, encPrivateKey } = _getSessionData();
const privateKeyStr = crypt.decryptAES(symmetricKey, encPrivateKey);
return JSON.parse(privateKeyStr);
}
export function getCurrentSessionId() {
if (window) {
return window.localStorage.getItem('currentSessionId');
} else {
return '';
}
}
export function getAccountId() {
return _getSessionData().accountId;
}
export function getEmail() {
return _getSessionData().email;
}
export function getFirstName() {
return _getSessionData().firstName;
}
/** Check if we (think) we have a session */
export function isLoggedIn() {
return !!getCurrentSessionId();
}
/** Log out and delete session data */
export async function logout() {
try {
await fetch.post('/auth/logout', null, getCurrentSessionId());
} catch (e) {
// Not a huge deal if this fails, but we don't want it to prevent the
// user from signing out.
console.warn('Failed to logout', e);
}
_unsetSessionData();
_callCallbacks();
}
/** Set data for the new session and store it encrypted with the sessionId */
export function setSessionData(
sessionId,
accountId,
firstName,
lastName,
email,
symmetricKey,
publicKey,
encPrivateKey,
) {
const dataStr = JSON.stringify({
id: sessionId,
accountId: accountId,
symmetricKey: symmetricKey,
publicKey: publicKey,
encPrivateKey: encPrivateKey,
email: email,
firstName: firstName,
lastName: lastName,
});
window.localStorage.setItem(_getSessionKey(sessionId), dataStr);
// NOTE: We're setting this last because the stuff above might fail
window.localStorage.setItem('currentSessionId', sessionId);
}
export async function listTeams() {
return fetch.get('/api/teams', getCurrentSessionId());
}
export async function endTrial() {
await fetch.put('/api/billing/end-trial', getCurrentSessionId());
}
// ~~~~~~~~~~~~~~~~ //
// Helper Functions //
// ~~~~~~~~~~~~~~~~ //
function _whoami(sessionId = null) {
return fetch.get('/auth/whoami', sessionId || getCurrentSessionId());
}
function _getSessionData() {
const sessionId = getCurrentSessionId();
if (!sessionId || !window) {
return {};
}
const dataStr = window.localStorage.getItem(_getSessionKey(sessionId));
return JSON.parse(dataStr);
}
function _unsetSessionData() {
const sessionId = getCurrentSessionId();
window.localStorage.removeItem(_getSessionKey(sessionId));
window.localStorage.removeItem(`currentSessionId`);
}
function _getSessionKey(sessionId) {
return `session__${(sessionId || '').slice(0, 10)}`;
}
function _getSrpParams() {
return srp.params[2048];
}
function _sanitizeEmail(email) {
return email.trim().toLowerCase();
}
function _sanitizePassphrase(passphrase) {
return passphrase.trim().normalize('NFKD');
}