Skip to content
Permalink
master
Go to file
### What
Add back the home domain verification, but with greater clarity about exactly what the home domain is.

### Why
In v2.1.0 the home domain verification that was added in v2.0.0 was removed because the exact contents of the home domain field could be interpreted in at least two different ways that led to inconsistent implementations. The home domain is still present in the SEP-10 challenge transaction and this change provides clarity to what it should contain and how clients should verify it. This change represents the intended understanding of the home domain field. See v2.0.0 and v2.1.0 for prior context.

### Notes
Servers should use this change to verify that they are placing the correct value for the home domain into the challenge transaction. Once clients implement v3.0.0 they will reject challenge transactions from servers who are providing home domain values that are incorrect.

Co-authored-by: Sergey Nebolsin <nebolsin@gmail.com>
Co-authored-by: Jake Urban <jake@stellar.org>
Co-authored-by: Alex Cordeiro <accordeiro@users.noreply.github.com>
9 contributors

Users who have contributed to this file

@leighmcculloch @JakeUrban @nebolsin @msfeldstein @vitchor @tomquisel @theaeolianmachine @christian-rogobete @accordeiro
290 lines (200 sloc) 19.7 KB

Preamble

SEP: 0010
Title: Stellar Web Authentication
Author: Sergey Nebolsin <@nebolsin>, Tom Quisel <tom.quisel@gmail.com>, Leigh McCulloch <@leighmcculloch>, Jake Urban <jake@stellar.org>
Status: Active
Created: 2018-07-31
Updated: 2020-11-06
Version 3.0.0

Simple Summary

This SEP defines the standard way for clients such as wallets or exchanges to create authenticated web sessions on behalf of a user who holds a Stellar account. A wallet may want to authenticate with any web service which requires a Stellar account ownership verification, for example, to upload KYC information to an anchor in an authenticated way as described in SEP-12.

Abstract

This protocol is a variation of mutual challenge-response, which uses Stellar transactions to encode challenges and responses.

It involves three components:

  • A domain hosting a SEP-1 stellar.toml containing a WEB_AUTH_ENDPOINT and SIGNING_KEY, referred to as the home domain.
  • An endpoint providing the GET and POST operations discussed in this document.
  • A client who will authenticate using a Stellar account.

The discovery flow is as follows:

  1. The client retrieves the stellar.toml from the home domain in accordance with SEP-1 stellar.toml.
  2. The client looks up the WEB_AUTH_ENDPOINT and SIGNING_KEY from the stellar.toml.

The authentication flow is as follows:

  1. The client obtains a unique challenge from the WEB_AUTH_ENDPOINT, which is represented as specially formed Stellar transaction
  2. The client verifies that the transaction has an invalid sequence number 0. This is extremely important to ensure the transaction isn't malicious.
  3. The client verifies that the transaction is signed by the SIGNING_KEY obtained through discovery flow.
  4. The client verifies that the transaction's first operation is a Manage Data operation that has its:
    1. Source account set to the user's account.
    2. Key set to <home domain> auth where the home domain is the home domain from the discovery flow.
    3. Value set to a nonce value.
  5. The client verifies that if the transaction has other operations they are Manage Data operations that all have their source accounts set to the the server's account.
  6. The client signs the transaction using the secret key(s) of signers for the user's Stellar account
  7. The client submits the signed challenge back to the server using token endpoint
  8. The server checks that the user's account exists
  9. If the user's account exists:
  10. The server gets the signers of the user's account
  11. The server verifies the client signatures count is one or more;
  12. The server verifies the client signatures on the transaction are signers of the user's account;
  13. The server verifies the weight provided by the signers meets the required threshold(s), if any
  14. If the signatures check out, the server responds with a JWT that represents the user's session
  15. If the user's account does not exist (optional):
  16. The server verifies the client signature count is one
  17. The server verifies the client signature is correct for the master key of the account
  18. Any future calls to the server can be authenticated by including the JWT as a parameter

