SEP: 0045
Title: Stellar Web Authentication for Contract Accounts
Author: Philip Liu <@philipliu>, Marcelo Salloum <@marcelosalloum>, Leigh McCulloch <@leighmcculloch>
Track: Standard
Status: Draft
Created: 2024-10-08
Updated: 2024-10-08
Version: 0.1.0
Discussion: https://github.com/stellar/stellar-protocol/discussions/1620
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 contract account. A wallet may want to authenticate with any web service which requires a contract account ownership verification, for example, to upload KYC information to an anchor in an authenticated way as described in SEP-12.
This SEP is based on SEP-10, but does not replace it. This SEP only supports C
(contract) accounts.
SEP-10 only support G
and M
accounts. Services wishing to support all accounts should implement both SEPs.
- Address: An identifier representing a possible account on the Stellar network. Addresses begin with specific prefixes like G for public keys (accounts controlled by secret keys), M for muxed accounts and C for contract accounts.
- Account: A stateful entity on the Stellar network that can hold balances. This can refer to a Stellar account
(
G...
address, or theG...
base address of anM...
address) or Soroban contract that abstracts an account (C...
addresses).
This protocol is a variation of mutual challenge-response, which uses Stellar authorization entries to encode challenges and responses.
It involves the following components:
- A Home Domain: a domain hosting a SEP-1 stellar.toml containing a
WEB_AUTH_FOR_CONTRACTS_ENDPOINT
(URL),WEB_AUTH_CONTRACT_ID
, andSIGNING_KEY
(G...
).- The
SIGNING_KEY
from this domain is referred to as the Home Domain Address in this document.
- The
- A Server: a server providing the
WEB_AUTH_FOR_CONTRACTS_ENDPOINT
that implements the GET and POST operations discussed in this document. The server's domain may be the Home Domain, a sub-domain of the Home Domain, or a different domain. - A Client Account: the account being authenticated.
- A Stellar contract account (
C...
).
- A Stellar contract account (
- A Client: the software used by the holder of the Client Account being authenticated by the Server.
- A Client Domain (optional): a domain hosting a SEP-1 stellar.toml containing a
SIGNING_KEY
used for Verifying the Client Domain- The
SIGNING_KEY
from this domain is referred to as the Client Domain Address in this document.
- The
- A Web Auth Contract: a contract that implements the
web_auth_verify
function as described in this document. The contract must be deployed at theWEB_AUTH_CONTRACT_ID
address specified in the Server'sstellar.toml
.
The discovery flow is as follows:
- The Client retrieves the
stellar.toml
from the Home Domain in accordance with SEP-1 stellar.toml. - The Client looks up the
WEB_AUTH_FOR_CONTRACTS_ENDPOINT
,WEB_AUTH_CONTRACT_ID
andSIGNING_KEY
(i.e. Home Domain Address) from thestellar.toml
.
The authentication flow is as follows:
- The Client requests a unique
challenge
from the Server which includes a list of authorization entries XDR-encoded strings - If the request contains a
client_domain
parameter, the Server may fetch the Client Domain Address and generate the challenge with an additional authorization entry as described in the Response section. - The Server responds with the challenge
- The Client verifies that each authorization entry does not contain any sub-invocations
- The Client verifies that the
contract_address
in each authorization entry matches theWEB_AUTH_CONTRACT_ID
from the Server'sstellar.toml
- The Client verifies that the
function_name
in each authorization entry isweb_auth_verify
- The Client verifies that the
args
map in each authorization entry match the expected values and is the same across all authorization entries:- The
account
value matches the Client Account address - The
home_domain
value matches the Home Domain - The
home_domain_address
value matches the Home Domain Address - The
web_auth_domain
value matches the Server's domain - If the Client included a
client_domain
in the request:- The
client_domain
value matches the Client's domain - The
client_domain_address
value matches the Client Domain Address
- The
- The
- The Client verifies that there is an authorization entry where
credentials.address.address
is the Home Domain Address and contains a valid signature from the Home Domain Address - The Client signs the authorization entry where
credentials.address.address
is the Client Account using the secret key(s) of the signer(s) for the Client Account- Note: Client signatures may not be required if the contract's
__check_auth
implementation does not require them
- Note: Client signatures may not be required if the contract's
- The Client obtains a signature from the Client Domain Address for the authorization entry where
credentials.address.address
is the Client Domain Address if the Client included a client domain in the request - The Client simulates the transaction with the signed authorization entries and verifies the following to ensure
the transaction does not have any unintended side effects:
- The transaction's ledger footprint
read_write
set contains onlycontract_data
entries where:- The
contract
is the Client Account address, and thekey
isledger_key_nonce
. - The
contract
is the Home Domain Address, and thekey
isledger_key_nonce
. - (Optional) if an authorization entry for the Client Domain Address was present in the challenge, the
contract
is the Client Domain Address, and thekey
isledger_key_nonce
.
- The
- The transaction's ledger footprint
- The Client submits the signed authorization entries back to the Server using
token
endpoint - The Server extracts the arguments from the authorization entries returned by the client
- The Server verifies that the
contract_address
in each authorization entry matches theWEB_AUTH_CONTRACT_ID
from the Server'sstellar.toml
- The Server verifies that the
function_name
in each authorization entry isweb_auth_verify
- The Server verifies that the
args
map in each authorization entry match the expected values and are the same across all authorization entries:- The
account
value matches the Client Account address - The
home_domain
value matches the Home Domain - The
home_domain_address
value matches the Home Domain Address - The
web_auth_domain
value matches the Server's domain - The
client_domain_address
value matches the Client Domain Address if the Client included aclient_domain
in the request, otherwise it is not present
- The
- (Optional) The Server verifies that the
nonce
argument is the same across all authorization entries and is unique - The Server verifies that there is an authorization entry where
credentials.address.address
is the Home Domain Address and contains a valid signature from the Home Domain Address - The Server verifies that there is an authorization entry where
credentials.address.address
is the Client Account address - The Server verifies that there is an authorization entry where
credentials.address.address
is the Client Domain Address if the arguments included aclient_domain_address
- The Server constructs a transaction with a single Invoke Host Function operation where the contract address is
WEB_AUTH_CONTRACT_ID
and the function isweb_auth_verify
using the previously extracted arguments and the authorization entries returned by the client - If the simulation succeeds, the Server responds with a JWT that represents the authenticated session
The flow achieves several things:
- Both Client and Server can be implemented using well-established Stellar libraries
- The Client can verify that the Server holds the secret key to the Home Domain Address
- The Server can verify that the Client is authorized by the contract account to create an authenticated session
- The Server can choose its own timeout for the authenticated session
- The Server can choose to include other application-specific claims
The organization with a Home Domain indicates that it supports authentication via this protocol by specifying
WEB_AUTH_FOR_CONTRACTS_ENDPOINT
in their stellar.toml
file. This is how a wallet knows where to find
the Server. A Server is required to implement the following behavior for the web authentication endpoint:
GET <WEB_AUTH_FOR_CONTRACTS_ENDPOINT>
: request a challenge (step 1)POST <WEB_AUTH_FOR_CONTRACTS_ENDPOINT>
: exchange a signed challenge for session JWT (step 2)
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.
This endpoint must respond with authorization entries to be signed by the Client. The Client signs the entries
using standard Stellar libraries and submit it to token
endpoint to prove that it controls the Client
Account. This approach is compatible with hardware wallets such as Ledger. The Client Application must also verify
the server's signature to be sure the challenge is signed by the Home Domain Address, that the home domain argument
in the function invocation is the Home Domain, and that the web auth domain argument in the function invocation is
the Server domain.
GET <WEB_AUTH_FOR_CONTRACTS_ENDPOINT>
Name | Type | Description |
---|---|---|
account |
C... string |
The Client Account address (C... ) that the Client wishes to authenticate with the Server. |
home_domain |
string | A Home Domain. Servers that generate tokens for multiple Home Domains can use this parameter to identify which home domain the Client hopes to authenticate with. |
client_domain |
string | (optional) a Client Domain. Supplied by Clients that intend to verify their domain in addition to the Client Account. See Verifying the Client Domain. Servers should ignore this parameter if the Server does not support Client Domain verification, or the Server does not support verification for the specific Client Domain included in the request. |
Example:
GET https://auth.example.com/?account=CCIBUCGPOHWMMMFPFTDWBSVHQRT4DIBJ7AD6BZJYDITBK2LCVBYW7HUQ&home_domain=example.com
On success the endpoint must return 200 OK
HTTP status code and a JSON object with the following fields:
authorization_entries
: an XDR-encoded array ofSorobanAuthorizationEntry
s. It contains an entry for the Client Account in addition to a signed entry from the Home Domain Address and optionally the Client Domain Address. The entries are not guaranteed to be in any specific order.
Each entry's root_invocation
function is a contract_fn
with no sub-invocations and the following values:
contract_address
is theWEB_AUTH_CONTRACT_ID
from the Server'sstellar.toml
function_name
isweb_auth_verify
- An
args
map containing the following Symbol to String pairs:account
matches theaccount
from the requesthome_domain
matches thehome_domain
from the requesthome_domain_address
matches the Home Domain Addressweb_auth_domain
matches the Server's domainclient_domain
matches the Client's domain if the Client included aclient_domain
in the request and the server supports Verifying the Client Domainclient_domain_address
matches the Client Domain Address if the Client included aclient_domain
in the request and the server supports Verifying the Client Domain- (Optional)
nonce
is a unique value that is the same across all authorization entries
network_passphrase
: (optional but recommended) Stellar network passphrase used by the Server. This allows a 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:
{
"authorization_entries": "AAAAAQAAAAHDwFN8u4knndhlRcZmGC28sL8G7WJnadYNY88ZwAICiAhjuHNQ4DVQAAAAAAAAAAEAAAAAAAAAAX6lC8GUFNGjTHdg8uQyeJvi1taYAbI3H1Ss91/oUNawAAAAD3dlYl9hdXRoX3ZlcmlmeQAAAAABAAAAEQAAAAEAAAAFAAAADwAAAAdhY2NvdW50AAAAAA4AAAA4Q0RCNEFVMzRYT0VTUEhPWU1WQzRNWlFZRlc2TEJQWUc1VlJHTzJPV0JWUjQ2R09BQUlCSVE0R0QAAAAPAAAAC2hvbWVfZG9tYWluAAAAAA4AAAAObG9jYWxob3N0OjgwODAAAAAAAA8AAAATaG9tZV9kb21haW5fYWRkcmVzcwAAAAAOAAAAOEdESkxCWVlLTUNYTlZWTkFCT0U2Nk5ZWFFHSUE1QUM1RDIyM1oyS0Y2WkVZSzRVQkNBN0ZLTFRHAAAADwAAAAVub25jZQAAAAAAAA4AAAAKMjA2MDIxNDExNQAAAAAADwAAAA93ZWJfYXV0aF9kb21haW4AAAAADgAAAA5sb2NhbGhvc3Q6ODA4MAAAAAAAAAAAAAEAAAAAAAAAANKw4wpgrtrVoAuJ7zcXgZAOgF0etbzpRfZJhXKBED5VHSLrhXQ/QVwAAaLlAAAAEAAAAAEAAAABAAAAEQAAAAEAAAACAAAADwAAAApwdWJsaWNfa2V5AAAAAAANAAAAINKw4wpgrtrVoAuJ7zcXgZAOgF0etbzpRfZJhXKBED5VAAAADwAAAAlzaWduYXR1cmUAAAAAAAANAAAAQDUYC2mj/RW0NcGfY66p+eltjwflRSmwc8ZZ7as2HAUvu4k/bY2ZHIhhS6M7Ufx74XmV9QlxHPv6vXUA/j9uRAwAAAAAAAAAAX6lC8GUFNGjTHdg8uQyeJvi1taYAbI3H1Ss91/oUNawAAAAD3dlYl9hdXRoX3ZlcmlmeQAAAAABAAAAEQAAAAEAAAAFAAAADwAAAAdhY2NvdW50AAAAAA4AAAA4Q0RCNEFVMzRYT0VTUEhPWU1WQzRNWlFZRlc2TEJQWUc1VlJHTzJPV0JWUjQ2R09BQUlCSVE0R0QAAAAPAAAAC2hvbWVfZG9tYWluAAAAAA4AAAAObG9jYWxob3N0OjgwODAAAAAAAA8AAAATaG9tZV9kb21haW5fYWRkcmVzcwAAAAAOAAAAOEdESkxCWVlLTUNYTlZWTkFCT0U2Nk5ZWFFHSUE1QUM1RDIyM1oyS0Y2WkVZSzRVQkNBN0ZLTFRHAAAADwAAAAVub25jZQAAAAAAAA4AAAAKMjA2MDIxNDExNQAAAAAADwAAAA93ZWJfYXV0aF9kb21haW4AAAAADgAAAA5sb2NhbGhvc3Q6ODA4MAAAAAAAAA==",
"network_passphrase": "Test SDF Network ; September 2015"
}
You can examine the example challenge 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."
}
This endpoint accepts a signed challenge transaction, validates it and responds with a session JSON Web Token authenticating the account.
The Client submits a challenge (that was previously returned by the challenge
endpoint) as a HTTP
POST request to WEB_AUTH_FOR_CONTRACTS_ENDPOINT
using one of the following formats (both should be equally supported
by the server):
- Content-Type:
application/x-www-form-urlencoded
, body:authorization_entries=<XDR (URL-encoded)>
) - Content-Type:
application/json
, body:{"authorization_entries": "<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 Server as an invalid input.
- Extract the arguments from the authorization entries returned by the client;
- Verify that the
contract_address
in each authorization entry matches theWEB_AUTH_CONTRACT_ID
from the Server'sstellar.toml
; - Verify that the
function_name
in each authorization entry isweb_auth_verify
; - Verify that the
args
in each authorization entry match the expected values and is the same across all authorization entries:- The
home_domain
value matches the Home Domain; - The
home_domain_address
value matches the Home Domain Address; - The
web_auth_domain
value matches the Server's domain; - The
client_domain
is present ifclient_domain_address
is present; - The
client_domain_address
value matches the Client Domain Address ifclient_domain
is present;
- The
- (Optional) Verify that the
nonce
argument is the same across all authorization entries and is valid; - Verify that there is an authorization entry where
credentials.address.address
is the Home Domain Address and contains a valid signature from the Home Domain Address; - Verify that there is an authorization entry where
credentials.address.address
is the Client Account address - Verify that there is an authorization entry where
credentials.address.address
is the Client Domain Address if the arguments included aclient_domain_address
. - Construct a transaction with a single Invoke Host Function operation where the contract address is
WEB_AUTH_CONTRACT_ID
and the function isweb_auth_verify
using the previously extracted arguments and the authorization entries returned by the client; - Simulate the transaction and verify that it succeeds.
The verification process confirms that the Client controls the Client Account and that the challenge was not tampered with. 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, Server responds with a session JWT, containing the following claims:
iss
(the principal that issued a token, RFC7519, Section 4.1.1) — a Uniform Resource Identifier (URI) for the issuer (https://example.com
orhttps://example.com/G...
)sub
(the principal that is the subject of the JWT, RFC7519, Section 4.1.2) — the Client Account's address (C...
).iat
(the time at which the JWT was issued RFC7519, Section 4.1.6) — current timestamp (1530644093
)exp
(the expiration time on or after which the JWT must not be accepted for processing, RFC7519, Section 4.1.4) — a server can pick its own expiration period for the token (1530730493
)client_domain
- (optional) a nonstandard JWT claim containing the client home domain, included if the challenge transaction contained aclient_domain
(see Verifying the Client Domain)
The JWT may contain other claims specific to your application, see RFC7519.
The Server should not provide more than one JWT for a specific challenge transaction.
POST <WEB_AUTH_ENDPOINT>
Request Parameters:
Name | Type | Description |
---|---|---|
authorization_entries |
string | an XDR-encoded array of signed SorobanAuthorizationEntry s |
Example:
POST https://auth.example.com/
Content-Type: application/json
{
"authorization_entries": "AAAAAQAAAAHDwFN8u4knndhlRcZmGC28sL8G7WJnadYNY88ZwAICiAhjuHNQ4DVQAAGi7gAAABAAAAABAAAAAQAAABEAAAABAAAAAgAAAA8AAAAKcHVibGljX2tleQAAAAAADQAAACCLcZbWB5Tc+LwIlJMazXz6KECPC89cSo589hUfJAOrhwAAAA8AAAAJc2lnbmF0dXJlAAAAAAAADQAAAEDIHVoFpnEUUmqOzTCzsYqu3naPvxUh1BmreIjoBbgXcbsCulvxQQdWKq40JWxNuacxOnoiOy1qyRD2ylTbirgBAAAAAAAAAAF+pQvBlBTRo0x3YPLkMnib4tbWmAGyNx9UrPdf6FDWsAAAAA93ZWJfYXV0aF92ZXJpZnkAAAAAAQAAABEAAAABAAAABQAAAA8AAAAHYWNjb3VudAAAAAAOAAAAOENEQjRBVTM0WE9FU1BIT1lNVkM0TVpRWUZXNkxCUFlHNVZSR08yT1dCVlI0NkdPQUFJQklRNEdEAAAADwAAAAtob21lX2RvbWFpbgAAAAAOAAAADmxvY2FsaG9zdDo4MDgwAAAAAAAPAAAAE2hvbWVfZG9tYWluX2FkZHJlc3MAAAAADgAAADhHREpMQllZS01DWE5WVk5BQk9FNjZOWVhRR0lBNUFDNUQyMjNaMktGNlpFWUs0VUJDQTdGS0xURwAAAA8AAAAFbm9uY2UAAAAAAAAOAAAACjIwNjAyMTQxMTUAAAAAAA8AAAAPd2ViX2F1dGhfZG9tYWluAAAAAA4AAAAObG9jYWxob3N0OjgwODAAAAAAAAAAAAABAAAAAAAAAADSsOMKYK7a1aALie83F4GQDoBdHrW86UX2SYVygRA+VR0i64V0P0FcAAGi5QAAABAAAAABAAAAAQAAABEAAAABAAAAAgAAAA8AAAAKcHVibGljX2tleQAAAAAADQAAACDSsOMKYK7a1aALie83F4GQDoBdHrW86UX2SYVygRA+VQAAAA8AAAAJc2lnbmF0dXJlAAAAAAAADQAAAEA1GAtpo/0VtDXBn2OuqfnpbY8H5UUpsHPGWe2rNhwFL7uJP22NmRyIYUujO1H8e+F5lfUJcRz7+r11AP4/bkQMAAAAAAAAAAF+pQvBlBTRo0x3YPLkMnib4tbWmAGyNx9UrPdf6FDWsAAAAA93ZWJfYXV0aF92ZXJpZnkAAAAAAQAAABEAAAABAAAABQAAAA8AAAAHYWNjb3VudAAAAAAOAAAAOENEQjRBVTM0WE9FU1BIT1lNVkM0TVpRWUZXNkxCUFlHNVZSR08yT1dCVlI0NkdPQUFJQklRNEdEAAAADwAAAAtob21lX2RvbWFpbgAAAAAOAAAADmxvY2FsaG9zdDo4MDgwAAAAAAAPAAAAE2hvbWVfZG9tYWluX2FkZHJlc3MAAAAADgAAADhHREpMQllZS01DWE5WVk5BQk9FNjZOWVhRR0lBNUFDNUQyMjNaMktGNlpFWUs0VUJDQTdGS0xURwAAAA8AAAAFbm9uY2UAAAAAAAAOAAAACjIwNjAyMTQxMTUAAAAAAA8AAAAPd2ViX2F1dGhfZG9tYWluAAAAAA4AAAAObG9jYWxob3N0OjgwODAAAAAAAAA="
}
You can examine the example credentials in the XDR Viewer
If the Server 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 can be used to authenticate future endpoint calls with the anchor |
Example:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJsb2NhbGhvc3Q6ODA4MCIsInN1YiI6IkNDTEhCVVJZTzRCMkpGVTRZQlpVUVpLSlEyWjM3MjNEUFhUV1U2WURQWE40VFozS0hWUTdOT1VMIiwiaWF0IjoxNzM3MTQyMjI0LCJleHAiOjE3MzcxNDI1MjQsImp0aSI6IjI2MWQ3ZjAyMzljOTMxMDVmMmU1NGJjMWZkYzk2OGY5ZTY4MTUyOTQ1MjljMjQwZDgxMzgwY2U3MGQyYzUyMjAiLCJob21lX2RvbWFpbiI6ImxvY2FsaG9zdDo4MDgwIn0.WrtL94Erny9DpfXehA0dfnpmNvDPPuh3_0oxAT16xuw"
}
Check the example session token on JWT.IO.
Every other HTTP status code will be considered an error. For example:
{
"error": "The provided authorization entries are not valid"
}
The Server must deploy a contract at the WEB_AUTH_CONTRACT_ID
address specified in the Server's
stellar.toml
. This contract allows the Server to customize authentication logic, including client domain
verification and nonce handling.
The contract must implement the web_auth_verify
function with the following signature. Within this function, the
contract should call require_auth
on the address to ensure the client has authorized the operation, and call
require_auth
on the home domain address so that the Server can verify that the challenge has not been tampered.
If the Server supports client domain verification, the contract can optionally require a signature from the
client_domain_address
. All other arguments are ignored by the contract.
The server can optionally include a nonce
in the web_auth_verify
function arguments. The nonce
is a random string
generated by the Server and included in the challenge transaction. The nonce
is verified when the Client
submits signed challenge to the Server. The nonce
is used to prevent replay attacks.
Below is an example implementation of the web_auth_verify
function in Rust:
#[contracterror]
pub enum WebAuthError {
MissingArgument = 1,
}
#[contractimpl]
impl WebAuthContract {
pub fn __constructor(env: Env, admin: Address) -> () {
env.storage().instance().set(&DataKey::Admin, &admin);
}
/// Verifies the client is authorized to authenticate with the server
///
/// Arguments:
/// - account: The client account address
/// - home_domain_address: The home domain address
/// - web_auth_domain: The server's domain
/// - client_domain: The client domain (optional)
/// - client_domain_address: The client domain address (optional)
/// - nonce: A random string generated by the server to prevent replay attacks (optional)
pub fn web_auth_verify(env: Env, args: Map<Symbol, String>) -> Result<(), WebAuthError> {
if let Some(address) = args.get(Symbol::new(&env, "account")) {
let addr = Address::from_string(&address);
addr.require_auth();
} else {
return Err(WebAuthError::MissingArgument);
}
if let Some(home_domain_address) = args.get(Symbol::new(&env, "home_domain_address")) {
let home_domain_addr = Address::from_string(&home_domain_address);
home_domain_addr.require_auth();
} else {
return Err(WebAuthError::MissingArgument);
}
// Optional client domain verification
if let Some(client_domain_address) = args.get(Symbol::new(&env, "client_domain_address")) {
let client_domain_addr = Address::from_string(&client_domain_address);
client_domain_addr.require_auth();
}
Ok(())
}
}
To prevent unintended side effects in case the web_auth_verify
invocation operation is executed on the network, the
Client should consider implementing the following recommendations in addition to verifying the function arguments
and sub-invocations.
When signing the authorization entries, the Client should set the signature expiration ledger to the current ledger plus a buffer. This ensures the signature remains valid for an appropriate duration while preventing replay attacks. Setting this to the current ledger plus 1 is a reasonable default.
A web service requiring SEP-45 authentication may want to attribute each HTTP request made to it to a specific Client software. For example, a web service may want to offer reduced fees for the users of a specific Client.
In order to use this optional feature, the organization that provides the Client must host a
SEP-1 stellar.toml file containing a SIGNING_KEY
attribute (i.e. the Client Domain Address). The
SIGNING_KEY
attribute must be a Stellar public key in the form of a G
address. The secret key paired with the
SIGNING_KEY
should be protected as anyone in possession of the secret can verify the Client Domain.
This setup allows the Server to verify that the challenge returned by the Client is also signed by the Client Domain Address, proving that the Client is associated with the Client Domain. Web services requiring SEP-45 authentication can now attribute requests made with the resulting JWT to the Client Domain that signed the challenge.
Servers may chose which Client Domains to verify. If the Client requests verification of its domain but the
Server has no use for verifying that domain, the Server should proceed as if the Client did not provide the
domain in the request for the challenge transaction. If the Server attempts but is unable to fetch the SIGNING_KEY
from the provided Client Domain, the Server should return a 400 Bad Request
HTTP status code.
Servers should select an expiration time for the JWT that is appropriate for the assumptions and risk of the interactions the Client can perform with it. A Client 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.
Signatures in Stellar involve both the secret key of the signer and the passphrase of the network. SEP-45 clients and servers must use the following convention when deciding what network passphrase to use for signing and verifying signatures in SEP-45:
- 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-45 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.
When generating and validating JWTs it's important to follow best practices. The IETF is in the process of producing a set of best current practices when using JWTs: IETF JWT BCP.
- None
0.1.0
: Initial draft