Skip to content

Commit

Permalink
SEP-10: muxed account & memo support (#709)
Browse files Browse the repository at this point in the history
Co-authored-by: George <Shaptic@users.noreply.github.com>
  • Loading branch information
JakeUrban and Shaptic committed Sep 16, 2021
1 parent 0d1ee7f commit 89201f4
Show file tree
Hide file tree
Showing 5 changed files with 1,276 additions and 1,093 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@

A breaking change will get clearly marked in this log.

## Unreleased
## [v8.3.0](https://github.com/stellar/js-stellar-sdk/compare/v8.2.5...v8.3.0)

### Updates

- Updates the following SEP-10 utility functions to be compilant with the protocols ([#709](https://github.com/stellar/js-stellar-sdk/pull/709/), [stellar-protocol/#1036](https://github.com/stellar/stellar-protocol/pull/1036))
- Updated `utils.buildChallengeTx()` to accept muxed accounts (`M...`) for client account IDs
- Updated `utils.buildChallengeTx()` to accept a `memo` parameter to attach to the challenge transaction
- Updated `utils.readChallengeTx()` to provide a `memo` property in the returned object
- Updated `utils.readChallengeTx()` to validate challenge transactions with muxed accounts (`M...`) as the client account ID
- Upgraded `js-stellar-base` package to version `^5.3.2` from `^6.0.3`, refer to its [release notes](https://github.com/stellar/js-stellar-base/releases/tag/v6.0.3) for more info ([#709](https://github.com/stellar/js-stellar-sdk/pull/709/), [stellar-protocol/#1036](https://github.com/stellar/stellar-protocol/pull/1036))

## [v8.2.5](https://github.com/stellar/js-stellar-sdk/compare/v8.2.4...v8.2.5)

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "stellar-sdk",
"version": "8.2.5",
"version": "8.3.0",
"description": "stellar-sdk is a library for working with the Stellar Horizon server.",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
Expand Down Expand Up @@ -137,7 +137,7 @@
"eventsource": "^1.0.7",
"lodash": "^4.17.11",
"randombytes": "^2.1.0",
"stellar-base": "^5.3.2",
"stellar-base": "^6.0.3",
"toml": "^2.3.0",
"tslib": "^1.10.0",
"urijs": "^1.19.1",
Expand Down
71 changes: 54 additions & 17 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import {
BASE_FEE,
FeeBumpTransaction,
Keypair,
Memo,
MemoID,
MemoNone,
Operation,
TimeoutInfinite,
Transaction,
Expand All @@ -25,11 +28,12 @@ export namespace Utils {
* @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} clientAccountID The stellar account (G...) or muxed account (M...) that the wallet wishes to authenticate with the server.
* @param {string} homeDomain The fully qualified domain name of the service requiring authentication
* @param {number} [timeout=300] Challenge duration (default to 5 minutes).
* @param {string} networkPassphrase The network passphrase. If you pass this argument then timeout is required.
* @param {string} webAuthDomain The fully qualified domain name of the service issuing the challenge.
* @param {string} [memo] The memo to attach to the challenge transaction. The memo must be of type `id`. If the `clientaccountID` is a muxed account, memos cannot be used.
* @example
* import { Utils, Keypair, Networks } from 'stellar-sdk'
*
Expand All @@ -44,11 +48,10 @@ export namespace Utils {
timeout: number = 300,
networkPassphrase: string,
webAuthDomain: string,
memo: string | null = null,
): string {
if (clientAccountID.startsWith("M")) {
throw Error(
"Invalid clientAccountID: multiplexed accounts are not supported.",
);
if (clientAccountID.startsWith("M") && memo) {
throw Error("memo cannot be used if clientAccountID is a muxed account");
}

const account = new Account(serverKeypair.publicKey(), "-1");
Expand All @@ -61,7 +64,7 @@ export namespace Utils {
// turned into binary represents 8 bits = 1 bytes.
const value = randomBytes(48).toString("base64");

const transaction = new TransactionBuilder(account, {
const builder = new TransactionBuilder(account, {
fee: BASE_FEE,
networkPassphrase,
timebounds: {
Expand All @@ -74,6 +77,7 @@ export namespace Utils {
name: `${homeDomain} auth`,
value,
source: clientAccountID,
withMuxing: true,
}),
)
.addOperation(
Expand All @@ -82,9 +86,13 @@ export namespace Utils {
value: webAuthDomain,
source: account.accountId(),
}),
)
.build();
);

if (memo) {
builder.addMemo(Memo.id(memo));
}

const transaction = builder.build();
transaction.sign(serverKeypair);

return transaction
Expand Down Expand Up @@ -113,27 +121,41 @@ export namespace Utils {
* @param {string} networkPassphrase The network passphrase, e.g.: 'Test SDF Network ; September 2015'.
* @param {string|string[]} [homeDomains] The home domain that is expected to be included in the first Manage Data operation's string key. If an array is provided, one of the domain names in the array must match.
* @param {string} webAuthDomain The home domain that is expected to be included as the value of the Manage Data operation with the 'web_auth_domain' key. If no such operation is included, this parameter is not used.
* @returns {Transaction|string|string} The actual transaction and the stellar public key (master key) used to sign the Manage Data operation, and matched home domain.
* @returns {Transaction|string|string|string} The actual transaction and the stellar public key (master key) used to sign the Manage Data operation, the matched home domain, and the memo attached to the transaction, which will be null if not present.
*/
export function readChallengeTx(
challengeTx: string,
serverAccountID: string,
networkPassphrase: string,
homeDomains: string | string[],
webAuthDomain: string,
): { tx: Transaction; clientAccountID: string; matchedHomeDomain: string } {
): {
tx: Transaction;
clientAccountID: string;
matchedHomeDomain: string;
memo: string | null;
} {
if (serverAccountID.startsWith("M")) {
throw Error(
"Invalid serverAccountID: multiplexed accounts are not supported.",
);
}

const transaction = TransactionBuilder.fromXDR(
challengeTx,
networkPassphrase,
);

if (!(transaction instanceof Transaction)) {
let transaction;
try {
transaction = new Transaction(challengeTx, networkPassphrase, true);
} catch {
try {
transaction = new FeeBumpTransaction(
challengeTx,
networkPassphrase,
true,
);
} catch {
throw new InvalidSep10ChallengeError(
"Invalid challenge: unable to deserialize challengeTx transaction string",
);
}
throw new InvalidSep10ChallengeError(
"Invalid challenge: expected a Transaction but received a FeeBumpTransaction",
);
Expand Down Expand Up @@ -171,6 +193,21 @@ export namespace Utils {
}
const clientAccountID: string = operation.source!;

let memo: string | null = null;
if (transaction.memo.type !== MemoNone) {
if (clientAccountID.startsWith("M")) {
throw new InvalidSep10ChallengeError(
"The transaction has a memo but the client account ID is a muxed account",
);
}
if (transaction.memo.type !== MemoID) {
throw new InvalidSep10ChallengeError(
"The transaction's memo must be of type `id`",
);
}
memo = transaction.memo.value as string;
}

if (operation.type !== "manageData") {
throw new InvalidSep10ChallengeError(
"The transaction's operation type should be 'manageData'",
Expand Down Expand Up @@ -272,7 +309,7 @@ export namespace Utils {
);
}

return { tx: transaction, clientAccountID, matchedHomeDomain };
return { tx: transaction, clientAccountID, matchedHomeDomain, memo };
}

/**
Expand Down
Loading

0 comments on commit 89201f4

Please sign in to comment.