The flow achieves several things:

  • Both client and server part can be implemented using well-established Stellar libraries
  • The client can verify that the server holds the secret key to a particular account
  • The server can verify that the client holds the secret key(s) to signers of their account
  • The client is able to prove their identity using a Ledger or other hardware wallet as well as by having direct access to the secret key(s)
  • The server can choose its own timeout for the user's session
  • The server can choose required threshold(s) that the user needs on the account, if any
  • The server can choose to include other application specific claims
  • The server can choose to authenticate accounts that do not yet exist

Authentication Endpoint

A web service indicates that it supports user authentication via this protocol by specifying WEB_AUTH_ENDPOINT in their stellar.toml file. This is how a wallet knows where to find the authentication server. A web server is required to implement the following behavior for the web authentication endpoint:

Cross-Origin Headers

Valid CORS headers are necessary to allow web clients from other sites to use the endpoints. The following HTTP header must be set for all authentication endpoints, including error responses.

Access-Control-Allow-Origin: *

In order for browsers-based wallets to validate the CORS headers, as specified by W3C, the preflight request (OPTIONS request) must be implemented in all the endpoints that support Cross-Origin.

Challenge

This endpoint must respond with a Stellar transaction signed by the server that has an invalid sequence number (0) and thus cannot be executed on the Stellar network. The client can then sign the transaction using standard Stellar libraries and submit it to token endpoint to prove that they control their account. This approach is compatible with hardware wallets such as Ledger. The client must also verify the server's signature to be sure the challenge is signed by the SIGNING_KEY, and the home domain in the challenge is the home domain from the discovery flow.

Request

GET <WEB_AUTH_ENDPOINT>

Request Parameters:

Name Type Description
account G... string The stellar account that the wallet wishes to authenticate with the server
home_domain string (optional) The home domain the client would like to authenticate. Servers that generate tokens for multiple home domains can use this parameter to identify which home domain the client hopes to authenticate. If not provided by the client, the server should assume a default for backwards compatibility with older clients.

Example:

GET https://auth.example.com/?account=GCIBUCGPOHWMMMFPFTDWBSVHQRT4DIBJ7AD6BZJYDITBK2LCVBYW7HUQ

Response

On success the endpoint must return 200 OK HTTP status code and a JSON object with these fields:

  • transaction: an XDR-encoded Stellar transaction with the following:
    • source account set to server's signing account
    • invalid sequence number (set to 0) so the transaction cannot be run on the Stellar network
    • time bounds: {min: now(), max: now() + 900 } (we recommend expiration of 15 minutes to give user time to sign transaction)
    • operations:
      • manage_data(source: client_account, key: '<home domain> auth', value: random_nonce())
        • The value of key is the home domain the challenge is authenticating, followed by auth. It can be at most 64 characters.
        • The value must be 64 bytes long. It contains a 48 byte cryptographic-quality random string encoded using base64 (for a total of 64 bytes after encoding).
      • zero or more manage_data(source: server_account, ...) reserved for future use
    • signature by the service's stellar.toml SIGNING_KEY
  • network_passphrase: (optional but recommended) Stellar network passphrase used by the server. This allows the client to verify that it's using the correct passphrase when signing and is useful for identifying when a client or server have been configured incorrectly.

Example:

{
  "transaction": "AAAAAgAAAADIiRu2BrqqeOcP28PWCkD4D5Rjjsqh71HwvqFX+F4VXAAAAGQAAAAAAAAAAAAAAAEAAAAAXzrUcQAAAABfOtf1AAAAAAAAAAEAAAABAAAAAEEB8rhqNa70RYjaNnF1ARE2CbL50iR9HPXST/fImJN1AAAACgAAADB0aGlzaXNhdGVzdC5zYW5kYm94LmFuY2hvci5hbmNob3Jkb21haW4uY29tIGF1dGgAAAABAAAAQGdGOFlIQm1zaGpEWEY0L0VJUFZucGVlRkxVTDY2V0tKMVBPYXZuUVVBNjBoL09XaC91M2Vvdk54WFJtSTAvQ2UAAAAAAAAAAfheFVwAAABAheKE1HjGnUCNwPbX8mz7CqotShKbA+xM2Hbjl6X0TBpEprVOUVjA6lqMJ1j62vrxn1mF3eJzsLa9s9hRofG3Ag==",
  "network_passphrase": "Public Global Stellar Network ; September 2015"
}

