The answer is in the source code, here's a part of the implementation in the UserManager at time of writing :
public virtual async Task<IdentityResult> CreateAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
await UpdateSecurityStampInternal(user, cancellationToken);
var result = await ValidateUserInternal(user, cancellationToken);
if (!result.Succeeded)
{
return result;
}
if (Options.Lockout.EnabledByDefault && SupportsUserLockout)
{
await GetUserLockoutStore().SetLockoutEnabledAsync(user, true, cancellationToken);
}
await UpdateNormalizedUserNameAsync(user, cancellationToken);
await Store.CreateAsync(user, cancellationToken);
return IdentityResult.Success;
}
So basically they always return true. This means that in the current version, putting my creation checks in the UserStore goes against the intended usage of the framework.
However I have noticed that this will be changed in the next release. The IUserStore interface will become :
Task<IdentityResult> CreateAsync(TUser user, CancellationToken cancellationToken);
And the UserManager implementation :
public virtual async Task<IdentityResult> CreateAsync(TUser user)
{
ThrowIfDisposed();
await UpdateSecurityStampInternal(user);
var result = await ValidateUserInternal(user);
if (!result.Succeeded)
{
return result;
}
if (Options.Lockout.AllowedForNewUsers && SupportsUserLockout)
{
await GetUserLockoutStore().SetLockoutEnabledAsync(user, true, CancellationToken);
}
await UpdateNormalizedUserNameAsync(user);
await UpdateNormalizedEmailAsync(user);
return await Store.CreateAsync(user, CancellationToken);
}
So putting the creation logic in the UserStore will be possible at that time. This will be a way better design in my opinion as the client shouldn't have to handle the integrity concerns.