import omit from 'lodash/omit'
import { JsonApiServer } from '../json_api'
import { Wallet } from './wallet'
/**
* Facilitates interaction with the wallet server.
*
* @class
*/
export class WalletServer extends JsonApiServer {
/**
* Create a new wallet server instance.
*
* @constructor
* @param {ShelfNetwork} sdk Parent SDK instance.
* @param {string} serverUrl wallet server URL.
* @param {Object} opts
* @param {boolean} [opts.allowHttp] Allow connecting to http servers, default: `false`. This must be set to false in production deployments!
* @param {Object} [opts.proxy] Proxy configuration. Look [axios docs](https://github.com/axios/axios#request-config) for more info
* @param {Object} [opts.httpBasicAuth] HTTP basic auth credentials. Look [axios docs](https://github.com/axios/axios#request-config) for more info.
* @param {Object} [opts.customHeaders] Custom headers for request.
* @param {boolean} [opts.withCredentials] Indicates whether or not cross-site Access-Control requests should be made using credentials.
* @param {string} [opts.responseType='json'] Indicates the type of data that the server will respond with options are 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'.
*/
constructor (sdk, serverUrl, opts = {}) {
opts.alias = 'wallets'
super(sdk, serverUrl, opts)
}
/**
* Create an encrypted wallet.
*
* @param {Wallet} wallet User's wallet.
* @param {string} password User's password.
*
* @return {Promise.<Wallet>} User's wallet.
*/
async create (wallet, password) {
let kdfParams = await this._getKdfParams()
let encryptedWallet = wallet.encrypt(kdfParams, password)
await this._makeCallBuilder()
.appendUrlSegment('create')
.withCredentials()
.post({
data: {
type: 'wallets',
id: encryptedWallet.id,
attributes: omit(encryptedWallet, ['id'])
}
})
return wallet
}
/**
* Get user's wallet.
*
* @param {string} email User's email.
* @param {string} password User's password.
* @param {string} [accountId] User's account ID. Can be used instead of email.
* @param {string} [platformId] User's platform ID. Must be used with email.
*
* @return {Promise.<Wallet>} User's email.
*/
async get (email, password, accountId, platformId) {
let loginParams = await this._getLoginParams({
email,
accountId,
platformId
})
let kdfParams = JSON.parse(loginParams.kdfParams)
let walletId = Wallet.deriveId(
password,
kdfParams,
loginParams.salt
)
let encryptedWallet = await this._makeCallBuilder()
.appendUrlSegment('show')
.post({
data: {
type: 'wallets',
id: walletId
}
})
.then(response => response.data)
return Wallet.fromEncrypted(
encryptedWallet.keychainData,
kdfParams,
loginParams.salt,
password
)
}
/**
* Verify if password is matching the wallet's one.
*
* @param {string} walletId User's wallet ID.
* @param {string} password A password to be verified.
* @param {string} accountId Account ID.
*/
async checkPassword (walletId, accountId, password) {
let loginParams = await this._getLoginParams({ accountId })
let kdfParams = JSON.parse(loginParams.kdfParams)
let walletIdForVerification = Wallet.deriveId(
password,
kdfParams,
loginParams.salt
)
return walletIdForVerification === walletId
}
/**
* Change user's password.
*
* @param {string} newPassword User's password.
* @param {string} [wallet] A wallet to be updated.
*
* @return {Promise.<Wallet>} Updated wallet.
*/
async changePassword (newPassword, wallet = null) {
wallet = wallet || this._sdk.wallet
let newWallet = wallet.clone()
let kdfParams = await this._getKdfParams()
let encrypted = newWallet.encrypt(kdfParams, newPassword)
await this._updateWallet(encrypted, kdfParams)
return newWallet
}
/**
* Recover user's wallet.
*
* @param {string} accountId User's account ID.
* @param {Keypair} newSignerKeypair New singer's keypair.
* @param {string} newPassword New password.
*
* @return {Promise.<Wallet>} Updated wallet.
*/
async recover (accountId, newSignerKeypair, newPassword) {
let newWallet = new Wallet(newSignerKeypair, accountId)
let kdfParams = await this._getKdfParams()
let encrypted = newWallet.encrypt(kdfParams, newPassword)
await this._updateWallet(encrypted, kdfParams, newWallet)
return newWallet
}
_getLoginParams ({ email, accountId, platformId }) {
platformId = platformId || this._sdk.platformId
const query = accountId ? { accountId, platformId } : { email, platformId }
return this._makeCallBuilder()
.appendUrlSegment('show_login_params')
.get(query)
.then(response => response.data)
}
_getKdfParams () {
return this._makeCallBuilder()
.appendUrlSegment('kdf_params')
.get()
.then(response => response.data)
}
_updateWallet (encryptedWallet, kdfParams, wallet) {
return this._makeCallBuilder()
.withCredentials({ wallet })
.appendUrlSegment('update')
.post({
data: {
id: encryptedWallet.id,
type: 'wallets',
attributes: {
accountId: encryptedWallet.accountId,
newWalletId: encryptedWallet.id,
keychainData: encryptedWallet.keychainData,
salt: encryptedWallet.salt,
kdfParams: JSON.stringify(kdfParams)
}
}
})
}
}