WebAuthForContracts

public final class WebAuthForContracts : @unchecked Sendable

Implements SEP-45 - Stellar Web Authentication for Contract Accounts.

This class provides functionality for authenticating Soroban smart contract accounts (C… addresses) through the Stellar Web Authentication protocol. The authentication flow returns a JWT token that can be used for subsequent requests to SEP-compliant services (SEP-6, SEP-12, SEP-24, SEP-31).

SEP-45 extends the SEP-10 authentication protocol to support contract accounts, which have different authentication requirements than traditional Stellar accounts. Contract accounts can implement custom authentication logic in their __check_auth function.

Typical Workflow

  1. Initialize from Domain: Create a WebAuthForContracts instance using the anchor’s stellar.toml
  2. Get JWT Token: Request and obtain a JWT token for authentication
  3. Use Token: Include the JWT token in subsequent SEP service requests

Example Usage

// Step 1: Create WebAuthForContracts from anchor domain
let result = await WebAuthForContracts.from(
    domain: "testanchor.stellar.org",
    network: .testnet
)

switch result {
case .success(let webAuth):
    // Step 2: Get JWT token for contract account
    let contractId = "CABC..."
    let signerKeyPair = try KeyPair(secretSeed: "S...")
    let jwtResult = await webAuth.jwtToken(
        forContractAccount: contractId,
        signers: [signerKeyPair]
    )

    switch jwtResult {
    case .success(let jwtToken):
        // Step 3: Use JWT token for SEP-24, SEP-6, SEP-12, etc.
        print("JWT Token: \(jwtToken)")
    case .failure(let error):
        print("JWT token error: \(error)")
    }
case .failure(let error):
    print("WebAuth initialization error: \(error)")
}

Advanced Features

Multi-Signature Contracts:

// Provide multiple signers for contracts requiring multiple signatures
let signers = [keyPair1, keyPair2]
let result = await webAuth.jwtToken(
    forContractAccount: contractId,
    signers: signers
)

Contracts Without Signature Requirements:

// For contracts whose __check_auth doesn't require signatures
let result = await webAuth.jwtToken(
    forContractAccount: contractId,
    signers: []  // Empty signers array
)

Client Domain Signing:

// For client domain verification (mutual authentication)
let clientDomainKeyPair = try KeyPair(accountId: "G...")
let result = await webAuth.jwtToken(
    forContractAccount: contractId,
    signers: [contractSignerKeyPair],
    clientDomain: "wallet.example.com",
    clientDomainAccountKeyPair: clientDomainKeyPair,
    clientDomainSigningCallback: { entry in
        // Sign on server and return signed entry
        return try await signOnServer(entry)
    }
)

See also:

  • SEP-0045 Specification
  • [StellarToml] for discovering authentication endpoints
  • [WebAuthenticator] for traditional account (G… and M…) authentication

Public Properties

  • The URL of the SEP-45 web authentication endpoint for obtaining JWT tokens.

    Declaration

    Swift

    public let authEndpoint: String
  • The web auth contract ID (C… address) from stellar.toml.

    Declaration

    Swift

    public let webAuthContractId: String
  • The server’s public signing key (G… address) used to validate challenge signatures.

    Declaration

    Swift

    public let serverSigningKey: String
  • The server’s home domain hosting the stellar.toml configuration file.

    Declaration

    Swift

    public let serverHomeDomain: String
  • The Stellar network used for authentication and signature validation.

    Declaration

    Swift

    public let network: Network
  • Whether to use application/x-www-form-urlencoded (true) or application/json (false) for requests.

    Declaration

    Swift

    public var useFormUrlEncoded: Bool
  • Optional Soroban RPC URL for fetching current ledger (defaults based on network).

    Declaration

    Swift

    public var sorobanRpcUrl: String?

Initialization

  • Creates a WebAuthForContracts instance by fetching configuration from a domain’s stellar.toml file.

    This is the recommended way to initialize a WebAuthForContracts. It automatically retrieves the authentication endpoint, web auth contract ID, and server signing key from the anchor’s stellar.toml configuration file.

    Example:

    let result = await WebAuthForContracts.from(
        domain: "testanchor.stellar.org",
        network: .testnet
    )
    

    Declaration

    Swift

    public static func from(domain: String, network: Network, secure: Bool = true) async -> WebAuthForContractsForDomainEnum

    Parameters

    domain

    The anchor’s domain (e.g., “testanchor.stellar.org”)

    network

    The Stellar network to use (.public, .testnet, or .futurenet)

    secure

    Whether to use HTTPS (true) or HTTP (false). Default is true.

    Return Value

    WebAuthForContractsForDomainEnum indicating success with instance or failure with error

  • Initializes a WebAuthForContracts instance with explicit configuration parameters.

    Throws

    WebAuthForContractsError if parameters are invalid

    Declaration

    Swift

    public init(
        authEndpoint: String,
        webAuthContractId: String,
        serverSigningKey: String,
        serverHomeDomain: String,
        network: Network,
        sorobanRpcUrl: String? = nil
    ) throws

    Parameters

    authEndpoint

    Endpoint to be used for the authentication procedure. Usually taken from stellar.toml.

    webAuthContractId

    The web auth contract ID (C… address). Usually taken from stellar.toml.

    serverSigningKey

    The server public key (G… address), taken from stellar.toml.

    serverHomeDomain

    The server home domain of the server where the stellar.toml was loaded from.

    network

    The network used.

    sorobanRpcUrl

    Optional Soroban RPC URL. Defaults based on network if not provided.

