jwtToken method

Future<String> jwtToken(
  1. String clientAccountId,
  2. List<KeyPair> signers, {
  3. int? memo,
  4. String? homeDomain,
  5. String? clientDomain,
  6. KeyPair? clientDomainAccountKeyPair,
  7. Future<String> clientDomainSigningDelegate(
    1. String transactionXdr
    )?,
})

Performs the complete SEP-0010 authentication flow and returns a JWT token.

This is the primary method for authenticating a user. It handles the entire challenge-response flow automatically:

  1. Requests a challenge transaction from the server
  2. Validates the challenge transaction
  3. Signs the transaction with the provided keypair(s)
  4. Submits the signed transaction to the server
  5. Returns the JWT token from the server

The returned JWT token can be used to authenticate requests to SEP-6, SEP-12, SEP-24, SEP-31, and other protected endpoints.

Parameters:

  • clientAccountId The Stellar account ID to authenticate (G... or M... address)
  • signers List of keypairs (with secret seeds) needed to sign for the account. For single-signature accounts, provide one keypair. For multi-signature accounts, provide all required signers.
  • memo Optional ID memo if using a muxed account that starts with G. Not allowed for M... addresses as they encode the memo.
  • homeDomain Optional home domain if the auth server serves multiple domains
  • clientDomain Optional domain of the client application. When provided, proves that the client controls this domain by signing with the domain's key.
  • clientDomainAccountKeyPair Optional keypair for the client domain's signing key (required if clientDomain is provided and no signing delegate is used)
  • clientDomainSigningDelegate Optional async callback to sign the challenge with the client domain key. Use this when the domain key is stored securely elsewhere.

Returns: Future

Throws:

Example - Basic authentication:

final webAuth = await WebAuth.fromDomain('testanchor.stellar.org', Network.TESTNET);
final userKeyPair = KeyPair.fromSecretSeed('SCZANGBA5YHTNYVVV4C3U252E2B6P6F5T3U6MM63WBSBZATAQI3EBTQ4');

final jwtToken = await webAuth.jwtToken(
  userKeyPair.accountId,
  [userKeyPair],
);

print('Authenticated! Token: $jwtToken');
// Use token in subsequent API calls

Example - Multi-signature account:

// Account requires 2 of 3 signatures
final signer1 = KeyPair.fromSecretSeed('S...');
final signer2 = KeyPair.fromSecretSeed('S...');

final jwtToken = await webAuth.jwtToken(
  'GACCOUNT...',
  [signer1, signer2], // Provide sufficient signers
);

Example - With client domain (proving your app's identity):

final webAuth = await WebAuth.fromDomain('anchor.example.com', Network.PUBLIC);
final userKeyPair = KeyPair.fromSecretSeed('S...');
final clientDomainKeyPair = KeyPair.fromSecretSeed('S...'); // Your app's signing key

final jwtToken = await webAuth.jwtToken(
  userKeyPair.accountId,
  [userKeyPair],
  clientDomain: 'wallet.myapp.com',
  clientDomainAccountKeyPair: clientDomainKeyPair,
);

Example - With external signing (for secure key storage):

final jwtToken = await webAuth.jwtToken(
  userKeyPair.accountId,
  [userKeyPair],
  clientDomain: 'wallet.myapp.com',
  clientDomainSigningDelegate: (transactionXdr) async {
    // Sign transaction using external service/hardware wallet
    return await externalSigningService.sign(transactionXdr);
  },
);

Implementation

Future<String> jwtToken(
  String clientAccountId,
  List<KeyPair> signers, {
  int? memo,
  String? homeDomain,
  String? clientDomain,
  KeyPair? clientDomainAccountKeyPair,
  Future<String> Function(String transactionXdr)? clientDomainSigningDelegate,
}) async {
  if (clientDomain != null &&
      clientDomainAccountKeyPair == null &&
      clientDomainSigningDelegate == null) {
    throw MissingClientDomainSigningKeyException();
  }

  // get the challenge transaction from the web auth server
  String transaction = await getChallenge(
    clientAccountId,
    memo,
    homeDomain,
    clientDomain,
  );

  String? clientDomainAccountId;
  if (clientDomainAccountKeyPair != null) {
    clientDomainAccountId = clientDomainAccountKeyPair.accountId;
  } else if (clientDomainSigningDelegate != null) {
    if (clientDomain == null) {
      throw MissingClientDomainException();
    }
    final StellarToml clientToml = await StellarToml.fromDomain(
      clientDomain,
      httpClient: this.httpClient,
      httpRequestHeaders: this.httpRequestHeaders,
    );
    if (clientToml.generalInformation.signingKey == null) {
      throw NoClientDomainSigningKeyFoundException(clientDomain);
    }
    clientDomainAccountId = clientToml.generalInformation.signingKey;
  }
  // validate the transaction received from the web auth server.
  validateChallenge(
    transaction,
    clientAccountId,
    clientDomainAccountId,
    gracePeriod,
    memo,
  ); // throws if not valid

  if (clientDomainAccountKeyPair != null) {
    transaction = signTransaction(transaction, [clientDomainAccountKeyPair]);
  } else if (clientDomainSigningDelegate != null) {
    transaction = await clientDomainSigningDelegate(transaction);
  }

  List<KeyPair> mSigners = List.from(signers, growable: true);
  // sign the transaction received from the web auth server using the provided user/client keypair by parameter.
  final signedTransaction = signTransaction(transaction, mSigners);

  // request the jwt token by sending back the signed challenge transaction to the web auth server.
  final String jwtToken = await sendSignedChallengeTransaction(
    signedTransaction,
  );

  return jwtToken;
}