Skip to content

Commit

Permalink
Merge pull request #13 from nearprotocol/j-wallet-access-key
Browse files Browse the repository at this point in the history
Access key integration initial take (a few known issues exist)
  • Loading branch information
janedegtiareva committed May 16, 2019
2 parents 74c4d3d + 4247845 commit 1fa679b
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 199 deletions.
152 changes: 54 additions & 98 deletions dist/nearlib.js
Expand Up @@ -14858,7 +14858,7 @@ class AccountInfo {
* @param {Object} json
*/
static fromJson(json) {
if (!json.public_key || !json.secret_key || !json.account_id || !json.network_id) {
if (!json.public_key || !json.secret_key || !json.account_id) {
throw 'Invalid account info format. Please ensure it contains public_key, secret_key, and account_id".';
}
return new AccountInfo(json.account_id, new KeyPair(json.public_key, json.secret_key), json.network_id);
Expand Down Expand Up @@ -14940,6 +14940,16 @@ class BrowserLocalStorageKeystore {
BrowserLocalStorageKeystore.storageKeyForSecretKey(accountId), key.getSecretKey());
}

/**
* Removes the key from local storage for a given id. Please be sure to store the key somewhere before doing this
* to prevent permanent key loss.
* @param {string} accountId
*/
async removeKey(accountId) {
this.localStorage.removeItem(BrowserLocalStorageKeystore.storageKeyForPublicKey(accountId));
this.localStorage.removeItem(BrowserLocalStorageKeystore.storageKeyForSecretKey(accountId));
}

async setKeyFromJson(json) {
const accountInfo = AccountInfo.fromJson(json);
if (this.networkId != accountInfo.networkId) {
Expand Down Expand Up @@ -14991,6 +15001,12 @@ class InMemoryKeyStore {
return this.keys[accountId + '_' + this.networkId];
}

async removeKey(accountId) {
if (this.getKey(accountId)) {
this.setKey(accountId, undefined);
}
}

async clear() {
this.keys = {};
}
Expand Down Expand Up @@ -15245,15 +15261,12 @@ module.exports = WalletAccessKey;
* Wallet based account and signer that uses external wallet through the iframe to sign transactions.
*/

const { sha256 } = require('js-sha256');
const { FunctionCallTransaction } = require('./protos');

const EMBED_WALLET_URL_SUFFIX = '/embed/';
const LOGIN_WALLET_URL_SUFFIX = '/login/';
const RANDOM_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const REQUEST_ID_LENGTH = 32;

const LOCAL_STORAGE_KEY_SUFFIX = '_wallet_auth_key';
const PENDING_ACCESS_KEY_PREFIX = 'pending_key'; // browser storage key for a pending access key (i.e. key has been generated but we are not sure it was added yet)

/**
* Wallet based account and signer that uses external wallet through the iframe to sign transactions.
Expand All @@ -15267,16 +15280,15 @@ const LOCAL_STORAGE_KEY_SUFFIX = '_wallet_auth_key';
*/
class WalletAccount {

constructor(appKeyPrefix, walletBaseUrl = 'https://wallet.nearprotocol.com') {
this._walletBaseUrl = walletBaseUrl;
constructor(appKeyPrefix, walletBaseUrl = 'https://wallet.nearprotocol.com', keyStore = new nearlib.BrowserLocalStorageKeystore()) {
this._walletBaseUrl = walletBaseUrl;
this._authDataKey = appKeyPrefix + LOCAL_STORAGE_KEY_SUFFIX;
this._keyStore = keyStore;

this._initHtmlElements();
this._signatureRequests = {};
this._authData = JSON.parse(window.localStorage.getItem(this._authDataKey) || '{}');

if (!this.isSignedIn()) {
this._tryInitFromUrl();
this._completeSignInWithAccessKey();
}
}

Expand Down Expand Up @@ -15319,99 +15331,43 @@ class WalletAccount {
newUrl.searchParams.set('success_url', success_url || currentUrl.href);
newUrl.searchParams.set('failure_url', failure_url || currentUrl.href);
newUrl.searchParams.set('app_url', currentUrl.origin);
window.location.replace(newUrl.toString());
if (!this.getAccountId() || !this._keyStore.getKey(this.getAccountId())) {
const accessKey = nearlib.KeyPair.fromRandomSeed();
newUrl.searchParams.set('public_key', accessKey.getPublicKey());
this._keyStore.setKey(PENDING_ACCESS_KEY_PREFIX + accessKey.getPublicKey(), accessKey).then(window.location.replace(newUrl.toString()));
}
}

/**
* Sign out from the current account
* @example
* walletAccount.signOut();
* Complete sign in for a given account id and public key. To be invoked by the app when getting a callback from the wallet.
*/
signOut() {
this._authData = {};
window.localStorage.removeItem(this._authDataKey);
}

_tryInitFromUrl() {
_completeSignInWithAccessKey() {
let currentUrl = new URL(window.location.href);
let authToken = currentUrl.searchParams.get('auth_token') || '';
let publicKey = currentUrl.searchParams.get('public_key') || '';
let accountId = currentUrl.searchParams.get('account_id') || '';
if (!!authToken && !!accountId) {
this._authData = {
authToken,
accountId,
};
window.localStorage.setItem(this._authDataKey, JSON.stringify(this._authData));
}
}

_initHtmlElements() {
// Wallet iframe
const iframe = document.createElement('iframe');
iframe.style = 'display: none;';
iframe.src = this._walletBaseUrl + EMBED_WALLET_URL_SUFFIX;
document.body.appendChild(iframe);
this._walletWindow = iframe.contentWindow;

// Message Event
window.addEventListener('message', this.receiveMessage.bind(this), false);
}

receiveMessage(event) {
if (!this._walletBaseUrl.startsWith(event.origin)) {
// Only processing wallet messages.
console.log('Wallet account ignoring message from ' + event.origin);
return;
}
let data;
try {
data = JSON.parse(event.data);
} catch (e) {
console.error('Can\'t parse the result', event.data, e);
return;
}
const request_id = data.request_id || '';
if (!(request_id in this._signatureRequests)) {
console.error('Request ID' + request_id + ' was not found');
return;
}
let signatureRequest = this._signatureRequests[request_id];
delete this._signatureRequests[request_id];

if (data.success) {
signatureRequest.resolve(data.result);
} else {
signatureRequest.reject(data.error);
if (accountId && publicKey) {
this._moveKeyFromTempToPermanent(accountId, publicKey);
}
}

_randomRequestId() {
var result = '';

for (var i = 0; i < REQUEST_ID_LENGTH; i++) {
result += RANDOM_ALPHABET.charAt(Math.floor(Math.random() * RANDOM_ALPHABET.length));
}

return result;
async _moveKeyFromTempToPermanent(accountId, publicKey) {
let keyPair = await this._keyStore.getKey(PENDING_ACCESS_KEY_PREFIX + publicKey);
this._authData = {
accountId
};
window.localStorage.setItem(this._authDataKey, JSON.stringify(this._authData));
await this._keyStore.setKey(accountId, keyPair);
await this._keyStore.removeKey(PENDING_ACCESS_KEY_PREFIX + publicKey);
}

_remoteSign(hash, methodName, args) {
// TODO(#482): Add timeout.
return new Promise((resolve, reject) => {
const request_id = this._randomRequestId();
this._signatureRequests[request_id] = {
request_id,
resolve,
reject,
};
this._walletWindow.postMessage(JSON.stringify({
action: 'sign_transaction',
token: this._authData.authToken,
method_name: methodName,
args: args || {},
hash,
request_id,
}), this._walletBaseUrl);
});
/**
* Sign out from the current account
* @example
* walletAccount.signOut();
*/
signOut() {
this._authData = {};
window.localStorage.removeItem(this._authDataKey);
}

/**
Expand All @@ -15427,15 +15383,15 @@ class WalletAccount {
const body = FunctionCallTransaction.decode(buffer);
let methodName = Buffer.from(body.methodName).toString();
let args = JSON.parse(Buffer.from(body.args).toString());
let signature = await this._remoteSign(sha256.array(buffer), methodName, args);
return {
signature,
};
const signer = new nearlib.SimpleKeyStoreSigner(this._keyStore);
let signature = await signer.signBuffer(buffer, originator);
console.log(signature);
return signature;
}

}

module.exports = WalletAccount;

}).call(this,require("buffer").Buffer)
},{"./protos":67,"buffer":21,"js-sha256":40}]},{},[2]);
},{"./protos":67,"buffer":21}]},{},[2]);
10 changes: 5 additions & 5 deletions dist/nearlib.min.js

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions signing/browser_local_storage_key_store.js
Expand Up @@ -35,6 +35,16 @@ class BrowserLocalStorageKeystore {
BrowserLocalStorageKeystore.storageKeyForSecretKey(accountId), key.getSecretKey());
}

/**
* Removes the key from local storage for a given id. Please be sure to store the key somewhere before doing this
* to prevent permanent key loss.
* @param {string} accountId
*/
async removeKey(accountId) {
this.localStorage.removeItem(BrowserLocalStorageKeystore.storageKeyForPublicKey(accountId));
this.localStorage.removeItem(BrowserLocalStorageKeystore.storageKeyForSecretKey(accountId));
}

async setKeyFromJson(json) {
const accountInfo = AccountInfo.fromJson(json);
if (this.networkId != accountInfo.networkId) {
Expand Down
6 changes: 6 additions & 0 deletions signing/in_memory_key_store.js
Expand Up @@ -15,6 +15,12 @@ class InMemoryKeyStore {
return this.keys[accountId + '_' + this.networkId];
}

async removeKey(accountId) {
if (this.getKey(accountId)) {
this.setKey(accountId, undefined);
}
}

async clear() {
this.keys = {};
}
Expand Down
6 changes: 6 additions & 0 deletions signing/unencrypted_file_system_keystore.js
Expand Up @@ -39,6 +39,12 @@ class UnencryptedFileSystemKeyStore {
return AccountInfo.fromJson(json).keyPair;
}

async removeKey(accountId) {
if (await promisify(fs.exists)(this.getKeyFilePath(accountId))) {
fs.unlink(this.getKeyFilePath(accountId));
}
}

/**
* Returns all account ids for a particular network.
*/
Expand Down

0 comments on commit 1fa679b

Please sign in to comment.