Skip to content

Commit

Permalink
txnbuild: impl SEP-10 v3.1 add web_auth_domain support (#3359)
Browse files Browse the repository at this point in the history
### What

Implement SEP-10 v3.1, adding support for building and verifying SEP-10 challenge transactions with the web_auth_domain manage data operation.

### Why

To maintain compatibility with SEP-10.

Close #3334.

### Compatibility

This SEP-10 change is completely backwards compatible with any server or client configuration using SEP-10 v2.1 or greater. If a server is on v3.1 and a client is on v2.1, they are compatible. If a server is on v2.1 and a client is on v3.1, they are compatible.

The SDK changes are however not backwards compatible because of changes to exported function signatures in the `txnbuild` package. Because of this, this change needs releasing in a major release of txnbuild.

The webauth changes are not backwards compatible because the server requires the `--domain` parameter to be specified for successful startup. This does not affect how webauth gets released because it is experimental. This will require updates to any deployments using webauth.
  • Loading branch information
leighmcculloch authored Feb 19, 2021
1 parent 4bc04fe commit c62baa7
Show file tree
Hide file tree
Showing 12 changed files with 1,021 additions and 147 deletions.
3 changes: 2 additions & 1 deletion exp/services/webauth/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# webauth

This is a [SEP-10] Web Authentication implementation based on SEP-10 v2.0.0
This is a [SEP-10] Web Authentication implementation based on SEP-10 v3.1.0
that requires a user to prove they possess a signing key(s) that meets the high
threshold for an account, i.e. they have the ability to perform any high
threshold operation on the given account. If an account does not exist it may
Expand Down Expand Up @@ -44,6 +44,7 @@ Flags:
--allow-accounts-that-do-not-exist Allow accounts that do not exist (ALLOW_ACCOUNTS_THAT_DO_NOT_EXIST)
--auth-home-domain string Home domain(s) of the service(s) requiring SEP-10 authentication comma separated (first domain is the default domain) (AUTH_HOME_DOMAIN)
--challenge-expires-in int The time period in seconds after which the challenge transaction expires (CHALLENGE_EXPIRES_IN) (default 300)
--domain string Domain that this service is hosted at (DOMAIN)
--horizon-url string Horizon URL used for looking up account details (HORIZON_URL) (default "https://horizon-testnet.stellar.org/")
--jwk string JSON Web Key (JWK) used for signing JWTs (if the key is an asymmetric key that has separate public and private key, the JWK must contain the private key) (JWK)
--jwt-expires-in int The time period in seconds after which the JWT expires (JWT_EXPIRES_IN) (default 300)
Expand Down
7 changes: 7 additions & 0 deletions exp/services/webauth/cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ func (c *ServeCommand) Command() *cobra.Command {
ConfigKey: &opts.SigningKeys,
Required: true,
},
{
Name: "domain",
Usage: "Domain that this this service is hosted at",
OptType: types.String,
ConfigKey: &opts.Domain,
Required: true,
},
{
Name: "auth-home-domain",
Usage: "Home domain(s) of the service(s) requiring SEP-10 authentication comma separated (first domain is the default domain)",
Expand Down
2 changes: 2 additions & 0 deletions exp/services/webauth/internal/serve/challenge.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type challengeHandler struct {
NetworkPassphrase string
SigningKey *keypair.Full
ChallengeExpiresIn time.Duration
Domain string
HomeDomains []string
}

Expand Down Expand Up @@ -59,6 +60,7 @@ func (h challengeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
tx, err := txnbuild.BuildChallengeTx(
h.SigningKey.Seed(),
account,
h.Domain,
homeDomain,
h.NetworkPassphrase,
h.ChallengeExpiresIn,
Expand Down
24 changes: 18 additions & 6 deletions exp/services/webauth/internal/serve/challenge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func TestChallenge(t *testing.T) {
NetworkPassphrase: network.TestNetworkPassphrase,
SigningKey: serverKey,
ChallengeExpiresIn: time.Minute,
Domain: "webauthdomain",
HomeDomains: []string{"testdomain"},
}

Expand Down Expand Up @@ -53,11 +54,16 @@ func TestChallenge(t *testing.T) {
assert.Equal(t, serverKey.Address(), sourceAccount.Address())
assert.Equal(t, tx.SeqNum(), int64(0))
assert.Equal(t, time.Unix(int64(tx.TimeBounds().MaxTime), 0).Sub(time.Unix(int64(tx.TimeBounds().MinTime), 0)), time.Minute)
assert.Len(t, tx.Operations(), 1)
opSourceAccount := tx.Operations()[0].SourceAccount.ToAccountId()
assert.Equal(t, account.Address(), opSourceAccount.Address())
assert.Len(t, tx.Operations(), 2)
op0SourceAccount := tx.Operations()[0].SourceAccount.ToAccountId()
assert.Equal(t, account.Address(), op0SourceAccount.Address())
assert.Equal(t, xdr.OperationTypeManageData, tx.Operations()[0].Body.Type)
assert.Regexp(t, "^testdomain auth", tx.Operations()[0].Body.ManageDataOp.DataName)
op1SourceAccount := tx.Operations()[1].SourceAccount.ToAccountId()
assert.Equal(t, sourceAccount.Address(), op1SourceAccount.Address())
assert.Equal(t, xdr.OperationTypeManageData, tx.Operations()[1].Body.Type)
assert.Equal(t, "web_auth_domain", string(tx.Operations()[1].Body.ManageDataOp.DataName))
assert.Equal(t, "webauthdomain", string(*tx.Operations()[1].Body.ManageDataOp.DataValue))

hash, err := network.HashTransactionInEnvelope(tx, res.NetworkPassphrase)
require.NoError(t, err)
Expand All @@ -76,6 +82,7 @@ func TestChallenge_anotherHomeDomain(t *testing.T) {
NetworkPassphrase: network.TestNetworkPassphrase,
SigningKey: serverKey,
ChallengeExpiresIn: time.Minute,
Domain: "webauthdomain",
HomeDomains: []string{"testdomain", anotherDomain},
}

Expand Down Expand Up @@ -103,11 +110,16 @@ func TestChallenge_anotherHomeDomain(t *testing.T) {
assert.Equal(t, serverKey.Address(), sourceAccount.Address())
assert.Equal(t, tx.SeqNum(), int64(0))
assert.Equal(t, time.Unix(int64(tx.TimeBounds().MaxTime), 0).Sub(time.Unix(int64(tx.TimeBounds().MinTime), 0)), time.Minute)
assert.Len(t, tx.Operations(), 1)
opSourceAccount := tx.Operations()[0].SourceAccount.ToAccountId()
assert.Equal(t, account.Address(), opSourceAccount.Address())
assert.Len(t, tx.Operations(), 2)
op0SourceAccount := tx.Operations()[0].SourceAccount.ToAccountId()
assert.Equal(t, account.Address(), op0SourceAccount.Address())
assert.Equal(t, xdr.OperationTypeManageData, tx.Operations()[0].Body.Type)
assert.Regexp(t, "^anotherdomain auth", tx.Operations()[0].Body.ManageDataOp.DataName)
op1SourceAccount := tx.Operations()[1].SourceAccount.ToAccountId()
assert.Equal(t, sourceAccount.Address(), op1SourceAccount.Address())
assert.Equal(t, xdr.OperationTypeManageData, tx.Operations()[1].Body.Type)
assert.Equal(t, "web_auth_domain", string(tx.Operations()[1].Body.ManageDataOp.DataName))
assert.Equal(t, "webauthdomain", string(*tx.Operations()[1].Body.ManageDataOp.DataValue))

hash, err := network.HashTransactionInEnvelope(tx, res.NetworkPassphrase)
require.NoError(t, err)
Expand Down
3 changes: 3 additions & 0 deletions exp/services/webauth/internal/serve/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Options struct {
Port int
NetworkPassphrase string
SigningKeys string
Domain string
AuthHomeDomains string
ChallengeExpiresIn time.Duration
JWK string
Expand Down Expand Up @@ -100,6 +101,7 @@ func handler(opts Options) (http.Handler, error) {
NetworkPassphrase: opts.NetworkPassphrase,
SigningKey: signingKeys[0],
ChallengeExpiresIn: opts.ChallengeExpiresIn,
Domain: opts.Domain,
HomeDomains: trimmedHomeDomains,
}.ServeHTTP)
mux.Post("/", tokenHandler{
Expand All @@ -111,6 +113,7 @@ func handler(opts Options) (http.Handler, error) {
JWTIssuer: opts.JWTIssuer,
JWTExpiresIn: opts.JWTExpiresIn,
AllowAccountsThatDoNotExist: opts.AllowAccountsThatDoNotExist,
Domain: opts.Domain,
HomeDomains: trimmedHomeDomains,
}.ServeHTTP)

Expand Down
7 changes: 4 additions & 3 deletions exp/services/webauth/internal/serve/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type tokenHandler struct {
JWTIssuer string
JWTExpiresIn time.Duration
AllowAccountsThatDoNotExist bool
Domain string
HomeDomains []string
}

Expand Down Expand Up @@ -53,7 +54,7 @@ func (h tokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
homeDomain string
)
for _, s := range h.SigningAddresses {
tx, clientAccountID, homeDomain, err = txnbuild.ReadChallengeTx(req.Transaction, s.Address(), h.NetworkPassphrase, h.HomeDomains)
tx, clientAccountID, homeDomain, err = txnbuild.ReadChallengeTx(req.Transaction, s.Address(), h.NetworkPassphrase, h.Domain, h.HomeDomains)
if err == nil {
signingAddress = s
break
Expand Down Expand Up @@ -102,7 +103,7 @@ func (h tokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if clientAccountExists {
requiredThreshold := txnbuild.Threshold(clientAccount.Thresholds.HighThreshold)
clientSignerSummary := clientAccount.SignerSummary()
signersVerified, err = txnbuild.VerifyChallengeTxThreshold(req.Transaction, signingAddress.Address(), h.NetworkPassphrase, h.HomeDomains, requiredThreshold, clientSignerSummary)
signersVerified, err = txnbuild.VerifyChallengeTxThreshold(req.Transaction, signingAddress.Address(), h.NetworkPassphrase, h.Domain, h.HomeDomains, requiredThreshold, clientSignerSummary)
if err != nil {
l.
WithField("signersCount", len(clientSignerSummary)).
Expand All @@ -118,7 +119,7 @@ func (h tokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
unauthorized.Render(w)
return
}
signersVerified, err = txnbuild.VerifyChallengeTxSigners(req.Transaction, signingAddress.Address(), h.NetworkPassphrase, h.HomeDomains, clientAccountID)
signersVerified, err = txnbuild.VerifyChallengeTxSigners(req.Transaction, signingAddress.Address(), h.NetworkPassphrase, h.Domain, h.HomeDomains, clientAccountID)
if err != nil {
l.Infof("Failed to verify with account master key as signer.")
unauthorized.Render(w)
Expand Down
Loading

0 comments on commit c62baa7

Please sign in to comment.