dvVerifySiwe function

DVWeb3Verification dvVerifySiwe({
  1. required String message,
  2. required Uint8List signature,
  3. required String expectedDomain,
  4. required String expectedNonce,
  5. DateTime? now,
})

Verifies a Sign-In with Ethereum message and its signature.

A signature never expires, so the checks that make this a login rather than a permanent credential are the nonce, the domain and the time window -- not the cryptography.

Implementation

DVWeb3Verification dvVerifySiwe({
  required String message,
  required Uint8List signature,
  required String expectedDomain,
  required String expectedNonce,
  DateTime? now,
}) {
  final DVSiweMessage? parsed = DVSiweMessage.parse(message);
  if (parsed == null) {
    return const DVWeb3Verification.failed(
      'The message was not a well-formed EIP-4361 request.',
    );
  }

  // Bound to the site that issued it, or a signature collected by any dapp the
  // user visits is a login here.
  if (parsed.domain != expectedDomain) {
    return DVWeb3Verification.failed(
      'The message was issued for "${parsed.domain}", not $expectedDomain.',
    );
  }

  // Bound to one attempt, or the signature is a bearer token forever.
  if (parsed.nonce != expectedNonce) {
    return const DVWeb3Verification.failed(
      'The nonce did not match the one issued.',
    );
  }

  final DateTime at = now ?? DateTime.now().toUtc();
  if (parsed.expirationTime != null && !at.isBefore(parsed.expirationTime!)) {
    return const DVWeb3Verification.failed('The message has expired.');
  }
  if (parsed.notBefore != null && at.isBefore(parsed.notBefore!)) {
    return const DVWeb3Verification.failed('The message is not yet valid.');
  }

  final String? recovered = dvRecoverSigner(message, signature);
  if (recovered == null) {
    return const DVWeb3Verification.failed(
      'The signature did not recover an address.',
    );
  }

  // Compared case-insensitively: the message may carry a lower-case address
  // while recovery produces the checksummed form, and rejecting on case would
  // refuse a perfectly good signature.
  if (recovered.toLowerCase() != parsed.address.toLowerCase()) {
    return DVWeb3Verification.failed(
      'The signature was produced by $recovered, not ${parsed.address}.',
    );
  }

  return DVWeb3Verification.passed(recovered);
}