This week I got my first PR thanks to Flyznex, he helped fix issue #234 Login has a very slow response and along implemented a better way to verify if a string is a valid email.
The issue came up when I found that sometime when I login it took a long time. I'd put my email/username and password in and click on the login button and nothing would happen, and after a while like a few seconds then it let me in.
PasswordSignInAsync
As Flyznex pointed in the PR, the SignInManager<TUser>
class in Asp.net Core Identity has two PasswordSignInAsync methods, one that takes in a string for userName while the other takes in a user object. If you call the one that takes in the userName, the method will call UserManager.FindByNameAsync(userName);
to look up the user first by its userName, hence you make an extra call to the database.
In my Login method I already looked the user up with its userName by calling my UserService method FindByEmailOrUsernameAsync(loginUser.UserName);
so I could just pass the user object to PasswordSignInAsync. My mistake was that I passed in user.UserName instead thus incurred the extra db call.
Verify if a string is valid email
On my login I allow user to use either either user name or email to login. When a user is logging in I first check if what's inputted is a valid email or not. If the string is not a valid email then the user is passing in his user name.
So checking if a string is email sounds easy but in reality it is very complex! If you don't believe me just take a look at Phil Haack's post I Knew How To Validate An Email Address Until I Read The RFC. Luckily there is a solution provided to us in the .NET documentation How to: Verify that Strings Are in Valid Email Format. And when you take a look at that implementation it's like 60 lines of C# code just to do this verification, for good reasons.
Before this PR I was using System.Net.Mail.MailAddress
class constructor to verify the validity of an email
// bad code, do not use! bool isEmail; try { new MailAddress(emailOrUsername); isEmail = true; } catch (FormatException) { isEmail = false; }
I originally thought good, less than 10 lines of code to get the same thing done. But this implementation is actually not reliable. Try pass in this string a@a
as input which is not a valid email, it actually returns true!
Another thing to notice is that verifying email feels like a utility feature, as the name of the class in the .NET doc also suggests RegexUtilities. For that normally we want to use a static method, but this implementation depends on a local field bool invalid = false;
that is shared between the two methods inside the class. But Flyznex was able to fix that by removing this local variable, as a result our implementation now is really simple to use, you can just call Util.IsValidEmail("email.to.verify@test.com")
.