Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

txnbuild: add muxed account & memo support to SEP-10 utility functions #4746

Merged
merged 23 commits into from
Jan 30, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
87f653b
add memo to BuildChallengeTx params and ReadChallengeTx return value
JakeUrban Jan 24, 2023
11d5a94
require memos of type ID in BuildChallengeTx
JakeUrban Jan 24, 2023
58bafb1
fix tests, update webauth server
JakeUrban Jan 24, 2023
d8ce2a5
reject requests with memo & muxed account
JakeUrban Jan 24, 2023
8ccbe3d
use correct 'sub' value for JWT
JakeUrban Jan 24, 2023
2ee1c58
minor adjustments for go check
JakeUrban Jan 24, 2023
7337242
added first test
JakeUrban Jan 24, 2023
af13430
add more tests
JakeUrban Jan 25, 2023
d318150
use Memo not MemoID type in challenge.go
JakeUrban Jan 25, 2023
9838244
add to function documentation
JakeUrban Jan 25, 2023
ebe0f2a
add to txnbuild CHANGELOG.md
JakeUrban Jan 25, 2023
9562534
Update txnbuild/transaction.go
JakeUrban Jan 25, 2023
aeff367
use *MemoID instead of Memo for param and return type
JakeUrban Jan 25, 2023
4678d6b
ensure memo in challenge.go can be nil
JakeUrban Jan 25, 2023
3fdf4d3
update CHANGELOG.md
JakeUrban Jan 25, 2023
229efa3
adjust test
JakeUrban Jan 25, 2023
e4288db
use badRequest instead of serverError when BuildChallengeTx() fails
JakeUrban Jan 25, 2023
b380001
add token tests
JakeUrban Jan 26, 2023
396c454
add challenge tests
JakeUrban Jan 26, 2023
59b6ae2
add bad memo test for challenge endpoint
JakeUrban Jan 26, 2023
6699055
add tests for uncovered conditions
JakeUrban Jan 26, 2023
57f94e2
Merge branch 'master' into sep10-muxed-account-support
JakeUrban Jan 27, 2023
0f31bdd
change server error to bad request in token endpoint
JakeUrban Jan 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions exp/services/webauth/internal/serve/challenge.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package serve