You can examine the example challenge transaction in the XDR Viewer

Every other HTTP status code will be considered an error. For example:

{
   "error": "The provided account has requested too many challenges recently. Try again later."
}

Token

This endpoint accepts a signed challenge transaction, validates it and responds with a session JSON Web Token authenticating the user.

Client submits a challenge transaction (that was previously returned by the challenge endpoint) as a HTTP POST request to WEB_AUTH_ENDPOINT using one of the following formats (both should be equally supported by the server):

  • Content-Type: application/x-www-form-urlencoded, body: transaction=<signed XDR (URL-encoded)>)
  • Content-Type: application/json, body: {"transaction": "<signed XDR>"}

To validate the challenge transaction the following steps are performed by the server. If any of the listed steps fail, then the authentication request must be rejected — that is, treated by the application as an invalid input.

  • decode the received input as a base64-urlencoded XDR representation of Stellar transaction envelope;
  • verify that transaction source account is equal to the server's signing key;
  • verify that transaction has time bounds set, and that current time is between the minimum and maximum bounds;
  • verify that transaction contains a single Manage Data operation that:
    • has a non-null source account
    • has the home domain as the key of the operation
  • verify that transaction envelope has a correct signature by server's signing key;
  • if the operation's source account exists:
    • verify that client signatures count is one or more;
    • verify that client signatures on the transaction are signers of the operation's source account;
    • verify that client signatures are correct;
    • verify that client signatures provide weight that meets the required threshold(s), if any;
  • if the operation's source account does not exist:
    • verify that client signature count is one;
    • verify that client signature is correct for the master key of the account address;
  • verify that transaction containing additional Manage Data operations have their source account as the server signing key;
  • verify that transaction sequenceNumber is equal to zero;
  • use operations's source account to determine the authenticating client and perform any additional service-specific validations.

The verification process confirms that a user holds an account. Depending on your application this may mean complete signing authority, some threshold of control, or being a signer of the account. See Verification for examples.

Upon successful verification, service responds with a session JWT, containing the following claims:

The JWT may contain other claims specific to your application, see RFC7519.

Request

POST <WEB_AUTH_ENDPOINT>

Request Parameters:

Name Type Description
transaction string base64 encoded signed challenge transaction XDR

Example:

POST https://auth.example.com/
Content-Type: application/json

{"transaction": "AAAAAgAAAADIiRu2BrqqeOcP28PWCkD4D5Rjjsqh71HwvqFX+F4VXAAAAGQAAAAAAAAAAAAAAAEAAAAAXzrUcQAAAABfOtf1AAAAAAAAAAEAAAABAAAAAEEB8rhqNa70RYjaNnF1ARE2CbL50iR9HPXST/fImJN1AAAACgAAADB0aGlzaXNhdGVzdC5zYW5kYm94LmFuY2hvci5hbmNob3Jkb21haW4uY29tIGF1dGgAAAABAAAAQGdGOFlIQm1zaGpEWEY0L0VJUFZucGVlRkxVTDY2V0tKMVBPYXZuUVVBNjBoL09XaC91M2Vvdk54WFJtSTAvQ2UAAAAAAAAAAvheFVwAAABAheKE1HjGnUCNwPbX8mz7CqotShKbA+xM2Hbjl6X0TBpEprVOUVjA6lqMJ1j62vrxn1mF3eJzsLa9s9hRofG3AsiYk3UAAABArIrkvqmA0V9lIZcVyCUdja6CiwkPwsV8BfI4CZOyR1Oq7ysvNJWwY0G42dpxN9OP1qz4dum8apG2hqvxVWjkDQ=="}

You can examine the example signed challenge transaction in the XDR Viewer

Response

If the web service successfully validates the submitted challenge transaction, the endpoint should return 200 OK HTTP status code and a JSON object with the following fields:

Name Type Description
token string The JWT that a user can use to authenticate future endpoint calls with the anchor

Example:

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJHQTZVSVhYUEVXWUZJTE5VSVdBQzM3WTRRUEVaTVFWREpIREtWV0ZaSjJLQ1dVQklVNUlYWk5EQSIsImp0aSI6IjE0NGQzNjdiY2IwZTcyY2FiZmRiZGU2MGVhZTBhZDczM2NjNjVkMmE2NTg3MDgzZGFiM2Q2MTZmODg1MTkwMjQiLCJpc3MiOiJodHRwczovL2ZsYXBweS1iaXJkLWRhcHAuZmlyZWJhc2VhcHAuY29tLyIsImlhdCI6MTUzNDI1Nzk5NCwiZXhwIjoxNTM0MzQ0Mzk0fQ.8nbB83Z6vGBgC1X9r3N6oQCFTBzDiITAfCJasRft0z0"
}

Check the example session token on JWT.IO.

Every other HTTP status code will be considered an error. For example:

{
   "error": "The provided transaction is not valid"
}

Verification

The verification process confirms that a user holds an account. Depending on your application this may mean complete signing authority, some threshold of control, or being a signer of the account.

An account's master key may not meet any threshold of control or could have had its weight reduced to zero. Most applications should not assume possession of the master key is possession of an account.

An account's signers may include third-party services providing services to the user. Authenticating users with less than any threshold may allow a third-party to authenticate as a user.

An account's signers may include the server's signing key if the server is a signer for the account. When determining the weight of the client sigantures the server's signature should be explicitly excluded. A server should not assist a client in authentication.

The server should only issue a JWT if the appropriate thresholds are met, but if a server is supporting a variety of applications it may choose to use additional application specific claims to capture the threshold of control the user has proven.

Verifying Authority to Move Funds

A server that needs to verify that a user has authority aligned with the capability to move money out of an account can verify that the user meets the medium threshold. It should do this by checking that the sum of the weights of the challenge transaction signers is equal or greater to the medium threshold.

Example

An anchor implementing SEP-24 will let an authenticated user define the destination of withdrawn funds. This level of control is similar to the capability to choose the destination of payments on the network which require a medium threshold.

Verifying Complete Authority

A server that needs to verify a user has complete authority of an account should verify that the weight of the client signatures meet the high threshold. It should do this by checking that the sum of the weights is equal or greater to the high threshold.

Verifying Being a Signer

A server may choose to issue JWTs for less than all thresholds and based on any other application specific logic. It's important to keep in mind that a Stellar account may have third-parties who are signers. Authenticating users with less than any threshold may allow a third-party to authenticate as a user.

Verifying Accounts that Do Not Exist

A server that needs to support validating accounts that do not exist can require a signature of the master key of the account address for accounts that do not exist.

JWT Expiration

Servers should select an expiration time for the JWT that is appropriate for the assumptions and risk of the interactions a user can perform with it. A user may be in control of an account at the time the JWT is issued but they may lose control of the account through a change in signers. Expiration times that are too long increase the risk that control on the account has changed. Expiration times that are too short increase the number of times authentication must reoccur, and a user using a hardware signing device or who must complete a complex signing process could have a poor user experience.

A convention for signatures

Signatures in Stellar involve both the secret key of the signer and the passphrase of the network. SEP-10 clients and servers must use the following convention when deciding what network passphrase to use for signing and verifying signatures in SEP-10:

  • If the server is for testing purposes or interacts with the Stellar testnet, use the Stellar testnet passphrase.
  • Otherwise, use the Stellar pubnet passphrase.

This convention ensures that SEP-10 clients and servers can use the same passphrase as they're using for interacting with the Stellar network.

The client can examine the network_passphrase (if defined) that the server includes in its response from the challenge endpoint to be sure it's using the correct passphrase and is connecting to the server that it expected.

JWT best practices

When generating and validating JWTs it's important to follow best practices. The IETF in the process of producing a set of best current practices when using JWTs: IETF JWT BCP.

Implementations

You can’t perform that action at this time.