validateChallenge method
void
validateChallenge(])
Validates the challenge transaction received from the web auth server.
Implementation
void validateChallenge(String challengeTransaction, String userAccountId,
String? clientDomainAccountId,
[int? timeBoundsGracePeriod, int? memo]) {
XdrTransactionEnvelope envelopeXdr =
XdrTransactionEnvelope.fromEnvelopeXdrString(challengeTransaction);
if (envelopeXdr.discriminant != XdrEnvelopeType.ENVELOPE_TYPE_TX) {
throw ChallengeValidationError(
"Invalid transaction type received in challenge");
}
final transaction = envelopeXdr.v1!.tx;
if (transaction.seqNum.sequenceNumber.int64 != 0) {
throw ChallengeValidationErrorInvalidSeqNr(
"Invalid transaction, sequence number not 0");
}
if (transaction.memo.discriminant != XdrMemoType.MEMO_NONE) {
if (userAccountId.startsWith("M")) {
throw ChallengeValidationErrorMemoAndMuxedAccount(
"Memo and muxed account (M...) found");
} else if (transaction.memo.discriminant != XdrMemoType.MEMO_ID) {
throw ChallengeValidationErrorInvalidMemoType("invalid memo type");
} else if (memo != null && transaction.memo.id!.uint64 != memo) {
throw ChallengeValidationErrorInvalidMemoValue("invalid memo value");
}
} else if (memo != null) {
throw ChallengeValidationErrorInvalidMemoValue("missing memo");
}
if (transaction.operations.length == 0) {
throw ChallengeValidationError("invalid number of operations (0)");
}
for (int i = 0; i < transaction.operations.length; i++) {
final op = transaction.operations[i];
if (op.sourceAccount == null) {
throw ChallengeValidationErrorInvalidSourceAccount(
"invalid source account (is null) in operation[$i]");
}
final opSourceAccountId =
MuxedAccount.fromXdr(op.sourceAccount!).accountId;
if (i == 0 && opSourceAccountId != userAccountId) {
throw ChallengeValidationErrorInvalidSourceAccount(
"invalid source account in operation[$i]");
}
// all operations must be manage data operations
if (op.body.discriminant != XdrOperationType.MANAGE_DATA ||
op.body.manageDataOp == null) {
throw ChallengeValidationErrorInvalidOperationType(
"invalid type of operation $i");
}
final dataName = op.body.manageDataOp!.dataName.string64;
if (i > 0) {
if (dataName == "client_domain") {
if (opSourceAccountId != clientDomainAccountId) {
throw ChallengeValidationErrorInvalidSourceAccount(
"invalid source account in operation[$i]");
}
} else if (opSourceAccountId != _serverSigningKey) {
throw ChallengeValidationErrorInvalidSourceAccount(
"invalid source account in operation[$i]");
}
}
if (i == 0 && dataName != _serverHomeDomain + " auth") {
throw ChallengeValidationErrorInvalidHomeDomain(
"invalid home domain in operation $i");
}
final dataValue = op.body.manageDataOp!.dataValue!.dataValue;
if (i > 0 && dataName == "web_auth_domain") {
final uri = Uri.parse(_authEndpoint);
if (uri.host != String.fromCharCodes(dataValue)) {
throw ChallengeValidationErrorInvalidWebAuthDomain(
"invalid web auth domain in operation $i");
}
}
}
// check timebounds
final timeBounds = transaction.preconditions.timeBounds;
if (timeBounds != null) {
int grace = 0;
if (timeBoundsGracePeriod != null) {
grace = timeBoundsGracePeriod;
}
final currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
if (currentTime < timeBounds.minTime.uint64 - grace ||
currentTime > timeBounds.maxTime.uint64 + grace) {
throw ChallengeValidationErrorInvalidTimeBounds(
"Invalid transaction, invalid time bounds");
}
}
// the envelope must have one signature and it must be valid: transaction signed by the server
final signatures = envelopeXdr.v1!.signatures;
if (signatures.length != 1) {
throw ChallengeValidationErrorInvalidSignature(
"Invalid transaction envelope, invalid number of signatures");
}
final firstSignature = envelopeXdr.v1!.signatures[0];
// validate signature
final serverKeyPair = KeyPair.fromAccountId(_serverSigningKey);
final transactionHash =
AbstractTransaction.fromEnvelopeXdr(envelopeXdr).hash(_network);
final valid = serverKeyPair.verify(
transactionHash, firstSignature.signature!.signature!);
if (!valid) {
throw ChallengeValidationErrorInvalidSignature(
"Invalid transaction envelope, invalid signature");
}
}