Public Methods

  • Obtains a JWT token through the SEP-45 authentication flow.

    This method handles the complete authentication workflow: requesting a challenge from the server, validating it, signing it with the provided keypairs, and submitting it to receive a JWT token. The returned token can be used for authenticating with SEP-6, SEP-12, SEP-24, SEP-31, and other services.

    Example:

    let contractId = "CABC..."
    let signerKeyPair = try KeyPair(secretSeed: "S...")
    let result = await webAuth.jwtToken(
        forContractAccount: contractId,
        signers: [signerKeyPair]
    )
    

    Declaration

    Swift

    public func jwtToken(
        forContractAccount clientAccountId: String,
        signers: [KeyPair],
        homeDomain: String? = nil,
        clientDomain: String? = nil,
        clientDomainAccountKeyPair: KeyPair? = nil,
        clientDomainSigningCallback: ((SorobanAuthorizationEntryXDR) async throws -> SorobanAuthorizationEntryXDR)? = nil,
        signatureExpirationLedger: UInt32? = nil
    ) async -> GetContractJWTTokenResponseEnum

    Parameters

    forContractAccount

    The contract account ID (starting with C) to authenticate

    signers

    Array of KeyPair objects with secret keys for signing the challenge. For contracts that implement __check_auth with signature verification, provide keypairs with sufficient weight. Can be empty for contracts whose __check_auth implementation does not require signatures.

    homeDomain

    The anchor’s domain hosting the stellar.toml file. Optional, defaults to server home domain

    clientDomain

    Domain of the client application for mutual authentication

    clientDomainAccountKeyPair

    KeyPair for client domain signing. If it includes a private key, it will be used directly

    clientDomainSigningCallback

    Function for remote client domain signing. Accepts SorobanAuthorizationEntryXDR, returns signed entry

    signatureExpirationLedger

    Optional expiration ledger for signatures. If nil and signers are provided, automatically set to current ledger + 10

    Return Value

    GetContractJWTTokenResponseEnum with JWT token on success or error details on failure

  • Requests a challenge from the authentication server.

    Declaration

    Swift

    public func getChallenge(
        forContractAccount clientAccountId: String,
        homeDomain: String? = nil,
        clientDomain: String? = nil
    ) async -> GetContractChallengeResponseEnum

    Parameters

    forContractAccount

    The contract account ID (starting with C) to authenticate

    homeDomain

    Optional home domain for the request. Defaults to server home domain if not provided

    clientDomain

    Optional client domain for mutual authentication

    Return Value

    GetContractChallengeResponseEnum with challenge response or error

  • Validates the authorization entries from the challenge response.

    Validation steps:

    1. Each entry has no sub-invocations
    2. contract_address matches WEB_AUTH_CONTRACT_ID
    3. function_name is “web_auth_verify”
    4. Args validation (account, home_domain, web_auth_domain, nonce, etc.)
    5. Server entry exists and has valid signature
    6. Client entry exists

    Throws

    ContractChallengeValidationError on validation failure

    Declaration

    Swift

    public func validateChallenge(
        authEntries: [SorobanAuthorizationEntryXDR],
        clientAccountId: String,
        homeDomain: String? = nil,
        clientDomainAccountId: String? = nil
    ) throws

    Parameters

    authEntries

    Authorization entries to validate

    clientAccountId

    Expected client contract account

    homeDomain

    Expected home domain. Defaults to server home domain if not provided

    clientDomainAccountId

    Optional expected client domain account

  • Signs the authorization entries for the client account.

    Throws

    Error if signing fails

    Declaration

    Swift

    public func signAuthorizationEntries(
        authEntries: [SorobanAuthorizationEntryXDR],
        clientAccountId: String,
        signers: [KeyPair],
        signatureExpirationLedger: UInt32?,
        clientDomainKeyPair: KeyPair?,
        clientDomainAccountId: String?,
        clientDomainSigningCallback: ((SorobanAuthorizationEntryXDR) async throws -> SorobanAuthorizationEntryXDR)?
    ) async throws -> [SorobanAuthorizationEntryXDR]

    Parameters

    authEntries

    Authorization entries to sign

    clientAccountId

    Client contract account

    signers

    Keypairs to sign with

    signatureExpirationLedger

    Optional expiration ledger for signatures

    clientDomainKeyPair

    Optional keypair for client domain signing

    clientDomainAccountId

    Optional client domain account ID (used with callback)

    clientDomainSigningCallback

    Optional callback for remote signing (single entry)

    Return Value

    Signed authorization entries

  • Submits signed authorization entries to obtain a JWT token.

    Declaration

    Swift

    public func sendSignedChallenge(
        signedEntries: [SorobanAuthorizationEntryXDR]
    ) async -> SubmitContractChallengeResponseEnum

    Parameters

    signedEntries

    Signed authorization entries

    Return Value

    SubmitContractChallengeResponseEnum with JWT token or error

  • Decodes authorization entries from base64 XDR.

    Throws

    Error if decoding fails

    Declaration

    Swift

    public func decodeAuthorizationEntries(base64Xdr: String) throws -> [SorobanAuthorizationEntryXDR]

    Parameters

    base64Xdr

    Base64-encoded XDR array of SorobanAuthorizationEntry

    Return Value

    Array of authorization entries