Ok, I figured it out. I looked at the source code here – https://github.com/aspnet/Identity/blob/dev/src/Identity/SignInManager.cs.
NotAllowed is only set here:
protected virtual async Task<SignInResult> PreSignInCheck(TUser user)
{
if (!await CanSignInAsync(user))
{
return SignInResult.NotAllowed;
}
if (await IsLockedOut(user))
{
return await LockedOut(user);
}
return null;
}
So I drilled down into CanSignInAsync…
public virtual async Task<bool> CanSignInAsync(TUser user)
{
if (Options.SignIn.RequireConfirmedEmail && !(await UserManager.IsEmailConfirmedAsync(user)))
{
Logger.LogWarning(0, "User {userId} cannot sign in without a confirmed email.", await UserManager.GetUserIdAsync(user));
return false;
}
if (Options.SignIn.RequireConfirmedPhoneNumber && !(await UserManager.IsPhoneNumberConfirmedAsync(user)))
{
Logger.LogWarning(1, "User {userId} cannot sign in without a confirmed phone number.", await UserManager.GetUserIdAsync(user));
return false;
}
return true;
}
Oh, I know where this is going. Let’s take a look at my Startup.cs Configuration.
services.Configure<IdentityOptions>(options =>
{
...
options.SignIn.RequireConfirmedEmail = true;
...
}
Oh dear, OK.
All I had to do was pop into the database and set my user as EmailConfirmed = true. PEBCAK.
“Not Allowed” makes sense, but there was no error message that came back with it – so it wasn’t the best way to know what’s going on. Luckily .NET Core is easy to dive into the source code with.