There’s a good, historical reason for this. I was there when it happened. This is how I remember it:
Anvil’s login names were originally case-sensitive. This adds security, by making it harder to brute-force guess a name.
However, most login names were email addresses. For historical reasons, email addresses, as used elsewhere, are case-insensitive. So not all users were careful about how they entered them. To accommodate these users, Anvil made a special case: if the login name is entered in all-lowercase, then it is treated as case-insensitive.
The result: Each user can have it their own way. Those users who want to have case-sensitive log-ins can do so. Those who don’t, don’t have to.
This, too, is a feature. It’s in the forums, but I haven’t seen it in the documentation.
And it’s under your control, right now. If you want to brute-force rewrite all your login names as lowercase, in your Users table, you can do so in a short (under 10 lines) Server Module function. Call it after pasting in a series of addresses, and you’re good to go.
I would prefer that this be 1) documented and 2) controlled via explicit flags. But for now, I would use this short, easy workaround.
@p.colbert has the history right! (My goodness – we’re old enough to have history now )
Because case-insensitive comparisons are expensive, we do exact-match lookups on the Users table. If you enter Stein@example.com in the login form, we search the Users table for the string and its lowercase equivalent – so:
stein@example.com (lower-case)
Stein@example.com (original case, for compatibility with existing mixed-case tables)
All the built-in functions (signup_with_email() etc) lowercase the email before storing it in the table, which makes all logins case-insensitive. If you’re building your own sign-up or enrolment process, I’d recommend you do this too.
It might be nice to flag this in the Users Service editor, or even automatically lower-case when you paste the address in – that would prevent you from being caught out like this! I’ll also make a note to make our documentation more clear about this behaviour.
In the meantime, as @p.colbert says, the fastest route for you is probably a quick script, in a Server Module or the Uplink, that goes:
for user in app_tables.users.search():
if user['email'] != user['email'].lower():
user['email'] = user['email'].lower()