jwtToken method
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:
- Requests a challenge transaction from the server
- Validates the challenge transaction
- Signs the transaction with the provided keypair(s)
- Submits the signed transaction to the server
- 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:
clientAccountIdThe Stellar account ID to authenticate (G... or M... address)signersList 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.memoOptional ID memo if using a muxed account that starts with G. Not allowed for M... addresses as they encode the memo.homeDomainOptional home domain if the auth server serves multiple domainsclientDomainOptional domain of the client application. When provided, proves that the client controls this domain by signing with the domain's key.clientDomainAccountKeyPairOptional keypair for the client domain's signing key (required if clientDomain is provided and no signing delegate is used)clientDomainSigningDelegateOptional async callback to sign the challenge with the client domain key. Use this when the domain key is stored securely elsewhere.
Returns: Future
Throws:
- ChallengeValidationError If the challenge transaction is invalid
- SubmitCompletedChallengeErrorResponseException If server rejects the signed challenge
- NoMemoForMuxedAccountsException If memo is provided for M... address
- MissingClientDomainException If signing delegate is provided without client domain
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;
}