import (
"net/http"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -33,7 +34,9 @@ func (h challengeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
queryValues := r.URL.Query()

account := queryValues.Get("account")
if !strkey.IsValidEd25519PublicKey(account) {
isStellarAccount := strkey.IsValidEd25519PublicKey(account)
isMuxedAccount := strkey.IsValidMuxedAccountEd25519PublicKey(account)
if !isStellarAccount && !isMuxedAccount {
badRequest.Render(w)
return
}
Expand All @@ -57,17 +60,30 @@ func (h challengeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
homeDomain = h.HomeDomains[0]
}

var memo *txnbuild.MemoID
memoParam := queryValues.Get("memo")
if memoParam != "" {
memoInt, err := strconv.ParseUint(memoParam, 10, 64)
if err != nil {
badRequest.Render(w)
return
}
memoId := txnbuild.MemoID(memoInt)
memo = &memoId
JakeUrban marked this conversation as resolved.
Show resolved Hide resolved
}

tx, err := txnbuild.BuildChallengeTx(
h.SigningKey.Seed(),
account,
h.Domain,
homeDomain,
h.NetworkPassphrase,
h.ChallengeExpiresIn,
memo,
)
if err != nil {
h.Logger.Ctx(ctx).WithStack(err).Error(err)
serverError.Render(w)
badRequest.Render(w)
return
}

Expand Down
25 changes: 22 additions & 3 deletions exp/services/webauth/internal/serve/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package serve

import (
"net/http"
"strconv"
"strings"
"time"

Expand All @@ -11,6 +12,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"
)
Expand Down Expand Up @@ -52,9 +54,10 @@ func (h tokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
clientAccountID string
signingAddress *keypair.FromAddress
homeDomain string
memo *txnbuild.MemoID
)
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
Expand All @@ -80,10 +83,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.")

muxedAccount, err := xdr.AddressToMuxedAccount(clientAccountID)
if err == nil {
clientAccountID = muxedAccount.ToAccountId().Address()
}

var clientAccountExists bool
clientAccount, err := h.HorizonClient.AccountDetail(horizonclient.AccountRequest{AccountID: clientAccountID})
switch {
Expand Down Expand Up @@ -140,10 +149,20 @@ func (h tokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}

var sub string
if muxedAccount == (xdr.MuxedAccount{}) {
JakeUrban marked this conversation as resolved.
Show resolved Hide resolved
sub = clientAccountID
if memo != nil {
JakeUrban marked this conversation as resolved.
Show resolved Hide resolved
sub += ":" + strconv.FormatUint(uint64(*memo), 10)
}
} else {
sub = muxedAccount.Address()
}

issuedAt := time.Unix(tx.Timebounds().MinTime, 0)
claims := jwt.Claims{
Issuer: h.JWTIssuer,
Subject: clientAccountID,
Subject: sub,
IssuedAt: jwt.NewNumericDate(issuedAt),
Expiry: jwt.NewNumericDate(issuedAt.Add(h.JWTExpiresIn)),
}
Expand Down
12 changes: 12 additions & 0 deletions exp/services/webauth/internal/serve/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func TestToken_formInputSuccess(t *testing.T) {
homeDomain,
network.TestNetworkPassphrase,
time.Minute,
nil,
)
require.NoError(t, err)

Expand Down Expand Up @@ -146,6 +147,7 @@ func TestToken_formInputSuccess_jwtHeaderAndPayloadAreDeterministic(t *testing.T
homeDomain,
network.TestNetworkPassphrase,
time.Minute,
nil,
)
require.NoError(t, err)

Expand Down Expand Up @@ -255,6 +257,7 @@ func TestToken_jsonInputSuccess(t *testing.T) {
homeDomain,
network.TestNetworkPassphrase,
time.Minute,
nil,
)
require.NoError(t, err)

Expand Down Expand Up @@ -412,6 +415,7 @@ func TestToken_jsonInputValidRotatingServerSigners(t *testing.T) {
homeDomain,
network.TestNetworkPassphrase,
time.Minute,
nil,
)
require.NoError(t, err)

Expand Down Expand Up @@ -497,6 +501,7 @@ func TestToken_jsonInputValidMultipleSigners(t *testing.T) {
homeDomain,
network.TestNetworkPassphrase,
time.Minute,
nil,
)
require.NoError(t, err)

Expand Down Expand Up @@ -605,6 +610,7 @@ func TestToken_jsonInputNotEnoughWeight(t *testing.T) {
homeDomain,
network.TestNetworkPassphrase,
time.Minute,
nil,
)
require.NoError(t, err)

Expand Down Expand Up @@ -691,6 +697,7 @@ func TestToken_jsonInputUnrecognizedSigner(t *testing.T) {
homeDomain,
network.TestNetworkPassphrase,
time.Minute,
nil,
)
require.NoError(t, err)

Expand Down Expand Up @@ -777,6 +784,7 @@ func TestToken_jsonInputAccountNotExistSuccess(t *testing.T) {
homeDomain,
network.TestNetworkPassphrase,
time.Minute,
nil,
)
require.NoError(t, err)

Expand Down Expand Up @@ -881,6 +889,7 @@ func TestToken_jsonInputAccountNotExistFail(t *testing.T) {
homeDomain,
network.TestNetworkPassphrase,
time.Minute,
nil,
)
require.NoError(t, err)

Expand Down Expand Up @@ -963,6 +972,7 @@ func TestToken_jsonInputAccountNotExistNotAllowed(t *testing.T) {
homeDomain,
network.TestNetworkPassphrase,
time.Minute,
nil,
)
require.NoError(t, err)

Expand Down Expand Up @@ -1047,6 +1057,7 @@ func TestToken_jsonInputUnrecognizedServerSigner(t *testing.T) {
homeDomain,
network.TestNetworkPassphrase,
time.Minute,
nil,
)
require.NoError(t, err)

Expand Down Expand Up @@ -1248,6 +1259,7 @@ func TestToken_jsonInputInvalidWebAuthDomainFail(t *testing.T) {
homeDomain,
network.TestNetworkPassphrase,
time.Minute,
nil,
)
require.NoError(t, err)

Expand Down
10 changes: 10 additions & 0 deletions txnbuild/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ file. This project adheres to [Semantic Versioning](http://semver.org/).

## Unreleased

### Breaking changes

* Muxed accounts and ID memos can be used in the `BuildChallengeTx()` and `ReadChallengeTx()` SEP-10 utilitiy functions to identify users of shared Stellar accounts. ([#4746](https://github.com/stellar/go/pull/4746))
* `BuildChallengeTx()`:
* Muxed account addresses can be passed as the `clientAccountID`.
* Adds an additional parameter of type `*txnbuild.MemoID`. Memos cannot be specified if the `clientAccoutID` id a muxed account address.
* `ReadChallengeTx()`:
* Muxed account addresses may be returned as the `clientAccountID`.
* Adds an additional return value of type `*txnbuild.MemoID`.


## [10.0.0](https://github.com/stellar/go/releases/tag/horizonclient-v9.0.0) - 2022-04-18

Expand Down
2 changes: 1 addition & 1 deletion txnbuild/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Loading