@stellar/typescript-wallet-sdk 1.10.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.MD +15 -0
- package/lib/bundle.js +24992 -15442
- package/lib/bundle.js.map +1 -1
- package/lib/bundle_browser.js +264 -190
- package/lib/bundle_browser.js.map +1 -1
- package/lib/walletSdk/Auth/index.d.ts +2 -2
- package/lib/walletSdk/Exceptions/index.d.ts +3 -0
- package/lib/walletSdk/Types/recovery.d.ts +1 -1
- package/package.json +3 -3
- package/src/walletSdk/Anchor/index.ts +4 -0
- package/src/walletSdk/Auth/index.ts +10 -154
- package/src/walletSdk/Exceptions/index.ts +9 -0
- package/src/walletSdk/Recovery/index.ts +1 -1
- package/src/walletSdk/Types/recovery.ts +1 -1
- package/test/auth.test.ts +5 -301
- package/test/integration/anchorplatform.test.ts +2 -2
- package/test/integration/recovery.test.ts +2 -0
- package/webpack.config.js +3 -0
|
@@ -7,7 +7,7 @@ type Sep10Params = {
|
|
|
7
7
|
webAuthEndpoint: string;
|
|
8
8
|
homeDomain: string;
|
|
9
9
|
httpClient: AxiosInstance;
|
|
10
|
-
serverSigningKey
|
|
10
|
+
serverSigningKey: string;
|
|
11
11
|
};
|
|
12
12
|
/**
|
|
13
13
|
* @alias Auth alias for Sep10 class.
|
|
@@ -24,7 +24,7 @@ export declare class Sep10 {
|
|
|
24
24
|
private webAuthEndpoint;
|
|
25
25
|
private homeDomain;
|
|
26
26
|
private httpClient;
|
|
27
|
-
private serverSigningKey
|
|
27
|
+
private serverSigningKey;
|
|
28
28
|
/**
|
|
29
29
|
* Creates a new instance of the Sep10 class.
|
|
30
30
|
*
|
|
@@ -103,6 +103,9 @@ export declare class NoAccountAndNoSponsorError extends Error {
|
|
|
103
103
|
export declare class Sep38PriceOnlyOneAmountError extends Error {
|
|
104
104
|
constructor();
|
|
105
105
|
}
|
|
106
|
+
export declare class MissingSigningKeyError extends Error {
|
|
107
|
+
constructor();
|
|
108
|
+
}
|
|
106
109
|
export declare class ChallengeValidationFailedError extends Error {
|
|
107
110
|
constructor(cause: Error);
|
|
108
111
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stellar/typescript-wallet-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"engines": {
|
|
5
|
-
"node": ">=
|
|
5
|
+
"node": ">=20"
|
|
6
6
|
},
|
|
7
7
|
"browser": "./lib/bundle_browser.js",
|
|
8
8
|
"main": "./lib/bundle.js",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@stablelib/base64": "^2.0.0",
|
|
49
49
|
"@stablelib/utf8": "^2.0.0",
|
|
50
|
-
"@stellar/stellar-sdk": "
|
|
50
|
+
"@stellar/stellar-sdk": "14.5.0",
|
|
51
51
|
"axios": "^1.4.0",
|
|
52
52
|
"base64url": "^3.0.1",
|
|
53
53
|
"https-browserify": "^1.0.0",
|
|
@@ -7,6 +7,7 @@ import { Customer, Sep12 } from "../Customer";
|
|
|
7
7
|
import {
|
|
8
8
|
ServerRequestFailedError,
|
|
9
9
|
KYCServerNotFoundError,
|
|
10
|
+
MissingSigningKeyError,
|
|
10
11
|
} from "../Exceptions";
|
|
11
12
|
import { Sep6, Transfer } from "./Sep6";
|
|
12
13
|
import { Interactive, Sep24 } from "./Sep24";
|
|
@@ -104,6 +105,9 @@ export class Anchor {
|
|
|
104
105
|
*/
|
|
105
106
|
async sep10(): Promise<Sep10> {
|
|
106
107
|
const tomlInfo = await this.sep1();
|
|
108
|
+
if (!tomlInfo.signingKey) {
|
|
109
|
+
throw new MissingSigningKeyError();
|
|
110
|
+
}
|
|
107
111
|
return new Sep10({
|
|
108
112
|
cfg: this.cfg,
|
|
109
113
|
webAuthEndpoint: tomlInfo.webAuthEndpoint,
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { AxiosInstance } from "axios";
|
|
2
|
-
import {
|
|
3
|
-
TransactionBuilder,
|
|
4
|
-
Transaction,
|
|
5
|
-
FeeBumpTransaction,
|
|
6
|
-
WebAuth,
|
|
7
|
-
} from "@stellar/stellar-sdk";
|
|
2
|
+
import { TransactionBuilder, Transaction, WebAuth } from "@stellar/stellar-sdk";
|
|
8
3
|
import { decode } from "jws";
|
|
9
4
|
|
|
10
5
|
import { Config } from "../";
|
|
@@ -38,7 +33,7 @@ type Sep10Params = {
|
|
|
38
33
|
webAuthEndpoint: string;
|
|
39
34
|
homeDomain: string;
|
|
40
35
|
httpClient: AxiosInstance;
|
|
41
|
-
serverSigningKey
|
|
36
|
+
serverSigningKey: string;
|
|
42
37
|
};
|
|
43
38
|
|
|
44
39
|
/**
|
|
@@ -57,7 +52,7 @@ export class Sep10 {
|
|
|
57
52
|
private webAuthEndpoint: string;
|
|
58
53
|
private homeDomain: string;
|
|
59
54
|
private httpClient: AxiosInstance;
|
|
60
|
-
private serverSigningKey
|
|
55
|
+
private serverSigningKey: string;
|
|
61
56
|
|
|
62
57
|
/**
|
|
63
58
|
* Creates a new instance of the Sep10 class.
|
|
@@ -176,22 +171,13 @@ export class Sep10 {
|
|
|
176
171
|
try {
|
|
177
172
|
const webAuthDomain = new URL(this.webAuthEndpoint).hostname;
|
|
178
173
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
);
|
|
187
|
-
} else {
|
|
188
|
-
readChallengeTx(
|
|
189
|
-
challengeResponse.transaction,
|
|
190
|
-
networkPassphrase,
|
|
191
|
-
this.homeDomain,
|
|
192
|
-
webAuthDomain,
|
|
193
|
-
);
|
|
194
|
-
}
|
|
174
|
+
WebAuth.readChallengeTx(
|
|
175
|
+
challengeResponse.transaction,
|
|
176
|
+
this.serverSigningKey,
|
|
177
|
+
networkPassphrase,
|
|
178
|
+
this.homeDomain,
|
|
179
|
+
webAuthDomain,
|
|
180
|
+
);
|
|
195
181
|
} catch (e) {
|
|
196
182
|
throw new ChallengeValidationFailedError(
|
|
197
183
|
e instanceof Error ? e : new Error(String(e)),
|
|
@@ -255,136 +241,6 @@ export const validateToken = (token: string) => {
|
|
|
255
241
|
}
|
|
256
242
|
};
|
|
257
243
|
|
|
258
|
-
/*
|
|
259
|
-
* Validates a SEP-10 challenge transaction without requiring the server's
|
|
260
|
-
* signing key. This performs all structural validations from the SEP-10 spec
|
|
261
|
-
* (sequence number, operation types, timebounds, home domain, web_auth_domain,
|
|
262
|
-
* nonce format) but skips the server account and signature checks.
|
|
263
|
-
*
|
|
264
|
-
* Used as a fallback when the anchor's stellar.toml does not publish a
|
|
265
|
-
* SIGNING_KEY, providing strong protection against malformed or malicious
|
|
266
|
-
* challenge transactions.
|
|
267
|
-
*
|
|
268
|
-
* @internal
|
|
269
|
-
* @see {@link https://github.com/stellar/js-stellar-sdk/blob/v13.0.0-beta.1/src/webauth/utils.ts#L188 | WebAuth.readChallengeTx}
|
|
270
|
-
*/
|
|
271
|
-
const readChallengeTx = (
|
|
272
|
-
challengeTx: string,
|
|
273
|
-
networkPassphrase: string,
|
|
274
|
-
homeDomain: string,
|
|
275
|
-
webAuthDomain: string,
|
|
276
|
-
): { tx: Transaction; clientAccountID: string } => {
|
|
277
|
-
let transaction: Transaction;
|
|
278
|
-
try {
|
|
279
|
-
transaction = new Transaction(challengeTx, networkPassphrase);
|
|
280
|
-
} catch {
|
|
281
|
-
try {
|
|
282
|
-
// eslint-disable-next-line no-new
|
|
283
|
-
new FeeBumpTransaction(challengeTx, networkPassphrase);
|
|
284
|
-
} catch {
|
|
285
|
-
throw new Error(
|
|
286
|
-
"Invalid challenge: unable to deserialize challengeTx transaction string",
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
throw new Error(
|
|
290
|
-
"Invalid challenge: expected a Transaction but received a FeeBumpTransaction",
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// verify sequence number
|
|
295
|
-
const sequence = Number.parseInt(transaction.sequence, 10);
|
|
296
|
-
if (sequence !== 0) {
|
|
297
|
-
throw new Error("The transaction sequence number should be zero");
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// verify operations
|
|
301
|
-
if (transaction.operations.length < 1) {
|
|
302
|
-
throw new Error("The transaction should contain at least one operation");
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const [operation, ...subsequentOperations] = transaction.operations;
|
|
306
|
-
|
|
307
|
-
if (!operation.source) {
|
|
308
|
-
throw new Error(
|
|
309
|
-
"The transaction's operation should contain a source account",
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
const clientAccountID: string = operation.source;
|
|
313
|
-
|
|
314
|
-
// verify memo
|
|
315
|
-
if (transaction.memo.type !== "none") {
|
|
316
|
-
if (clientAccountID.startsWith("M")) {
|
|
317
|
-
throw new Error(
|
|
318
|
-
"The transaction has a memo but the client account ID is a muxed account",
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
if (transaction.memo.type !== "id") {
|
|
322
|
-
throw new Error("The transaction's memo must be of type `id`");
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
if (operation.type !== "manageData") {
|
|
327
|
-
throw new Error("The transaction's operation type should be 'manageData'");
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// verify timebounds
|
|
331
|
-
if (!transaction.timeBounds) {
|
|
332
|
-
throw new Error("The transaction requires timebounds");
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
if (Number.parseInt(transaction.timeBounds.maxTime, 10) === 0) {
|
|
336
|
-
throw new Error("The transaction requires non-infinite timebounds");
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const now = Math.floor(Date.now() / 1000);
|
|
340
|
-
const gracePeriod = 60 * 5;
|
|
341
|
-
const minTime = Number.parseInt(transaction.timeBounds.minTime, 10) || 0;
|
|
342
|
-
const maxTime = Number.parseInt(transaction.timeBounds.maxTime, 10) || 0;
|
|
343
|
-
if (now < minTime - gracePeriod || now > maxTime + gracePeriod) {
|
|
344
|
-
throw new Error("The transaction has expired");
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// verify nonce value
|
|
348
|
-
if (operation.value === undefined || !operation.value) {
|
|
349
|
-
throw new Error("The transaction's operation value should not be null");
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
if (Buffer.from(operation.value.toString(), "base64").length !== 48) {
|
|
353
|
-
throw new Error(
|
|
354
|
-
"The transaction's operation value should be a 64 bytes base64 random string",
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// verify home domain
|
|
359
|
-
if (`${homeDomain} auth` !== operation.name) {
|
|
360
|
-
throw new Error(
|
|
361
|
-
"Invalid homeDomains: the transaction's operation key name " +
|
|
362
|
-
"does not match the expected home domain",
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// verify subsequent operations are all manageData
|
|
367
|
-
for (const op of subsequentOperations) {
|
|
368
|
-
if (op.type !== "manageData") {
|
|
369
|
-
throw new Error(
|
|
370
|
-
"The transaction has operations that are not of type 'manageData'",
|
|
371
|
-
);
|
|
372
|
-
}
|
|
373
|
-
if (op.name === "web_auth_domain") {
|
|
374
|
-
if (op.value === undefined) {
|
|
375
|
-
throw new Error("'web_auth_domain' operation value should not be null");
|
|
376
|
-
}
|
|
377
|
-
if (op.value.compare(Buffer.from(webAuthDomain)) !== 0) {
|
|
378
|
-
throw new Error(
|
|
379
|
-
`'web_auth_domain' operation value does not match ${webAuthDomain}`,
|
|
380
|
-
);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
return { tx: transaction, clientAccountID };
|
|
386
|
-
};
|
|
387
|
-
|
|
388
244
|
const createAuthSignToken = async (
|
|
389
245
|
account: AccountKeypair,
|
|
390
246
|
claims: AuthHeaderClaims,
|
|
@@ -280,6 +280,15 @@ export class Sep38PriceOnlyOneAmountError extends Error {
|
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
+
export class MissingSigningKeyError extends Error {
|
|
284
|
+
constructor() {
|
|
285
|
+
super(
|
|
286
|
+
"Anchor's stellar.toml does not contain a SIGNING_KEY, which is required for SEP-10 authentication",
|
|
287
|
+
);
|
|
288
|
+
Object.setPrototypeOf(this, MissingSigningKeyError.prototype);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
283
292
|
export class ChallengeValidationFailedError extends Error {
|
|
284
293
|
constructor(cause: Error) {
|
|
285
294
|
super(`SEP-10 challenge validation failed: ${cause.message}`);
|
|
@@ -76,7 +76,7 @@ export class Recovery extends AccountRecover {
|
|
|
76
76
|
webAuthEndpoint: server.authEndpoint,
|
|
77
77
|
homeDomain: server.homeDomain,
|
|
78
78
|
httpClient: this.httpClient,
|
|
79
|
-
|
|
79
|
+
serverSigningKey: server.signingKey,
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
|
package/test/auth.test.ts
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
TransactionBuilder as SdkTransactionBuilder,
|
|
12
12
|
Operation,
|
|
13
13
|
BASE_FEE,
|
|
14
|
-
xdr as StellarXdr,
|
|
15
14
|
} from "@stellar/stellar-sdk";
|
|
16
15
|
import { randomBytes } from "crypto";
|
|
17
16
|
import axios from "axios";
|
|
@@ -30,6 +29,7 @@ import {
|
|
|
30
29
|
ExpiredTokenError,
|
|
31
30
|
ChallengeValidationFailedError,
|
|
32
31
|
NetworkPassphraseMismatchError,
|
|
32
|
+
MissingSigningKeyError,
|
|
33
33
|
} from "../src/walletSdk/Exceptions";
|
|
34
34
|
|
|
35
35
|
const createToken = (payload: Record<string, unknown>): string => {
|
|
@@ -261,7 +261,7 @@ describe("Sep10 challenge validation", () => {
|
|
|
261
261
|
token,
|
|
262
262
|
responseNetworkPassphrase = networkPassphrase,
|
|
263
263
|
}: {
|
|
264
|
-
serverSigningKey
|
|
264
|
+
serverSigningKey: string;
|
|
265
265
|
challengeXdr: string;
|
|
266
266
|
token: string;
|
|
267
267
|
responseNetworkPassphrase?: string;
|
|
@@ -282,7 +282,7 @@ describe("Sep10 challenge validation", () => {
|
|
|
282
282
|
webAuthEndpoint,
|
|
283
283
|
homeDomain,
|
|
284
284
|
httpClient,
|
|
285
|
-
|
|
285
|
+
serverSigningKey,
|
|
286
286
|
});
|
|
287
287
|
|
|
288
288
|
return { sep10, postStub };
|
|
@@ -618,301 +618,6 @@ describe("Sep10 challenge validation", () => {
|
|
|
618
618
|
});
|
|
619
619
|
});
|
|
620
620
|
|
|
621
|
-
// ============================================================
|
|
622
|
-
// WITHOUT serverSigningKey — uses local readChallengeTx
|
|
623
|
-
// ============================================================
|
|
624
|
-
describe("without serverSigningKey (local readChallengeTx)", () => {
|
|
625
|
-
const authenticateWithoutKey = (
|
|
626
|
-
challengeXdr: string,
|
|
627
|
-
clientKeypair: Keypair,
|
|
628
|
-
) => {
|
|
629
|
-
const accountKp = SigningKeypair.fromSecret(clientKeypair.secret());
|
|
630
|
-
const token = createJwt(clientKeypair);
|
|
631
|
-
const { sep10, postStub } = setupSep10({
|
|
632
|
-
challengeXdr,
|
|
633
|
-
token,
|
|
634
|
-
});
|
|
635
|
-
return { sep10, accountKp, postStub };
|
|
636
|
-
};
|
|
637
|
-
|
|
638
|
-
it("should accept a valid challenge", async () => {
|
|
639
|
-
const { xdr, clientKeypair } = buildChallenge();
|
|
640
|
-
const { sep10, accountKp } = authenticateWithoutKey(xdr, clientKeypair);
|
|
641
|
-
const authToken = await sep10.authenticate({ accountKp });
|
|
642
|
-
expect(authToken.account).toBe(clientKeypair.publicKey());
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
it("should reject invalid XDR", async () => {
|
|
646
|
-
const clientKeypair = Keypair.random();
|
|
647
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
648
|
-
"not-valid-xdr",
|
|
649
|
-
clientKeypair,
|
|
650
|
-
);
|
|
651
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
652
|
-
ChallengeValidationFailedError,
|
|
653
|
-
);
|
|
654
|
-
expect(postStub.notCalled).toBe(true);
|
|
655
|
-
});
|
|
656
|
-
|
|
657
|
-
it("should reject a FeeBumpTransaction", async () => {
|
|
658
|
-
const { xdr: innerXdr, serverKeypair, clientKeypair } = buildChallenge();
|
|
659
|
-
const innerTx = new Transaction(innerXdr, networkPassphrase);
|
|
660
|
-
const feeBump = SdkTransactionBuilder.buildFeeBumpTransaction(
|
|
661
|
-
serverKeypair,
|
|
662
|
-
BASE_FEE,
|
|
663
|
-
innerTx,
|
|
664
|
-
networkPassphrase,
|
|
665
|
-
);
|
|
666
|
-
feeBump.sign(serverKeypair);
|
|
667
|
-
|
|
668
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
669
|
-
feeBump.toXDR(),
|
|
670
|
-
clientKeypair,
|
|
671
|
-
);
|
|
672
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
673
|
-
ChallengeValidationFailedError,
|
|
674
|
-
);
|
|
675
|
-
expect(postStub.notCalled).toBe(true);
|
|
676
|
-
});
|
|
677
|
-
|
|
678
|
-
it("should reject non-zero sequence number", async () => {
|
|
679
|
-
const { xdr, clientKeypair } = buildChallenge({ sequence: "99" });
|
|
680
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
681
|
-
xdr,
|
|
682
|
-
clientKeypair,
|
|
683
|
-
);
|
|
684
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
685
|
-
ChallengeValidationFailedError,
|
|
686
|
-
);
|
|
687
|
-
expect(postStub.notCalled).toBe(true);
|
|
688
|
-
});
|
|
689
|
-
|
|
690
|
-
it("should reject a challenge with no operations", async () => {
|
|
691
|
-
const serverKeypair = Keypair.random();
|
|
692
|
-
const clientKeypair = Keypair.random();
|
|
693
|
-
const serverAccount = new Account(serverKeypair.publicKey(), "-1");
|
|
694
|
-
const tx = new SdkTransactionBuilder(serverAccount, {
|
|
695
|
-
fee: BASE_FEE,
|
|
696
|
-
networkPassphrase,
|
|
697
|
-
})
|
|
698
|
-
.setTimeout(300)
|
|
699
|
-
.build();
|
|
700
|
-
tx.sign(serverKeypair);
|
|
701
|
-
|
|
702
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
703
|
-
tx.toXDR(),
|
|
704
|
-
clientKeypair,
|
|
705
|
-
);
|
|
706
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
707
|
-
ChallengeValidationFailedError,
|
|
708
|
-
);
|
|
709
|
-
expect(postStub.notCalled).toBe(true);
|
|
710
|
-
});
|
|
711
|
-
|
|
712
|
-
it("should reject first operation without source account", async () => {
|
|
713
|
-
const { xdr, clientKeypair } = buildChallenge({
|
|
714
|
-
omitFirstOpSource: true,
|
|
715
|
-
});
|
|
716
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
717
|
-
xdr,
|
|
718
|
-
clientKeypair,
|
|
719
|
-
);
|
|
720
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
721
|
-
ChallengeValidationFailedError,
|
|
722
|
-
);
|
|
723
|
-
expect(postStub.notCalled).toBe(true);
|
|
724
|
-
});
|
|
725
|
-
|
|
726
|
-
it("should reject memo with muxed client account", async () => {
|
|
727
|
-
const clientKeypair = Keypair.random();
|
|
728
|
-
const baseAccount = new Account(clientKeypair.publicKey(), "0");
|
|
729
|
-
const muxed = new MuxedAccount(baseAccount, "123");
|
|
730
|
-
|
|
731
|
-
const { xdr } = buildChallenge({
|
|
732
|
-
clientKeypair,
|
|
733
|
-
clientSource: muxed.accountId(),
|
|
734
|
-
memo: Memo.id("456"),
|
|
735
|
-
});
|
|
736
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
737
|
-
xdr,
|
|
738
|
-
clientKeypair,
|
|
739
|
-
);
|
|
740
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
741
|
-
ChallengeValidationFailedError,
|
|
742
|
-
);
|
|
743
|
-
expect(postStub.notCalled).toBe(true);
|
|
744
|
-
});
|
|
745
|
-
|
|
746
|
-
it("should reject non-id memo type", async () => {
|
|
747
|
-
const { xdr, clientKeypair } = buildChallenge({
|
|
748
|
-
memo: Memo.text("test"),
|
|
749
|
-
});
|
|
750
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
751
|
-
xdr,
|
|
752
|
-
clientKeypair,
|
|
753
|
-
);
|
|
754
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
755
|
-
ChallengeValidationFailedError,
|
|
756
|
-
);
|
|
757
|
-
expect(postStub.notCalled).toBe(true);
|
|
758
|
-
});
|
|
759
|
-
|
|
760
|
-
it("should reject first operation that is not manageData", async () => {
|
|
761
|
-
const { xdr, clientKeypair } = buildChallenge({
|
|
762
|
-
firstOpType: "payment",
|
|
763
|
-
});
|
|
764
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
765
|
-
xdr,
|
|
766
|
-
clientKeypair,
|
|
767
|
-
);
|
|
768
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
769
|
-
ChallengeValidationFailedError,
|
|
770
|
-
);
|
|
771
|
-
expect(postStub.notCalled).toBe(true);
|
|
772
|
-
});
|
|
773
|
-
|
|
774
|
-
it("should reject infinite timebounds (maxTime=0)", async () => {
|
|
775
|
-
const { xdr, clientKeypair } = buildChallenge({
|
|
776
|
-
useExplicitTimebounds: true,
|
|
777
|
-
minTime: 0,
|
|
778
|
-
maxTime: 0,
|
|
779
|
-
});
|
|
780
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
781
|
-
xdr,
|
|
782
|
-
clientKeypair,
|
|
783
|
-
);
|
|
784
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
785
|
-
ChallengeValidationFailedError,
|
|
786
|
-
);
|
|
787
|
-
expect(postStub.notCalled).toBe(true);
|
|
788
|
-
});
|
|
789
|
-
|
|
790
|
-
it("should reject expired timebounds", async () => {
|
|
791
|
-
const now = Math.floor(Date.now() / 1000);
|
|
792
|
-
const { xdr, clientKeypair } = buildChallenge({
|
|
793
|
-
useExplicitTimebounds: true,
|
|
794
|
-
minTime: now - 7200,
|
|
795
|
-
maxTime: now - 3600,
|
|
796
|
-
});
|
|
797
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
798
|
-
xdr,
|
|
799
|
-
clientKeypair,
|
|
800
|
-
);
|
|
801
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
802
|
-
ChallengeValidationFailedError,
|
|
803
|
-
);
|
|
804
|
-
expect(postStub.notCalled).toBe(true);
|
|
805
|
-
});
|
|
806
|
-
|
|
807
|
-
it("should reject missing nonce value", async () => {
|
|
808
|
-
const { xdr, clientKeypair } = buildChallenge({ omitNonce: true });
|
|
809
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
810
|
-
xdr,
|
|
811
|
-
clientKeypair,
|
|
812
|
-
);
|
|
813
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
814
|
-
ChallengeValidationFailedError,
|
|
815
|
-
);
|
|
816
|
-
expect(postStub.notCalled).toBe(true);
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
it("should reject wrong nonce length", async () => {
|
|
820
|
-
const { xdr, clientKeypair } = buildChallenge({
|
|
821
|
-
nonce: randomBytes(16).toString("base64"),
|
|
822
|
-
});
|
|
823
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
824
|
-
xdr,
|
|
825
|
-
clientKeypair,
|
|
826
|
-
);
|
|
827
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
828
|
-
ChallengeValidationFailedError,
|
|
829
|
-
);
|
|
830
|
-
expect(postStub.notCalled).toBe(true);
|
|
831
|
-
});
|
|
832
|
-
|
|
833
|
-
it("should reject wrong home domain", async () => {
|
|
834
|
-
const { xdr, clientKeypair } = buildChallenge({
|
|
835
|
-
challengeHomeDomain: "evil.example.com",
|
|
836
|
-
});
|
|
837
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
838
|
-
xdr,
|
|
839
|
-
clientKeypair,
|
|
840
|
-
);
|
|
841
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
842
|
-
ChallengeValidationFailedError,
|
|
843
|
-
);
|
|
844
|
-
expect(postStub.notCalled).toBe(true);
|
|
845
|
-
});
|
|
846
|
-
|
|
847
|
-
it("should reject subsequent non-manageData operation", async () => {
|
|
848
|
-
const serverKeypair = Keypair.random();
|
|
849
|
-
const { xdr, clientKeypair } = buildChallenge({
|
|
850
|
-
serverKeypair,
|
|
851
|
-
additionalOps: [
|
|
852
|
-
Operation.payment({
|
|
853
|
-
destination: serverKeypair.publicKey(),
|
|
854
|
-
asset: Asset.native(),
|
|
855
|
-
amount: "1",
|
|
856
|
-
}),
|
|
857
|
-
],
|
|
858
|
-
});
|
|
859
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
860
|
-
xdr,
|
|
861
|
-
clientKeypair,
|
|
862
|
-
);
|
|
863
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
864
|
-
ChallengeValidationFailedError,
|
|
865
|
-
);
|
|
866
|
-
expect(postStub.notCalled).toBe(true);
|
|
867
|
-
});
|
|
868
|
-
|
|
869
|
-
it("should reject null web_auth_domain value", async () => {
|
|
870
|
-
const { xdr, clientKeypair } = buildChallenge({
|
|
871
|
-
webAuthDomainValue: null,
|
|
872
|
-
});
|
|
873
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
874
|
-
xdr,
|
|
875
|
-
clientKeypair,
|
|
876
|
-
);
|
|
877
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
878
|
-
ChallengeValidationFailedError,
|
|
879
|
-
);
|
|
880
|
-
expect(postStub.notCalled).toBe(true);
|
|
881
|
-
});
|
|
882
|
-
|
|
883
|
-
it("should reject mismatched web_auth_domain", async () => {
|
|
884
|
-
const { xdr, clientKeypair } = buildChallenge({
|
|
885
|
-
webAuthDomainValue: "evil.example.com",
|
|
886
|
-
});
|
|
887
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
888
|
-
xdr,
|
|
889
|
-
clientKeypair,
|
|
890
|
-
);
|
|
891
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
892
|
-
ChallengeValidationFailedError,
|
|
893
|
-
);
|
|
894
|
-
expect(postStub.notCalled).toBe(true);
|
|
895
|
-
});
|
|
896
|
-
|
|
897
|
-
it("should reject a challenge with missing timebounds", async () => {
|
|
898
|
-
const { xdr: txXdr, clientKeypair } = buildChallenge();
|
|
899
|
-
|
|
900
|
-
// Strip timebounds by setting preconditions to PRECOND_NONE in the XDR
|
|
901
|
-
const envelope = StellarXdr.TransactionEnvelope.fromXDR(txXdr, "base64");
|
|
902
|
-
envelope.v1().tx().cond(StellarXdr.Preconditions.precondNone());
|
|
903
|
-
const noTimeboundsXdr = envelope.toXDR().toString("base64");
|
|
904
|
-
|
|
905
|
-
const { sep10, accountKp, postStub } = authenticateWithoutKey(
|
|
906
|
-
noTimeboundsXdr,
|
|
907
|
-
clientKeypair,
|
|
908
|
-
);
|
|
909
|
-
await expect(sep10.authenticate({ accountKp })).rejects.toThrow(
|
|
910
|
-
ChallengeValidationFailedError,
|
|
911
|
-
);
|
|
912
|
-
expect(postStub.notCalled).toBe(true);
|
|
913
|
-
});
|
|
914
|
-
});
|
|
915
|
-
|
|
916
621
|
describe("network passphrase mismatch", () => {
|
|
917
622
|
it("should reject when server returns a different network passphrase", async () => {
|
|
918
623
|
const { xdr, serverKeypair, clientKeypair } = buildChallenge();
|
|
@@ -983,7 +688,7 @@ describe("Anchor.sep10() signing key handling", () => {
|
|
|
983
688
|
sinon.restore();
|
|
984
689
|
});
|
|
985
690
|
|
|
986
|
-
it("should
|
|
691
|
+
it("should throw MissingSigningKeyError when TOML has no SIGNING_KEY", async () => {
|
|
987
692
|
sinon.stub(StellarToml.Resolver, "resolve").resolves({
|
|
988
693
|
WEB_AUTH_ENDPOINT: "https://testanchor.stellar.org/auth",
|
|
989
694
|
DOCUMENTATION: {},
|
|
@@ -1001,8 +706,7 @@ describe("Anchor.sep10() signing key handling", () => {
|
|
|
1001
706
|
language: "en",
|
|
1002
707
|
});
|
|
1003
708
|
|
|
1004
|
-
|
|
1005
|
-
expect(sep10).toBeDefined();
|
|
709
|
+
await expect(anchor.sep10()).rejects.toThrow(MissingSigningKeyError);
|
|
1006
710
|
});
|
|
1007
711
|
|
|
1008
712
|
it("should succeed when TOML has SIGNING_KEY", async () => {
|
|
@@ -6,14 +6,14 @@ let wallet;
|
|
|
6
6
|
let stellar;
|
|
7
7
|
let anchor;
|
|
8
8
|
let accountKp;
|
|
9
|
-
const anchorUrl = "
|
|
9
|
+
const anchorUrl = "anchor-sep-server-dev.stellar.org";
|
|
10
10
|
|
|
11
11
|
describe("Anchor Platform Integration Tests", () => {
|
|
12
12
|
beforeAll(async () => {
|
|
13
13
|
// Setup
|
|
14
14
|
wallet = Wallet.TestNet();
|
|
15
15
|
stellar = wallet.stellar();
|
|
16
|
-
anchor = wallet.anchor({ homeDomain: anchorUrl
|
|
16
|
+
anchor = wallet.anchor({ homeDomain: anchorUrl });
|
|
17
17
|
accountKp = stellar.account().createKeypair();
|
|
18
18
|
await stellar.fundTestnetAccount(accountKp.publicKey);
|
|
19
19
|
}, 30000);
|
|
@@ -22,6 +22,7 @@ describe("Recovery Integration Tests", () => {
|
|
|
22
22
|
endpoint: "http://localhost:8000",
|
|
23
23
|
authEndpoint: "http://localhost:8001",
|
|
24
24
|
homeDomain: "test-domain",
|
|
25
|
+
signingKey: "GA43H67KTOSLFDM5LPUSJGIJSPWV6WVQN6FUYQWZKWJE5EMHXWE5EGK5",
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
const server2Key: RecoveryServerKey = "server2";
|
|
@@ -29,6 +30,7 @@ describe("Recovery Integration Tests", () => {
|
|
|
29
30
|
endpoint: "http://localhost:8002",
|
|
30
31
|
authEndpoint: "http://localhost:8003",
|
|
31
32
|
homeDomain: "test-domain",
|
|
33
|
+
signingKey: "GAZ2ITFWBXNT4SXSDEPM43IN72FQXOUCVXEXSHYX3VT2WZUI4E6UBSI2",
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
const servers: RecoveryServerMap = {
|