mirror of
https://github.com/Kong/insomnia
synced 2024-11-12 17:26:32 +00:00
46d3719b99
* Maybe working POC * Change to use remote url * Other URL too * Some logic * Got the push part working * Made some updates * Fix * Update * Add status code check * Stuff * Implemented new sync api * A bit more robust * Debounce changes * Change timeout * Some fixes * Remove .less * Better error handling * Fix base url * Support for created vs updated docs * Try silent * Silence removal too * Small fix after merge * Fix test * Stuff * Implement key generation algorithm * Tidy * stuff * A bunch of stuff for the new API * Integrated the session stuff * Stuff * Just started on encryption * Lots of updates to encryption * Finished createResourceGroup function * Full encryption/decryption working (I think) * Encrypt localstorage with sessionID * Some more * Some extra checks * Now uses separate DB. Still needs to be simplified a LOT * Fix deletion bug * Fixed unicode bug with encryption * Simplified and working * A bunch of polish * Some stuff * Removed some workspace meta properties * Migrated a few more meta properties * Small changes * Fix body scrolling and url cursor jumping * Removed duplication of webpack port * Remove workspaces reduces * Some small fixes * Added sync modal and opt-in setting * Good start to sync flow * Refactored modal footer css * Update sync status * Sync logger * A bit better logging * Fixed a bunch of sync-related bugs * Fixed signup form button * Gravatar component * Split sync modal into tabs * Tidying * Some more error handling * start sending 'user agent * Login/signup error handling * Use real UUIDs * Fixed tests * Remove unused function * Some extra checks * Moved cloud sync setting to about page * Some small changes * Some things
276 lines
6.6 KiB
JavaScript
276 lines
6.6 KiB
JavaScript
import srp from 'srp';
|
|
import * as crypt from './crypt';
|
|
import * as util from './util';
|
|
|
|
const NO_SESSION = '__NO_SESSION__';
|
|
|
|
/**
|
|
* Create a new account
|
|
*
|
|
* @returns {Promise}
|
|
* @param firstName
|
|
* @param lastName
|
|
* @param rawEmail
|
|
* @param rawPassphrase
|
|
*/
|
|
export async function signup (firstName, lastName, rawEmail, rawPassphrase) {
|
|
const email = _sanitizeEmail(rawEmail);
|
|
const passphrase = _sanitizePassphrase(rawPassphrase);
|
|
|
|
const account = await _initAccount(firstName, lastName, email);
|
|
const authSecret = await crypt.deriveKey(passphrase, account.email, account.saltKey);
|
|
const derivedSymmetricKey = await crypt.deriveKey(passphrase, account.email, account.saltEnc);
|
|
|
|
// Compute the verifier key
|
|
// Add verifier to account object
|
|
account.verifier = srp.computeVerifier(
|
|
_getSrpParams(),
|
|
Buffer.from(account.saltAuth, 'hex'),
|
|
Buffer.from(account.email, 'utf8'),
|
|
Buffer.from(authSecret, 'hex')
|
|
).toString('hex');
|
|
|
|
// Generate keypair
|
|
const {publicKey, privateKey} = await crypt.generateKeyPairJWK();
|
|
const symmetricKeyJWK = await crypt.generateAES256Key();
|
|
|
|
// Encode keypair
|
|
const encSymmetricJWKMessage = crypt.encryptAES(derivedSymmetricKey, JSON.stringify(symmetricKeyJWK));
|
|
const encPrivateJWKMessage = crypt.encryptAES(symmetricKeyJWK, JSON.stringify(privateKey));
|
|
|
|
// Add keys to account
|
|
account.publicKey = JSON.stringify(publicKey);
|
|
account.encPrivateKey = JSON.stringify(encPrivateJWKMessage);
|
|
account.encSymmetricKey = JSON.stringify(encSymmetricJWKMessage);
|
|
|
|
return util.fetchPost('/auth/signup', account);
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a new session
|
|
*
|
|
* @returns {Promise}
|
|
* @param rawEmail
|
|
* @param rawPassphrase
|
|
*/
|
|
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 util.fetchPost('/auth/login-s', {email});
|
|
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 util.fetchPost(
|
|
'/auth/login-a',
|
|
{srpA, email}
|
|
);
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~ //
|
|
// Compute and Submit M1 //
|
|
// ~~~~~~~~~~~~~~~~~~~~~ //
|
|
|
|
c.setB(new Buffer(srpB, 'hex'));
|
|
const srpM1 = c.computeM1().toString('hex');
|
|
const {srpM2} = await util.fetchPost('/auth/login-m1', {
|
|
srpM1,
|
|
sessionStarterId,
|
|
});
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
// Verify Server Identity M2 //
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
|
|
c.checkM2(new Buffer(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
|
|
} = 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,
|
|
email,
|
|
JSON.parse(symmetricKeyStr),
|
|
JSON.parse(publicKey),
|
|
JSON.parse(encPrivateKey),
|
|
);
|
|
}
|
|
|
|
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 () {
|
|
return localStorage.getItem('currentSessionId') || NO_SESSION;
|
|
}
|
|
|
|
export function getAccountId () {
|
|
return getSessionData().accountId;
|
|
}
|
|
|
|
export function getEmail () {
|
|
return getSessionData().email;
|
|
}
|
|
|
|
export function getFirstName () {
|
|
return getSessionData().firstName;
|
|
}
|
|
|
|
/**
|
|
* get Data about the current session
|
|
* @returns Object
|
|
*/
|
|
export function getSessionData () {
|
|
const sessionId = getCurrentSessionId();
|
|
if (sessionId == NO_SESSION) {
|
|
return {};
|
|
}
|
|
|
|
const dataStr = localStorage.getItem(_getSessionKey(sessionId));
|
|
return JSON.parse(dataStr);
|
|
}
|
|
|
|
/**
|
|
* Set data for the new session and store it encrypted with the sessionId
|
|
*
|
|
* @param sessionId
|
|
* @param accountId
|
|
* @param firstName
|
|
* @param symmetricKey
|
|
* @param publicKey
|
|
* @param encPrivateKey
|
|
* @param email
|
|
*/
|
|
export function setSessionData (
|
|
sessionId,
|
|
accountId,
|
|
firstName,
|
|
email,
|
|
symmetricKey,
|
|
publicKey,
|
|
encPrivateKey
|
|
) {
|
|
const dataStr = JSON.stringify({
|
|
id: sessionId,
|
|
accountId: accountId,
|
|
symmetricKey: symmetricKey,
|
|
publicKey: publicKey,
|
|
encPrivateKey: encPrivateKey,
|
|
email: email,
|
|
firstName: firstName,
|
|
});
|
|
|
|
localStorage.setItem(_getSessionKey(sessionId), dataStr);
|
|
|
|
// NOTE: We're setting this last because the stuff above might fail
|
|
localStorage.setItem('currentSessionId', sessionId);
|
|
}
|
|
|
|
/**
|
|
* Unset the session data (log out)
|
|
*/
|
|
export function unsetSessionData () {
|
|
const sessionId = getCurrentSessionId();
|
|
localStorage.removeItem(_getSessionKey(sessionId));
|
|
localStorage.removeItem(`currentSessionId`);
|
|
}
|
|
|
|
/**
|
|
* Check if we (think) we have a session
|
|
*
|
|
* @returns {boolean}
|
|
*/
|
|
export function isLoggedIn () {
|
|
return getCurrentSessionId() !== NO_SESSION;
|
|
}
|
|
|
|
/**
|
|
* Log out
|
|
*/
|
|
export async function logout () {
|
|
await util.fetchPost('/auth/logout');
|
|
unsetSessionData();
|
|
}
|
|
|
|
/**
|
|
* Who Am I
|
|
*/
|
|
export function whoami (sessionId = null) {
|
|
return util.fetchGet('/auth/whoami', sessionId);
|
|
}
|
|
|
|
|
|
// ~~~~~~~~~~~~~~~~ //
|
|
// Helper Functions //
|
|
// ~~~~~~~~~~~~~~~~ //
|
|
|
|
function _getSessionKey (sessionId) {
|
|
return `session__${sessionId.slice(0, 10)}`
|
|
}
|
|
|
|
async function _initAccount (firstName, lastName, email) {
|
|
return {
|
|
email,
|
|
firstName,
|
|
lastName,
|
|
id: await crypt.generateAccountId(),
|
|
saltEnc: await crypt.getRandomHex(),
|
|
saltAuth: await crypt.getRandomHex(),
|
|
saltKey: await crypt.getRandomHex(),
|
|
};
|
|
}
|
|
|
|
function _getSrpParams () {
|
|
return srp.params[2048];
|
|
}
|
|
|
|
function _sanitizeEmail (email) {
|
|
return email.trim().toLowerCase();
|
|
}
|
|
|
|
function _sanitizePassphrase (passphrase) {
|
|
return passphrase.trim().normalize('NFKD');
|
|
}
|