Skip to content

Commit

Permalink
Add SEP0010 transaction challenge builder. (#375)
Browse files Browse the repository at this point in the history
*  Implement SEP0010 transaction challenge builder.

* Improve docs.

* Use BASE_FEE instead of fixed number.

* Add example in jsdoc.

* Add randombytes.

* Use randomBytes instead of crypto.randomBytes.
  • Loading branch information
abuiles committed Jul 23, 2019
1 parent 087c412 commit 9a8dc5c
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 1 deletion.
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -131,13 +131,15 @@
"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",
"detect-node": "^2.0.4",
"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",
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Expand Up @@ -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";
Expand Down
66 changes: 66 additions & 0 deletions src/utils.ts
@@ -0,0 +1,66 @@
import randomBytes from "randombytes";
import {
Account,
BASE_FEE,
Keypair,
Operation,
TransactionBuilder,
} from "stellar-base";

/**
* @namespace Utils
*/
export namespace Utils {
/**
* 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)
* @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).
* @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,
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: BASE_FEE,
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();
}
}
45 changes: 45 additions & 0 deletions 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);
});
});
});
14 changes: 13 additions & 1 deletion yarn.lock
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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==
Expand Down

0 comments on commit 9a8dc5c

Please sign in to comment.