Web Auth
SEP-10 Web Authentication client.
Provides secure authentication for Stellar wallets and applications using a challenge-response protocol with transaction signing. This implements the client side of the SEP-10 specification for authenticating with Stellar services.
SEP-10 Authentication Flow:
Client requests a challenge transaction from the authentication server
Server generates a transaction with specific security requirements and signs it
Client validates the challenge transaction (13 security checks)
Client signs the validated transaction with their keypair(s)
Client submits the signed transaction back to the server
Server verifies the signatures and returns a JWT token
Client uses the JWT token to authenticate subsequent API requests
The JWT token can be used to authenticate requests to SEP-6 (Deposit/Withdrawal), SEP-12 (KYC), SEP-24 (Hosted Deposit/Withdrawal), SEP-31 (Cross-Border Payments), and other Stellar services that require authentication.
Usage Patterns:
High-Level API (Recommended for most use cases):
// Initialize from domain's stellar.toml
val webAuth = WebAuth.fromDomain("example.com", Network.PUBLIC)
// One-line authentication
val authToken = webAuth.jwtToken(
clientAccountId = userKeyPair.getAccountId(),
signers = listOf(userKeyPair)
)
// Use token in API requests
httpClient.get("https://example.com/api/account") {
header("Authorization", "Bearer ${authToken.token}")
}Low-Level API (For custom flows or debugging):
val webAuth = WebAuth.fromDomain("example.com", Network.PUBLIC)
// Step 1: Request challenge
val challenge = webAuth.getChallenge(clientAccountId = accountId)
// Step 2: Validate challenge (critical security step)
webAuth.validateChallenge(
challengeXdr = challenge.transaction,
clientAccountId = accountId
)
// Step 3: Sign challenge
val signedChallenge = webAuth.signTransaction(
challengeXdr = challenge.transaction,
signers = listOf(userKeyPair)
)
// Step 4: Submit and get token
val authToken = webAuth.sendSignedChallenge(signedChallenge)Multi-Signature Accounts:
val webAuth = WebAuth.fromDomain("example.com", Network.PUBLIC)
// Provide all required signers for a multi-sig account
val authToken = webAuth.jwtToken(
clientAccountId = multiSigAccountId,
signers = listOf(signer1KeyPair, signer2KeyPair, signer3KeyPair)
)Account with Memo (Sub-accounts):
// For custodial services with sub-accounts identified by memo
val authToken = webAuth.jwtToken(
clientAccountId = custodialAccountId,
signers = listOf(userKeyPair),
memo = 12345 // User's memo ID
)Muxed Account (Modern Sub-accounts):
// For muxed accounts (M... addresses)
val authToken = webAuth.jwtToken(
clientAccountId = "M...", // Muxed account address
signers = listOf(userKeyPair)
)Client Domain Verification (Local Signing):
// When your application wants to prove domain ownership to the server
val authToken = webAuth.jwtToken(
clientAccountId = userAccountId,
signers = listOf(userKeyPair),
clientDomain = "wallet.mycompany.com",
clientDomainKeyPair = clientDomainSigningKey
)Client Domain Verification (Wallet Backend Signing):
// When your wallet's domain key is managed by your backend server
val signingDelegate = ClientDomainSigningDelegate { transactionXdr ->
// Send to wallet backend for domain signing
val response = httpClient.post("https://backend.wallet.com/sign") {
setBody(SignRequest(transactionXdr))
}
response.body<SignResponse>().signedTransaction
}
val authToken = webAuth.jwtToken(
clientAccountId = userAccountId,
signers = listOf(userKeyPair),
clientDomain = "wallet.mycompany.com",
clientDomainSigningDelegate = signingDelegate
)Security Considerations:
Always validate the challenge before signing (done automatically in jwtToken())
Verify HTTPS is used for all communication with the authentication endpoint
Verify the server's signing key matches the stellar.toml SIGNING_KEY
Never skip validation checks (all 13 checks are required)
Store JWT tokens securely (encrypted storage, not in logs)
Check token expiration before use
Use fromDomain to automatically discover and verify server configuration
Time Bounds and Grace Period:
Challenge transactions have time bounds to prevent replay attacks
The grace period (default 300 seconds / 5 minutes) accounts for:
Network latency between client and server
Clock skew between client and server
Time for user to review and sign the challenge
Challenges are typically valid for 15 minutes
If validation fails due to time bounds, request a fresh challenge
See also:
fromDomain for automatic configuration from stellar.toml
jwtToken for the complete high-level authentication flow
validateChallenge for the 13 security validation checks
Properties
The authentication endpoint URL (from stellar.toml WEB_AUTH_ENDPOINT)
Optional delegate for external client domain signing
Grace period for time bounds validation (default: 300 seconds)
The home domain of the server (used in challenge validation)
The server's public signing key (from stellar.toml SIGNING_KEY)
Functions
Requests a challenge transaction from the authentication server.
Performs complete SEP-10 authentication flow.
Submits a signed challenge transaction to obtain JWT token.
Signs a challenge transaction with provided keypairs.
Validates a challenge transaction according to SEP-10 security requirements.