I am using Microsoft.AspNet.Identity for logging into my c# mvc web application. I have implemented the different User Stores including the Lockout User Store. But I can't get it to work properly. In my Custom User Manager I set the max tries, lockout time etc:
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(30);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
If I use the above code and parameters my user never gets locked. If I set the manager.MaxFailedAccessAttemptsBeforeLockout to 2, the my User gets locked after one try. Does anyone have a tutorial on how to correctly implement the "IUserLockoutStore" interface? I've been searching Google all morning and am not getting closer to my goal. Here is my current implementation of the "IUserLockoutStore" interface.
public Task<DateTimeOffset> GetLockoutEndDateAsync(Gebruiker user)
{
var lockOutDate = user.LockOutDate.HasValue ? user.LockOutDate.Value : new DateTimeOffset(DateTime.Now.AddMinutes(-5));
return Task.FromResult(lockOutDate);
}
public Task SetLockoutEndDateAsync(Gebruiker user, DateTimeOffset lockoutEnd)
{
user.LockOutDate = lockoutEnd;
user.IsLocked = true;
return Context.SaveChangesAsync();
}
public Task<int> IncrementAccessFailedCountAsync(Gebruiker user)
{
user.LoginTry++;
return Context.SaveChangesAsync();
}
public Task ResetAccessFailedCountAsync(Gebruiker user)
{
user.LoginTry = 0;
return Context.SaveChangesAsync();
}
public Task<int> GetAccessFailedCountAsync(Gebruiker user) => Task.FromResult(user.LoginTry);
public Task<bool> GetLockoutEnabledAsync(Gebruiker user) => Task.FromResult(true);
public Task SetLockoutEnabledAsync(Gebruiker user, bool enabled)=> Task.FromResult(enabled);
My implementation is very similar to yours except for these two things:
In GetLockoutEndDateAsync I use utc time:
... new DateTimeOffset(DateTime.UtcNow.AddMinutes(-5))
Also (and perhaps more significantly), your return value from IncrementAccessFailedCountAsync should return the count (but you are returning the result of SaveChanges):
public Task<int> IncrementAccessFailedCountAsync(Gebruiker user)
{
user.LoginTry++;
Context.SaveChangesAsync();
return user.LoginTry;
}
It is just a coincidence that SaveChangesAsync also returns an int which is probably why you didn't notice this.
Another note is that you don't have to call Context.SaveChangesAsync() in the first place. This is handled by your implementation of IUserStore. Your IUserLockoutStore (and others like IUserLoginStore and IUserEmailStore etc) don't save to the DB. The infrastructure calls those interfaces to set things, then at the end call IUserStore.UpdateAsync (or .CreateAsync). So it should simply be:
public Task<int> IncrementAccessFailedCountAsync(Gebruiker user)
{
user.LoginTry++;
return Task.FromResult(user.LoginTry);
}
Related
This question asks about how to retrieve all users from AspNetCore Identity, in an async method:
ASP.NET Identity 2 UserManager get all users async
The non-sync method is simple:
[HttpGet]
public ActionResult<IEnumerable<UserDto>> GetAsync()
{
var users = userManager.Users
.ToList()
.Select(user => user.AsDto());
return Ok(users);
}
The obvious solution is this:
[HttpGet]
public async Task<ActionResult<IEnumerable<UserDto>>> GetAsync()
{
var users = await userManager.Users
.ToListAsync()
.Select(user => user.AsDto());
return Ok(users);
}
But this doesn't work because userManager.Users is an IQueryable<>, and that doesn't define a .ToListAsync().
The recommended answer in the question above is:
public async Task<List<User>> GetUsersAsync()
{
using (var context = new YourContext())
{
return await UserManager.Users.ToListAsync();
}
}
But that is tied to Entity Framework, and if you're using AspNetCore.Identity.MongoDbCore, you won't have any dbcontexts, and the above simply doesn't work.
One comment on the answer points out that the .ToListAsync() extension method is defined in System.Data.Entity - but if you're using MongoDb you won't be including that.
How, actually, do you access all users from UserManager<> in an async method?
Basiccally, AspNetCore.Identity.MongoDbCore was just implementation wrapping around core concept of AspNetCore.Identity. And things gone just a bit difference from Sql implementation, cause they doesn't make use of unit of work concept (I know, SaveChange implement would wrap operation in transaction, but for everymethod call, the query execute immediately got this pretty similar to mongo in someway).
Then, If we need any furthur extension of those basic task, why don't just write our own implementation on top of default MongoDbCore implementation like good old days ?
// Starting with UserStore
public class ApplicationUserStore : MongoUserStore<How many generic would depend on use cases>, IApplicationUserStore
{
protected IMongoCollection<TUser> ApplicationUsersCollection;
public ApplicationUserStore(ApplicationDbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
{
ApplicationUsersCollection = Context.GetCollection<TUser>();
}
public async Task<ICollection<ApplicationUser>> GetAllUserAsync() => // We have access to UsersCollection, implement as normal mongodb operation;
}
// Then, UserManager
public class ApplicationUserManager : UserManager<How many generic would depend on use cases>
{
private readonly IApplicationUserStore _applicationUserStore;
// Constructtor that could resolve our ApplicationUserStore.
public ApplicationUserManager(IApplicationUserStore applicationUserStore,...)
{
_applicationUserStore = applicationUserStore;
}
public async Task<ICollection<ApplicationUser>> GetAllUserAsync() => _applicationUserStore.GetAllUserAsync();
// Registering things
services.AddScoped<IApplicationUserStore, ApplicationUserStore>();
services.AddScoped<ApplicationUserManager>();
}
This was just describe the idea, implementation should be vary.
I am writing a ABAC system in which I will decide if a user can access to certain data based on some roles/atributes/etc. However, there is a special kind of user (something like superadministrator) who should be able to access everything, everywhere, always. I don't want to go through all policies, controllers, actions and methods and add a check on this specific role. Is there a way to do it in a more centralized way? (for example: in the startup).
If it is not possible to add it to a global place, I was thinking on adding it at least globally on a controller level: I was looking here and I saw that the decorator [Authorize(Roles = "Administrator")] lets you restrict the access to a certain method/class just to Administrator users. However, I want kind of "the opposite". I mean something like an AuthorizeAlways that had the following behaviour:
[AuthorizeAlways(Roles = "SuperAdministrator")]
public class ControlPanelController : Controller
{
[Authorize(Roles = "SetterUser")]
public ActionResult SetTime()
{
}
[Authorize(Roles = "PowerUser")]
[MinimumAgeAuthorize(50)]
public ActionResult ShutDown()
{
}
}
In this case I'd like that SuperAdministrator (even if they are 49 years old) has access to everywhere. A SetterUser has access only to SetTime and only a PowerUser who is older than 50 years old can access ShutDown.
I don't know if this makes much sense. Is it possible? Where could I do it? Thanks!
This blog post provides a good tutorial for how to implement custom authorization:
https://seanspaniel.wordpress.com/2019/12/13/custom-authorization-in-asp-net-core-3/
From that tutorial, in the CustomAuthorizationMiddleware class you could check for the "SuperAdministrator" role and grant access to every endpoint.
public static class CustomAuthorizationMiddleware
{
public static async Task Authorize(HttpContext httpContext, Func next)
{
var endpointMetaData = httpContext.GetEndpoint().Metadata;
bool hasCustomAuthorizeAttribute = endpointMetaData.Any(x => x is CustomAuthorizeAttribute);
if (!hasCustomAuthorizeAttribute)
{
await next.Invoke();
return;
}
CustomAuthorizeAttribute customAuthorizeAttribute = endpointMetaData
.FirstOrDefault(x => x is CustomAuthorizeAttribute) as CustomAuthorizeAttribute;
// Check if user has allowed role or super administrator role
bool isAuthorized = customAuthorizeAttribute.AllowedUserRoles
.Any(allowedRole => httpContext.User.IsInRole(allowedRole))
|| httpContext.User.IsInRole("SuperAdministrator");
if (isAuthorized)
{
await next.Invoke();
return;
}
httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await httpContext.Response.WriteAsync("unauthorized");
}
}
I'm using various resources to try and implement an Identity system with MS Access for an AngularJS app.
I created classes which implement the Identity interfaces I need, and I'm stuck at the stage of creating the Account controller (which will be the API for registeration, login, etc).
The class UserStore implements IUserStore and has the CreateAsync method:
public Task CreateAsync(TUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
var result = userTable.Insert(user);
return Task.FromResult(result);
}
AccountController implements ApiController and has the Register method:
[AllowAnonymous]
[Route("register")]
public async Task<IHttpActionResult> Register(IdentityUser user)
{
var result = await _userStore.CreateAsync(user);
if (result == 0)
{
return InternalServerError();
}
return Ok();
}
userTable.Insert(user) returns an int indicating the number of rows affected in the DB table. The line var result = await _userStore.CreateAsync(user); throws an error, saying it actually returns void, and so void cannot be assigned to var (or to anything else).
I'm having a hard time understanding how to write the Register method and the CreateAsync method so that they will work together.
BTW, I thought I should give up the whole async thing and just make CreateAsync and Register return the int value as-is, but I can't do that since UserStore implements `IUserStore'.
The issue is that the return type cannot be passed from the CreateAsync as it is simply a Task return. It would need to be Task<int> but you cannot do that since it's implementing the IUserStore interface. Why do you need the result, I'm assuming you do not?
Try this instead:
[AllowAnonymous]
[Route("register")]
public async Task<IHttpActionResult> Register(IdentityUser user)
{
await _userStore.CreateAsync(user);
return Ok();
}
Additionally, consider making userTable.Insert(user) an async call if at all possible.
I would suggest not giving up on async/await. Especially for I/O bound operations on a web site like this, they really make your application usable.
If you're really concerned about whether or not the insert might be problematic, try this instead:
public async Task CreateAsync(TUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
var existingUser = await this.FindByIdAsync(user.Id);
if (existingUser != null)
{
await this.UpdateAsync(user);
}
else
{
userTable.Insert(user);
}
}
I'm building a set of APIs. One of them is a authentication API, that returns JWT tokens. I'm trying to implement a Session per Action approach, with ActionFiltersAttribute. My controller is decorated with this attribute:
public class NHibernateSessionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var session = NHibernateSessionManager.SessionFactory.OpenSession();
session.BeginTransaction();
CurrentSessionContext.Bind(session);
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var session = CurrentSessionContext.Unbind(NHibernateSessionManager.SessionFactory)
if (session != null)
{
if (session.Transaction.IsActive)
{
try
{
session.Transaction.Commit();
}
catch
{
session.Transaction.Rollback();
}
}
session.Close();
}
}
}
Where is the problem? To manage the users with NHibernate istead of Entity Framework I've implemented all the needed ASP.NET Identity interfaces, and they all return a Task<T>. For example on the following action:
AccountController.cs
public async Task<IHttpActionResult> ChangePassword(ChangePasswordBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword,
model.NewPassword);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
There ChangePasswordAsync calls internally several methods wich have code inside new tasks, where the SessionFactory.GetCurrentSession() causes a NullException. As far as I know, because that's another Thread and context.
In code, the first attempt to save executes with no fail, and the second not. The duplicated code is only to ilustrate the situation.
UserStore.cs
public System.Threading.Tasks.Task UpdateAsync(UserModel user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
//Here the Session is found
DataProviderI<UserModel, int> prov = new DataProviderImplGeneric<UserModel, int>();
prov.Save(user);
return Task.Factory.StartNew(() =>
{
//Here the Session is NOT found
DataProviderI<UserModel, int> prov2 = new DataProviderImplGeneric<UserModel, int>();
prov.Save(user);
});
}
What's the best way to deal with this and get the same ISession during all the Action ?
As far as I know NHibernate doesn't support async calls and I could refactorize the methods with a return of type Task.FromResult(0) on void cases or Task.FromResult<T>(T) where T is an object, but I would like to know if there's another solution to take advantage of parallelism
Seems like the problem you are dealing with is stemming from the HttpContext being null while inside a task and therefore cannot access the NHibernate session stored inside of the context variable.
You could work around this by getting the ISession before you call into a task.
Add in a constructor to your DataProviderImplGeneric so you manually pass one in.
DataProviderImplGeneric(ISession session) {
this.session = session;
}
just retrieve the session before you call into this from inside a task.
var session = GetCurrentNHibernateSession();
return Task.Factory.StartNew(() => {
var dataProvider = DataProviderImplGeneric<UserModel, int>(session);
return dataProvider.Save(user);
}
I've wrote a custom UserStore for the ASP.NET Identity which I'm using it in an ASP.NET MVC 5.1 application. Everything is working as expected and I'm very happy with this new feature (Identity) of ASP.NET...
The issue is that I think is almost 2 days since I'm trying to return an error from the UpdateAsync method and somehow it seems that I'm not able to return anything.
LE: I'm using ASP.NET Identity Core 2.0.0-beta1
This is my code in few lines:
public Task UpdateAsync(IdentityUser user)
{
AdminUserEntity userEntity = new AdminUserEntity();
userEntity.IsNew = false;
userEntity.Id = user.UserModel.Id;
userEntity.UserCompleteName = user.UserModel.UserCompleteName;
userEntity.IsDisabled = user.UserModel.IsDisabled;
if (!String.IsNullOrEmpty(user.UserModel.PasswordHash))
userEntity.PasswordHash = user.UserModel.PasswordHash;
if (user.Claims != null && user.Claims.Count > 0)
{
foreach (Claim claim in user.Claims)
{
AdminUserClaimEntity claimEntity = userEntity.AdminUserClaims.AddNew();
claimEntity.AdminUserUniqueId = user.UserModel.UniqueId;
claimEntity.ClaimType = claim.Type;
claimEntity.ClaimValue = claim.Value;
}
}
try
{
byte[] timestamp = Convert.FromBase64String(user.UserModel.Timestamp);
AdminUserEntityManagement.UpdateCompleteAdminUserEntity(userEntity, timestamp);
}
catch (Exception exception)
{
List<string> errors = new List<string>() {exception.Message};
return Task.FromResult<IdentityResult>(IdentityResult.Failed(errors.ToArray()));
}
return Task.FromResult<object>(null);
}
Though there is an exception and the catch block is getting executed, the following line always returns success:
IdentityResult result = await UserManager.UpdateAsync(identityUser);
Can someone please tell me what am I missing?
I'm assuming this UpdateAsync is in your ApplicationUserManager class, shouldn't the signature be
public override Task<IdentityResult> UpdateAsync
if you are trying to change how the UserManager's UpdateAsync method works.
First of all, #hao-kung, thank you for your help. Now I can say that I've seen the big picture. Though I can't say that I like it, I understood how UserManager is working.
Also, if you think that I'm saying something wrong, please, correct me.
For the others, in order to customize UserManager behavior (even if you just want to surface an error which you are anyway able to catch in the UserStore custom class) you have to follow these steps (for better understanding I will exemplify by describing what I've did to catch a business exception for the UpdateAsync method and show it in the interface):
In the UserStore class (which should implement among other interfaces the IUserStore interface), don't catch any exception.
public Task UpdateAsync(IdentityUser user)
{
// here an exception will be thrown if there is a concurrency issue
byte[] timestamp = Convert.FromBase64String(user.UserModel.Timestamp);
AdminUserEntityManagement.UpdateCompleteAdminUserEntity(userEntity, timestamp);
return Task.FromResult<object>(null);
}
Extend UserManager class and override the method that you want to customize (catch the business exception thrown earlier and surface it to the interface):
public class AdminUserManager : UserManager<IdentityUser>
public override async Task<IdentityResult> UpdateAsync(IdentityUser user)
{
Task<IdentityResult> result = base.UpdateAsync(user);
try
{
IdentityResult identityResult = await result;
}
catch (Exception exception)
{
List<string> errors = new List<string>() { exception.Message };
return IdentityResult.Failed(errors.ToArray());
}
return result.Result;
}
In the MVC controller (for example) read the result of the UpdateAsync method from the extended UserManager class:
IdentityResult result = await UserManager.UpdateAsync(identityUser);
if (result.Succeeded)
{
this.SetNotification("The user has been updated.", EnumToastrNotificationType.Info);
return RedirectToAction("ShowUsers", "UserManagement");
}
else
{
this.AddErrors(result);
}