Two different base64 strings can decode to one byte.
The common read: Two base64 strings that differ character by character are two different values, so comparing the encoded form is comparing the bytes with extra steps.
Base64 is not one-to-one, and the spec has said so since it was written. The last group of a base64 string carries bits that belong to no byte, and RFC 4648 puts the obligation on the encoder rather than on the decoder: conforming encoders MUST set those bits to zero, and decoders only MAY choose to reject them, which is why a decoder in its default mode accepts both spellings and returns one byte.
The browser's rule is not an implementation quirk either; it is written into the platform's own algorithm, which states explicitly that YQ and YR both return a.
Whitespace tolerance is the second part of the same problem, and Node and the browser agree on it: RFC 4648 says implementations MUST reject encoded data containing characters outside the base alphabet unless the specification referring to this document explicitly states otherwise, and referring specifications do exactly that, so ASCII whitespace inside the string is removed before any decoding happens, by Node's Buffer because its documentation says so and by the browser because step ONE of the algorithm atob runs is to remove it.
A string is therefore not a value; it is one of several legal spellings of a value, and which spellings decode to the same bytes was decided by some referring document rather than by base64. For example, a customer sees a duplicate charge on one invoice and files it against the payment provider, the on-call engineer re-files it against the retry logic, and the service's idempotency guard, a Set of encoded payloads, is doing what its tests say it does.
Decode first and compare bytes, and where the encoded form has to be the key, turn on strictness explicitly: lastChunkHandling: "strict" rejects the spelling the default silently accepts, and it is an opt-in, and the loose default accepts the same spellings atob does.

