From eabd8edbf998f7b4e778fa89faa4f54ac22de873 Mon Sep 17 00:00:00 2001 From: Adolfo Builes Date: Fri, 19 Jul 2019 15:06:05 -0700 Subject: [PATCH 1/6] Implement SEP0010 transaction challenge builder. --- src/index.ts | 1 + src/utils.ts | 51 +++++++++++++++++++++++++++++++++++++++++ test/unit/utils_test.js | 45 ++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 src/utils.ts create mode 100644 test/unit/utils_test.js diff --git a/src/index.ts b/src/index.ts index ad07af319..ddd2f8688 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,6 +27,7 @@ export { SERVER_TIME_MAP, getCurrentServerTime, } from "./horizon_axios_client"; +export * from "./utils"; // expose classes and functions from stellar-base export * from "stellar-base"; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 000000000..5132da980 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,51 @@ +import { randomBytes } from "crypto"; +import { Account, Keypair, Operation, TransactionBuilder } from "stellar-base"; + +/** + * Collection on util functions + * + * @namespace Utils + */ +export namespace Utils { + /** + * Returns a valid SEP0010 challenge which you can use for Stellar Web Authentication. + * + * @see [SEP0010: Stellar Web Authentication](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md) + * @param {Keypair} serverKeypair Server's keypair. + * @param {string} clientAccountID Client's Stellar account. + * @param {string} anchorName. + * @param {number} [timeout=300] Challenge duration (default to 5 minutes). + */ + export function buildChallengeTx( + serverKeypair: Keypair, + clientAccountID: string, + anchorName: string, + timeout: number = 300, + ): string { + const account = new Account(serverKeypair.publicKey(), "-1"); + const now = Math.floor(Date.now() / 1000); + + const transaction = new TransactionBuilder(account, { + fee: 100, + timebounds: { + minTime: now, + maxTime: now + timeout, + }, + }) + .addOperation( + Operation.manageData({ + name: `${anchorName} auth`, + value: randomBytes(64), + source: clientAccountID, + }), + ) + .build(); + + transaction.sign(serverKeypair); + + return transaction + .toEnvelope() + .toXDR("base64") + .toString(); + } +} diff --git a/test/unit/utils_test.js b/test/unit/utils_test.js new file mode 100644 index 000000000..ef7357649 --- /dev/null +++ b/test/unit/utils_test.js @@ -0,0 +1,45 @@ +describe('Utils', function() { + describe('Utils.buildChallengeTx', function() { + it('returns challenge which follows SEP0010 spec', function() { + let keypair = StellarSdk.Keypair.random(); + + const challenge = StellarSdk.Utils.buildChallengeTx( + keypair, + "GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF", + "SDF" + ); + + const transaction = new StellarSdk.Transaction(challenge); + + expect(transaction.sequence).to.eql("0"); + expect(transaction.source).to.eql(keypair.publicKey()); + expect(transaction.operations.length).to.eql(1); + + const { maxTime, minTime } = transaction.timeBounds; + + expect(parseInt(maxTime) - parseInt(minTime)).to.eql(300); + + const [ operation ] = transaction.operations; + + expect(operation.name).to.eql("SDF auth"); + expect(operation.source).to.eql("GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF"); + expect(operation.type).to.eql("manageData"); + expect(operation.value.length).to.eql(64); + }); + + it('uses the passed-in timeout', function() { + let keypair = StellarSdk.Keypair.random(); + + const challenge = StellarSdk.Utils.buildChallengeTx( + keypair, + "GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF", + "SDF", + 600 + ); + + const transaction = new StellarSdk.Transaction(challenge); + const { maxTime, minTime } = transaction.timeBounds; + expect(parseInt(maxTime) - parseInt(minTime)).to.eql(600); + }); + }); +}); From 9a1e744cd6208a44a077ed862beb392be90adbaa Mon Sep 17 00:00:00 2001 From: Adolfo Builes Date: Mon, 22 Jul 2019 15:46:22 -0500 Subject: [PATCH 2/6] Improve docs. --- src/utils.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 5132da980..3b49376ba 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,19 +2,21 @@ import { randomBytes } from "crypto"; import { Account, Keypair, Operation, TransactionBuilder } from "stellar-base"; /** - * Collection on util functions - * * @namespace Utils */ export namespace Utils { /** - * Returns a valid SEP0010 challenge which you can use for Stellar Web Authentication. + * Returns a valid [SEP0010](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md) + * challenge transaction which you can use for Stellar Web Authentication. * * @see [SEP0010: Stellar Web Authentication](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md) - * @param {Keypair} serverKeypair Server's keypair. - * @param {string} clientAccountID Client's Stellar account. - * @param {string} anchorName. + * @function + * @memberof Utils + * @param {Keypair} serverKeypair Keypair for server's signing account. + * @param {string} clientAccountID The stellar account that the wallet wishes to authenticate with the server. + * @param {string} anchorName Anchor's name to be used in the manage_data key. * @param {number} [timeout=300] Challenge duration (default to 5 minutes). + * @returns {string} Transaction in Base64 */ export function buildChallengeTx( serverKeypair: Keypair, From 8a67e7949c24ad78d9ecd74ee1d579e0d55a552e Mon Sep 17 00:00:00 2001 From: Adolfo Builes Date: Tue, 23 Jul 2019 10:44:25 -0500 Subject: [PATCH 3/6] Use BASE_FEE instead of fixed number. --- src/utils.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 3b49376ba..f558290ba 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,11 @@ import { randomBytes } from "crypto"; -import { Account, Keypair, Operation, TransactionBuilder } from "stellar-base"; +import { + Account, + BASE_FEE, + Keypair, + Operation, + TransactionBuilder, +} from "stellar-base"; /** * @namespace Utils @@ -28,7 +34,7 @@ export namespace Utils { const now = Math.floor(Date.now() / 1000); const transaction = new TransactionBuilder(account, { - fee: 100, + fee: BASE_FEE, timebounds: { minTime: now, maxTime: now + timeout, From e99ce82bec91ff59b58cf92b95a0a2cee56fc15d Mon Sep 17 00:00:00 2001 From: Adolfo Builes Date: Tue, 23 Jul 2019 11:27:09 -0500 Subject: [PATCH 4/6] Add example in jsdoc. --- src/utils.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index f558290ba..0210619ae 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -22,7 +22,14 @@ export namespace Utils { * @param {string} clientAccountID The stellar account that the wallet wishes to authenticate with the server. * @param {string} anchorName Anchor's name to be used in the manage_data key. * @param {number} [timeout=300] Challenge duration (default to 5 minutes). - * @returns {string} Transaction in Base64 + * @example + * import { Utils, Keypair, Network } from 'stellar-sdk' + * + * Network.useTestNetwork(); + * + * let serverKeyPair = Keypair.fromSecret("server-secret") + * let challenge = Utils.buildChallengeTx(serverKeyPair, "client-stellar-account-id", "SDF", 300) + * @returns {string} A base64 encoded string of the raw TransactionEnvelope xdr struct for the transaction. */ export function buildChallengeTx( serverKeypair: Keypair, From 9da1cbd0abd564d3050db04f85262b899c33ea99 Mon Sep 17 00:00:00 2001 From: Adolfo Builes Date: Tue, 23 Jul 2019 13:55:26 -0500 Subject: [PATCH 5/6] Add randombytes. --- package.json | 2 ++ yarn.lock | 14 +++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4f5d9e83a..ea0f655c7 100644 --- a/package.json +++ b/package.json @@ -131,6 +131,7 @@ "dependencies": { "@types/eventsource": "^1.1.2", "@types/node": ">= 8", + "@types/randombytes": "^2.0.0", "@types/urijs": "^1.19.2", "axios": "^0.19.0", "bignumber.js": "^4.0.0", @@ -138,6 +139,7 @@ "es6-promise": "^4.2.4", "eventsource": "^1.0.7", "lodash": "^4.17.11", + "randombytes": "^2.1.0", "stellar-base": "^1.0.3", "toml": "^2.3.0", "tslib": "^1.10.0", diff --git a/yarn.lock b/yarn.lock index 535f97a8e..aa871a823 100644 --- a/yarn.lock +++ b/yarn.lock @@ -132,6 +132,11 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.130.tgz#04b3a690d5f4fc34579963c99adae067b8c8eb5a" integrity sha512-H++wk0tbneBsRVfLkgAAd0IIpmpVr2Bj4T0HncoOsQf3/xrJexRYQK2Tqo0Ej3pFslM8GkMgdis9bu6xIb1ycw== +"@types/node@*": + version "12.6.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.6.8.tgz#e469b4bf9d1c9832aee4907ba8a051494357c12c" + integrity sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg== + "@types/node@>= 8": version "11.13.8" resolved "https://registry.yarnpkg.com/@types/node/-/node-11.13.8.tgz#e5d71173c95533be9842b2c798978f095f912aab" @@ -142,6 +147,13 @@ resolved "https://registry.yarnpkg.com/@types/parsimmon/-/parsimmon-1.10.0.tgz#ffb81cb023ff435a41d4710a29ab23c561dc9fdf" integrity sha512-bsTIJFVQv7jnvNiC42ld2pQW2KRI+pAG243L+iATvqzy3X6+NH1obz2itRKDZZ8VVhN3wjwYax/VBGCcXzgTqQ== +"@types/randombytes@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/randombytes/-/randombytes-2.0.0.tgz#0087ff5e60ae68023b9bc4398b406fea7ad18304" + integrity sha512-bz8PhAVlwN72vqefzxa14DKNT8jK/mV66CSjwdVQM/k3Th3EPKfUtdMniwZgMedQTFuywAsfjnZsg+pEnltaMA== + dependencies: + "@types/node" "*" + "@types/urijs@^1.19.2": version "1.19.2" resolved "https://registry.yarnpkg.com/@types/urijs/-/urijs-1.19.2.tgz#45212f53c3f14f1d90ca823a719b164748ad413b" @@ -6848,7 +6860,7 @@ randomatic@^3.0.0: kind-of "^6.0.0" math-random "^1.0.1" -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== From 862948b1ca8c42bfbe35c5a9f3d819a94625e7d0 Mon Sep 17 00:00:00 2001 From: Adolfo Builes Date: Tue, 23 Jul 2019 13:55:53 -0500 Subject: [PATCH 6/6] Use randomBytes instead of crypto.randomBytes. --- src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index 0210619ae..194a37022 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import { randomBytes } from "crypto"; +import randomBytes from "randombytes"; import { Account, BASE_FEE,