From 58bafb115cd924e20e13aa7a3c2e6abcc1bd91fc Mon Sep 17 00:00:00 2001 From: Jake Urban Date: Tue, 24 Jan 2023 12:54:18 -0800 Subject: [PATCH] fix tests, update webauth server --- .../webauth/internal/serve/challenge.go | 12 +++ exp/services/webauth/internal/serve/token.go | 14 ++- .../webauth/internal/serve/token_test.go | 12 +++ txnbuild/example_test.go | 2 +- txnbuild/transaction.go | 12 ++- .../transaction_challenge_example_test.go | 6 +- txnbuild/transaction_test.go | 95 ++++++++++--------- 7 files changed, 96 insertions(+), 57 deletions(-) diff --git a/exp/services/webauth/internal/serve/challenge.go b/exp/services/webauth/internal/serve/challenge.go index a01458215d..20e1635c61 100644 --- a/exp/services/webauth/internal/serve/challenge.go +++ b/exp/services/webauth/internal/serve/challenge.go @@ -2,6 +2,7 @@ package serve import ( "net/http" + "strconv" "strings" "time" @@ -57,6 +58,16 @@ func (h challengeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { homeDomain = h.HomeDomains[0] } + var memo txnbuild.MemoID + if queryValues.Get("memo") != "" { + memoInt, err := strconv.ParseUint(queryValues.Get("memo"), 10, 64) + if err != nil { + badRequest.Render(w) + return + } + memo = txnbuild.MemoID(memoInt) + } + tx, err := txnbuild.BuildChallengeTx( h.SigningKey.Seed(), account, @@ -64,6 +75,7 @@ func (h challengeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { homeDomain, h.NetworkPassphrase, h.ChallengeExpiresIn, + memo, ) if err != nil { h.Logger.Ctx(ctx).WithStack(err).Error(err) diff --git a/exp/services/webauth/internal/serve/token.go b/exp/services/webauth/internal/serve/token.go index 56db5f269f..c69ce6586a 100644 --- a/exp/services/webauth/internal/serve/token.go +++ b/exp/services/webauth/internal/serve/token.go @@ -11,6 +11,7 @@ import ( supportlog "github.com/stellar/go/support/log" "github.com/stellar/go/support/render/httpjson" "github.com/stellar/go/txnbuild" + "github.com/stellar/go/xdr" "gopkg.in/square/go-jose.v2" "gopkg.in/square/go-jose.v2/jwt" ) @@ -52,9 +53,10 @@ func (h tokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { clientAccountID string signingAddress *keypair.FromAddress homeDomain string + memo txnbuild.Memo ) for _, s := range h.SigningAddresses { - tx, clientAccountID, homeDomain, err = txnbuild.ReadChallengeTx(req.Transaction, s.Address(), h.NetworkPassphrase, h.Domain, h.HomeDomains) + tx, clientAccountID, homeDomain, memo, err = txnbuild.ReadChallengeTx(req.Transaction, s.Address(), h.NetworkPassphrase, h.Domain, h.HomeDomains) if err == nil { signingAddress = s break @@ -80,10 +82,16 @@ func (h tokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { WithField("tx", hash). WithField("account", clientAccountID). WithField("serversigner", signingAddress.Address()). - WithField("homedomain", homeDomain) + WithField("homedomain", homeDomain). + WithField("memo", memo) l.Info("Start verifying challenge transaction.") + var muxedAccount xdr.MuxedAccount + if muxedAccount, err = xdr.AddressToMuxedAccount(clientAccountID); err == nil { + clientAccountID = muxedAccount.ToAccountId().Address() + } + var clientAccountExists bool clientAccount, err := h.HorizonClient.AccountDetail(horizonclient.AccountRequest{AccountID: clientAccountID}) switch { @@ -143,7 +151,7 @@ func (h tokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { issuedAt := time.Unix(tx.Timebounds().MinTime, 0) claims := jwt.Claims{ Issuer: h.JWTIssuer, - Subject: clientAccountID, + Subject: muxedAccount.Address(), IssuedAt: jwt.NewNumericDate(issuedAt), Expiry: jwt.NewNumericDate(issuedAt.Add(h.JWTExpiresIn)), } diff --git a/exp/services/webauth/internal/serve/token_test.go b/exp/services/webauth/internal/serve/token_test.go index 7aa41d4b78..1c2745fa5a 100644 --- a/exp/services/webauth/internal/serve/token_test.go +++ b/exp/services/webauth/internal/serve/token_test.go @@ -46,6 +46,7 @@ func TestToken_formInputSuccess(t *testing.T) { homeDomain, network.TestNetworkPassphrase, time.Minute, + nil, ) require.NoError(t, err) @@ -146,6 +147,7 @@ func TestToken_formInputSuccess_jwtHeaderAndPayloadAreDeterministic(t *testing.T homeDomain, network.TestNetworkPassphrase, time.Minute, + nil, ) require.NoError(t, err) @@ -255,6 +257,7 @@ func TestToken_jsonInputSuccess(t *testing.T) { homeDomain, network.TestNetworkPassphrase, time.Minute, + nil, ) require.NoError(t, err) @@ -412,6 +415,7 @@ func TestToken_jsonInputValidRotatingServerSigners(t *testing.T) { homeDomain, network.TestNetworkPassphrase, time.Minute, + nil, ) require.NoError(t, err) @@ -497,6 +501,7 @@ func TestToken_jsonInputValidMultipleSigners(t *testing.T) { homeDomain, network.TestNetworkPassphrase, time.Minute, + nil, ) require.NoError(t, err) @@ -605,6 +610,7 @@ func TestToken_jsonInputNotEnoughWeight(t *testing.T) { homeDomain, network.TestNetworkPassphrase, time.Minute, + nil, ) require.NoError(t, err) @@ -691,6 +697,7 @@ func TestToken_jsonInputUnrecognizedSigner(t *testing.T) { homeDomain, network.TestNetworkPassphrase, time.Minute, + nil, ) require.NoError(t, err) @@ -777,6 +784,7 @@ func TestToken_jsonInputAccountNotExistSuccess(t *testing.T) { homeDomain, network.TestNetworkPassphrase, time.Minute, + nil, ) require.NoError(t, err) @@ -881,6 +889,7 @@ func TestToken_jsonInputAccountNotExistFail(t *testing.T) { homeDomain, network.TestNetworkPassphrase, time.Minute, + nil, ) require.NoError(t, err) @@ -963,6 +972,7 @@ func TestToken_jsonInputAccountNotExistNotAllowed(t *testing.T) { homeDomain, network.TestNetworkPassphrase, time.Minute, + nil, ) require.NoError(t, err) @@ -1047,6 +1057,7 @@ func TestToken_jsonInputUnrecognizedServerSigner(t *testing.T) { homeDomain, network.TestNetworkPassphrase, time.Minute, + nil, ) require.NoError(t, err) @@ -1248,6 +1259,7 @@ func TestToken_jsonInputInvalidWebAuthDomainFail(t *testing.T) { homeDomain, network.TestNetworkPassphrase, time.Minute, + nil, ) require.NoError(t, err) diff --git a/txnbuild/example_test.go b/txnbuild/example_test.go index 3aab258ab9..26b5d95187 100644 --- a/txnbuild/example_test.go +++ b/txnbuild/example_test.go @@ -760,7 +760,7 @@ func ExampleBuildChallengeTx() { webAuthDomain := "webauthdomain.example.org" timebound := time.Duration(5 * time.Minute) - tx, err := BuildChallengeTx(serverSignerSeed, clientAccountID, webAuthDomain, anchorName, network.TestNetworkPassphrase, timebound) + tx, err := BuildChallengeTx(serverSignerSeed, clientAccountID, webAuthDomain, anchorName, network.TestNetworkPassphrase, timebound, nil) check(err) txeBase64, err := tx.Base64() diff --git a/txnbuild/transaction.go b/txnbuild/transaction.go index f7a874056f..af23b48831 100644 --- a/txnbuild/transaction.go +++ b/txnbuild/transaction.go @@ -1113,7 +1113,7 @@ func generateRandomNonce(n int) ([]byte, error) { // one of the following functions to completely verify the transaction: // - VerifyChallengeTxThreshold // - VerifyChallengeTxSigners -func ReadChallengeTx(challengeTx, serverAccountID, network, webAuthDomain string, homeDomains []string) (tx *Transaction, clientAccountID string, matchedHomeDomain string, memo MemoID, err error) { +func ReadChallengeTx(challengeTx, serverAccountID, network, webAuthDomain string, homeDomains []string) (tx *Transaction, clientAccountID string, matchedHomeDomain string, memo Memo, err error) { parsed, err := TransactionFromXDR(challengeTx) if err != nil { return tx, clientAccountID, matchedHomeDomain, memo, errors.Wrap(err, "could not parse challenge") @@ -1176,10 +1176,16 @@ func ReadChallengeTx(challengeTx, serverAccountID, network, webAuthDomain string } clientAccountID = op.SourceAccount + memo = tx.Memo() rawOperations := tx.envelope.Operations() - if len(rawOperations) > 0 && rawOperations[0].SourceAccount.Type == xdr.CryptoKeyTypeKeyTypeMuxedEd25519 && tx.Memo() != nil { + if rawOperations[0].SourceAccount.Type == xdr.CryptoKeyTypeKeyTypeMuxedEd25519 && memo != nil { err = errors.New("memos are not valid for challenge transactions with a muxed client account") return tx, clientAccountID, matchedHomeDomain, memo, err + } else if rawOperations[0].SourceAccount.Type == xdr.CryptoKeyTypeKeyTypeEd25519 && memo != nil { + if rawMemo, err := memo.ToXDR(); err != nil || rawMemo.Type != xdr.MemoTypeMemoId { + err = errors.New("invalid memo, only ID memos are permitted") + return tx, clientAccountID, matchedHomeDomain, memo, err + } } // verify manage data value @@ -1297,7 +1303,7 @@ func VerifyChallengeTxThreshold(challengeTx, serverAccountID, network, webAuthDo // server account or one of the signers provided in the arguments. func VerifyChallengeTxSigners(challengeTx, serverAccountID, network, webAuthDomain string, homeDomains []string, signers ...string) ([]string, error) { // Read the transaction which validates its structure. - tx, _, _, err := ReadChallengeTx(challengeTx, serverAccountID, network, webAuthDomain, homeDomains) + tx, _, _, _, err := ReadChallengeTx(challengeTx, serverAccountID, network, webAuthDomain, homeDomains) if err != nil { return nil, err } diff --git a/txnbuild/transaction_challenge_example_test.go b/txnbuild/transaction_challenge_example_test.go index a183aa44f4..220782579e 100644 --- a/txnbuild/transaction_challenge_example_test.go +++ b/txnbuild/transaction_challenge_example_test.go @@ -37,7 +37,7 @@ func ExampleVerifyChallengeTxThreshold() { // Server builds challenge transaction var challengeTx string { - tx, err := txnbuild.BuildChallengeTx(serverAccount.Seed(), clientAccount.Address(), "webauthdomain.stellar.org", "test", network.TestNetworkPassphrase, time.Minute) + tx, err := txnbuild.BuildChallengeTx(serverAccount.Seed(), clientAccount.Address(), "webauthdomain.stellar.org", "test", network.TestNetworkPassphrase, time.Minute, nil) if err != nil { fmt.Println("Error:", err) return @@ -52,7 +52,7 @@ func ExampleVerifyChallengeTxThreshold() { // Client reads and signs challenge transaction var signedChallengeTx string { - tx, txClientAccountID, _, err := txnbuild.ReadChallengeTx(challengeTx, serverAccount.Address(), network.TestNetworkPassphrase, "webauthdomain.stellar.org", []string{"test"}) + tx, txClientAccountID, _, _, err := txnbuild.ReadChallengeTx(challengeTx, serverAccount.Address(), network.TestNetworkPassphrase, "webauthdomain.stellar.org", []string{"test"}) if err != nil { fmt.Println("Error:", err) return @@ -75,7 +75,7 @@ func ExampleVerifyChallengeTxThreshold() { // Server verifies signed challenge transaction { - _, txClientAccountID, _, err := txnbuild.ReadChallengeTx(challengeTx, serverAccount.Address(), network.TestNetworkPassphrase, "webauthdomain.stellar.org", []string{"test"}) + _, txClientAccountID, _, _, err := txnbuild.ReadChallengeTx(challengeTx, serverAccount.Address(), network.TestNetworkPassphrase, "webauthdomain.stellar.org", []string{"test"}) if err != nil { fmt.Println("Error:", err) return diff --git a/txnbuild/transaction_test.go b/txnbuild/transaction_test.go index b813dd8b95..b9452f2929 100644 --- a/txnbuild/transaction_test.go +++ b/txnbuild/transaction_test.go @@ -1073,7 +1073,7 @@ func TestBuildChallengeTx(t *testing.T) { { // 1 minute timebound - tx, err := BuildChallengeTx(kp0.Seed(), kp0.Address(), "testwebauth.stellar.org", "testanchor.stellar.org", network.TestNetworkPassphrase, time.Minute) + tx, err := BuildChallengeTx(kp0.Seed(), kp0.Address(), "testwebauth.stellar.org", "testanchor.stellar.org", network.TestNetworkPassphrase, time.Minute, nil) assert.NoError(t, err) txeBase64, err := tx.Base64() assert.NoError(t, err) @@ -1097,7 +1097,7 @@ func TestBuildChallengeTx(t *testing.T) { { // 5 minutes timebound - tx, err := BuildChallengeTx(kp0.Seed(), kp0.Address(), "testwebauth.stellar.org", "testanchor.stellar.org", network.TestNetworkPassphrase, time.Duration(5*time.Minute)) + tx, err := BuildChallengeTx(kp0.Seed(), kp0.Address(), "testwebauth.stellar.org", "testanchor.stellar.org", network.TestNetworkPassphrase, time.Duration(5*time.Minute), nil) assert.NoError(t, err) txeBase64, err := tx.Base64() assert.NoError(t, err) @@ -1121,7 +1121,7 @@ func TestBuildChallengeTx(t *testing.T) { } //transaction with infinite timebound - _, err := BuildChallengeTx(kp0.Seed(), kp0.Address(), "webauthdomain", "sdf", network.TestNetworkPassphrase, 0) + _, err := BuildChallengeTx(kp0.Seed(), kp0.Address(), "webauthdomain", "sdf", network.TestNetworkPassphrase, 0, nil) if assert.Error(t, err) { assert.Contains(t, err.Error(), "provided timebound must be at least 1s (300s is recommended)") } @@ -1878,7 +1878,7 @@ func TestReadChallengeTx_validSignedByServerAndClient(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, clientKP.Address(), readClientAccountID) assert.NoError(t, err) @@ -1913,7 +1913,7 @@ func TestReadChallengeTx_validSignedByServer(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, clientKP.Address(), readClientAccountID) assert.NoError(t, err) @@ -1946,7 +1946,7 @@ func TestReadChallengeTx_invalidNotSignedByServer(t *testing.T) { tx64, err := tx.Base64() require.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, clientKP.Address(), readClientAccountID) assert.EqualError(t, err, "transaction not signed by "+serverKP.Address()) @@ -1982,7 +1982,7 @@ func TestReadChallengeTx_invalidCorrupted(t *testing.T) { tx64, err := tx.Base64() require.NoError(t, err) tx64 = strings.ReplaceAll(tx64, "A", "B") - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Nil(t, readTx) assert.Equal(t, "", readClientAccountID) assert.EqualError( @@ -2022,7 +2022,7 @@ func TestReadChallengeTx_invalidServerAccountIDMismatch(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, "", readClientAccountID) assert.EqualError(t, err, "transaction source account is not equal to server's account") @@ -2057,7 +2057,7 @@ func TestReadChallengeTx_invalidSeqNoNotZero(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, "", readClientAccountID) assert.EqualError(t, err, "transaction sequence number must be 0") @@ -2092,7 +2092,7 @@ func TestReadChallengeTx_invalidTimeboundsInfinite(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, "", readClientAccountID) assert.EqualError(t, err, "transaction requires non-infinite timebounds") @@ -2127,7 +2127,7 @@ func TestReadChallengeTx_invalidTimeboundsOutsideRange(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, "", readClientAccountID) assert.Error(t, err) @@ -2164,7 +2164,7 @@ func TestReadChallengeTx_validTimeboundsWithGracePeriod(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, clientKP.Address(), readClientAccountID) assert.NoError(t, err) @@ -2200,7 +2200,7 @@ func TestReadChallengeTx_invalidTimeboundsWithGracePeriod(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, "", readClientAccountID) assert.Error(t, err) @@ -2230,7 +2230,7 @@ func TestReadChallengeTx_invalidOperationWrongType(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, "", readClientAccountID) assert.EqualError(t, err, "operation type should be manage_data") @@ -2258,7 +2258,7 @@ func TestReadChallengeTx_invalidOperationNoSourceAccount(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - _, _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + _, _, _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.EqualError(t, err, "operation should have a source account") } @@ -2291,7 +2291,7 @@ func TestReadChallengeTx_invalidDataValueWrongEncodedLength(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, clientKP.Address(), readClientAccountID) assert.EqualError(t, err, "random nonce encoded as base64 should be 64 bytes long") @@ -2326,7 +2326,7 @@ func TestReadChallengeTx_invalidDataValueCorruptBase64(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, clientKP.Address(), readClientAccountID) assert.EqualError(t, err, "failed to decode random nonce provided in manage_data operation: illegal base64 data at input byte 37") @@ -2362,7 +2362,7 @@ func TestReadChallengeTx_invalidDataValueWrongByteLength(t *testing.T) { tx64, err := tx.Base64() assert.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, clientKP.Address(), readClientAccountID) assert.EqualError(t, err, "random nonce before encoding as base64 should be 48 bytes long") @@ -2377,6 +2377,7 @@ func TestReadChallengeTx_acceptsV0AndV1Transactions(t *testing.T) { "testanchor.stellar.org", network.TestNetworkPassphrase, time.Hour, + nil, ) assert.NoError(t, err) @@ -2391,7 +2392,7 @@ func TestReadChallengeTx_acceptsV0AndV1Transactions(t *testing.T) { assert.NoError(t, err) for _, challenge := range []string{v1Challenge, v0Challenge} { - parsedTx, clientAccountID, _, err := ReadChallengeTx( + parsedTx, clientAccountID, _, _, err := ReadChallengeTx( challenge, kp0.Address(), network.TestNetworkPassphrase, @@ -2417,6 +2418,7 @@ func TestReadChallengeTx_forbidsFeeBumpTransactions(t *testing.T) { "testanchor.stellar.org", network.TestNetworkPassphrase, time.Hour, + nil, ) assert.NoError(t, err) @@ -2435,7 +2437,7 @@ func TestReadChallengeTx_forbidsFeeBumpTransactions(t *testing.T) { challenge, err := feeBumpTx.Base64() assert.NoError(t, err) - _, _, _, err = ReadChallengeTx( + _, _, _, _, err = ReadChallengeTx( challenge, kp0.Address(), network.TestNetworkPassphrase, @@ -2445,41 +2447,40 @@ func TestReadChallengeTx_forbidsFeeBumpTransactions(t *testing.T) { assert.EqualError(t, err, "challenge cannot be a fee bump transaction") } -func TestReadChallengeTx_forbidsMuxedAccounts(t *testing.T) { +func TestReadChallengeTx_allowsMuxedAccounts(t *testing.T) { kp0 := newKeypair0() + kp1 := newKeypair1() + aid := xdr.MustAddress(kp1.Address()) + muxedAccount := xdr.MuxedAccount{ + Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, + Med25519: &xdr.MuxedAccountMed25519{ + Id: 0xcafebabe, + Ed25519: *aid.Ed25519, + }, + } tx, err := BuildChallengeTx( kp0.Seed(), - kp0.Address(), + muxedAccount.Address(), "testwebauth.stellar.org", "testanchor.stellar.org", network.TestNetworkPassphrase, time.Hour, + nil, ) - - env := tx.ToXDR() assert.NoError(t, err) - aid := xdr.MustAddress(kp0.Address()) - muxedAccount := xdr.MuxedAccount{ - Type: xdr.CryptoKeyTypeKeyTypeMuxedEd25519, - Med25519: &xdr.MuxedAccountMed25519{ - Id: 0xcafebabe, - Ed25519: *aid.Ed25519, - }, - } - *env.V1.Tx.Operations[0].SourceAccount = muxedAccount - challenge, err := marshallBase64(env, env.Signatures()) + challenge, err := marshallBase64(tx.ToXDR(), tx.Signatures()) assert.NoError(t, err) - _, _, _, err = ReadChallengeTx( + tx, _, _, _, err = ReadChallengeTx( challenge, kp0.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}, ) - errorMessage := "only valid Ed25519 accounts are allowed in challenge transactions" - assert.Contains(t, err.Error(), errorMessage) + assert.NoError(t, err) + assert.Equal(t, tx.envelope.Operations()[0].SourceAccount, &muxedAccount) } func TestReadChallengeTx_doesVerifyHomeDomainFailure(t *testing.T) { @@ -2511,7 +2512,7 @@ func TestReadChallengeTx_doesVerifyHomeDomainFailure(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - _, _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"willfail"}) + _, _, _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"willfail"}) assert.EqualError(t, err, "operation key does not match any homeDomains passed (key=\"testanchor.stellar.org auth\", homeDomains=[willfail])") } @@ -2544,7 +2545,7 @@ func TestReadChallengeTx_doesVerifyHomeDomainSuccess(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - _, _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + _, _, _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, nil, err) } @@ -2582,7 +2583,7 @@ func TestReadChallengeTx_allowsAdditionalManageDataOpsWithSourceAccountSetToServ assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, clientKP.Address(), readClientAccountID) assert.NoError(t, err) @@ -2622,7 +2623,7 @@ func TestReadChallengeTx_disallowsAdditionalManageDataOpsWithoutSourceAccountSet assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - _, _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + _, _, _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.EqualError(t, err, "subsequent operations are unrecognized") } @@ -2659,7 +2660,7 @@ func TestReadChallengeTx_disallowsAdditionalOpsOfOtherTypes(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - readTx, readClientAccountID, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + readTx, readClientAccountID, _, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.Equal(t, tx, readTx) assert.Equal(t, clientKP.Address(), readClientAccountID) assert.EqualError(t, err, "operation type should be manage_data") @@ -2693,7 +2694,7 @@ func TestReadChallengeTx_matchesHomeDomain(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - _, _, matchedHomeDomain, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + _, _, matchedHomeDomain, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) require.NoError(t, err) assert.Equal(t, matchedHomeDomain, "testanchor.stellar.org") } @@ -2726,7 +2727,7 @@ func TestReadChallengeTx_doesNotMatchHomeDomain(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - _, _, matchedHomeDomain, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"not", "going", "to", "match"}) + _, _, matchedHomeDomain, _, err := ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"not", "going", "to", "match"}) assert.Equal(t, matchedHomeDomain, "") assert.EqualError(t, err, "operation key does not match any homeDomains passed (key=\"testanchor.stellar.org auth\", homeDomains=[not going to match])") } @@ -2754,7 +2755,7 @@ func TestReadChallengeTx_validWhenWebAuthDomainMissing(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - _, _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + _, _, _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.NoError(t, err) } @@ -2786,7 +2787,7 @@ func TestReadChallengeTx_invalidWebAuthDomainSourceAccount(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - _, _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + _, _, _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.EqualError(t, err, `web auth domain operation must have server source account`) } @@ -2818,7 +2819,7 @@ func TestReadChallengeTx_invalidWebAuthDomain(t *testing.T) { assert.NoError(t, err) tx64, err := tx.Base64() require.NoError(t, err) - _, _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) + _, _, _, _, err = ReadChallengeTx(tx64, serverKP.Address(), network.TestNetworkPassphrase, "testwebauth.stellar.org", []string{"testanchor.stellar.org"}) assert.EqualError(t, err, `web auth domain operation value is "testwebauth.example.org" but expect "testwebauth.stellar.org"`) }