By default, MVC 5 Single Page Application uses EntityFramework to store users and passwords for authentication.
In my scenario, I must use an existing homemade AuthenticationService.
I decided to create a custom IUserStore. I then must implement the GetPasswordHashASync to validate credentials.
Our architect considers this as a security breach but I do not agree with this. I then would like to get your opinion about this.
What is the difference between getting the PasswordHash for the database of another service on the same server node. In my opinion, I dont this it is a security breach...
Here's some code to demonstrate how it works.
The user logs in with his credentials so it calls the Login Method of my AccountController. Then, it calls the UserManager FindUserAsync:
var user = await UserManager.FindAsync(model.Email, model.Password);
Since I create my own IUserStore, I call our service (WFC) like this:
if (client.IsUsernameExists(userName, remoteInfo, out messages))
{
user = new ApplicationUser() { Email = userName, Username = userName};
}
Under the hood it then calls the GetPasswordHashAsync. My implementation then call our service again:
passwordHash = client.GetPasswordHash(user.Username, RemoteInfo, out messages);
Any thoughts?
The interface you are looking to implement is the IUserPasswordStore. Nothing wrong with that.
This is the correct way of implementing the IdentityStores of OWIN (and probably other authentication frameworks).
I've implemented my own UserStore for a MongoDB implementation of OWIN. Here is my implementation of the IUserPasswordStore
public Task SetPasswordHashAsync(TUser user, string passwordHash)
{
user.PasswordHash = passwordHash;
return Task.FromResult(0);
}
public Task<string> GetPasswordHashAsync(TUser user)
{
return Task.FromResult(user.PasswordHash);
}
The password hash is stored in the DB, so when you pull the user from the DB, it has a property which is the hash. So the appropriate implementation for GetPasswordHashAsync, is to return the hash from the user object.
Related
I used Identity Server and protected endpoints with policies and roles. Those are reflected in the access token I'm distributing to the client. Today, I got the suggestion that instead of protecting a method like this:
[Authorize(Policy = "Elevated"), HttpGet("metadata")]
public IActionResult GetTenantMetadata() { ... }
we could skip the role section of the JWT and only do the following.
[Authorize, HttpGet("metadata")]
public IActionResult GetTenantMetadata() { ... }
Then, in the Startup.cs, we'd register a custom handler like this.
services.AddHttpContextAccessor();
services.AddTransient<IAuthorizationHandler, HeaderHandler>();
The actual security would be then performed in the handler based on the sub value from the token. No roles, no scopes no nothing. To me, it appears intuitively ill-advised putting a lot of responsibility on me to create the protective logic, instead of relying on IDS and people way smarter than me. All the solutions I've seen deal with security using claims and roles. So my gut feeling says it's a bad idea.
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
CustomRequirement requirement)
{
HttpRequest request = Accessor.HttpContext.Request;
string param = request.Headers.SingleOrDefault(a => a.Key == "param1");
string someAccess = dbContext.GetAccess(param, context.User.GetUserId());
bool authorized = SomeEvaluation(param, someAccess);
if (authorized) context.Succeed(requirement);
else context.Fail();
return Task.CompletedTask;
}
However, I wasn't able to motivate my choice (other than the guts and others' samples), so finally, I got unsure. I'm humble to understand that if I can't explain why, then maybe I'm wrong.
I googled claims based security and roles policies security etc. but I could come up with a name for the suggested alternative. Consequently, I haven't found any material discussing that matter.
What is such a custom authorization method called and what's the advantage/caveat of it?
It's still claims authorization, just not a good / practical one: you're only using the sub claim. There's no reason to throw away/not use perfectly good claims provided by a trusted identity provider.
First, letting clients send the user ID via a header is a security risk. How would you know if it's not tampered with, and the user is not pretending to be an admin? You can't, that's why you ask an ID provider to authenticate the user and sign a token for identification.
Second, cherry-picking the user ID and fetching other user claims at runtime from a database or API could work, but it may not always possible (app doesn't keep user data / delegates authentication to a 3rd party) or feasible (high traffic application, too many db lookups).
To authorize an action, you can declare policies with assertions. But for more advanced or imperative checks, you have the option to implement an AuthorizationHandler and imperatively allow/block an action.
It's useful for resource-based authorization, but an overkill for what would be a single line of code if the claims you need are already provided in the access token.
Further info
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/claims?view=aspnetcore-5.0
I have been doing a lot of research but none resulted in helping me understand what is the point of UserClaim Table.
When you create a MVC5 project, there are some default tables created upon your database being registered. I understand the purpose of all of them except UserClaim.
From my understanding, User Claims are basically key pair values about the user. For example if I want to have a FavouriteBook field, I can add that field to the user table and access it. Actually I already have something like that built in. Each of my users have "Custom URL" And so I have created a claim in the following way:
public class User : IdentityUser
{
public string CustomUrl { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
userIdentity.AddClaim(new Claim("CustomUrl", CustomUrl));
return userIdentity;
}
}
public static class UsersCustomUrl
{
public static string GetCustomUrl(this IIdentity identity)
{
var claim = ((ClaimsIdentity)identity).FindFirst("CustomUrl");
return (claim != null) ? claim.Value : string.Empty;
}
}
Above basically allows me to access the CustomUrl by simply calling User.Identity.GetCustomUrl()
The above code won't write to the UserClaims table as the value exists in the Users Table. So what is the point of this table?
I am speculating that maybe I should add CustomUrl to UserClaims and somehow bind that to identity and that may what it is for? I would love to know the answer!
Claims are really useful in cases where you present multiple ways in which your users can register / sign on with your website... in particular, I'm talking about third-party authentication with organisations such as Google, Facebook and Twitter.
After a user has authenticated themselves through their chosen third party, that third party will disclose a set of claims to you, a set of information that describes the user in a way that you can identify them.
What information the claims will contain varies from provider to provider. For example, Google will share the users email address, their first name, their last name but compare that to Twitter... Twitter doesn't share any of that, you receive the identifier of their Twitter account along with their access tokens.
Claims based authentication provides a simple method to facilitate all this information, whilst the alternative may very well have meant creating tables in your database for each individual provider you worked with.
I'm working on an authentication system that uses ASP.NET Identity with Entity Framework, and I want to have a few claims that are computed values instead of being hardcoded into the claims table.
When a user logs in, how can I add dynamic claims to that login session without actually adding them to the claims table?
For example, I may want to store each user's DOB, but I want add IsBirthday as a claim if the login date matches the user's DOB. I don't want to have to store a "IsBirthday" claim for each user since it changes daily for everyone.
In my code, I use this to log in:
var signInResult = await SignInManager.PasswordSignInAsync(username, password, false, false);
After this is called I can reference the ClaimsPrincipal, but the Claims property is an IEnumerable, not a List, so I can't add to it.
EDIT: I should also mention I am using the Microsoft.AspNet.Identity.Owin libraries.
OK, everyone, I did a bit of digging into the classes provided in ASP.NET Identity and found the one I needed to override. The SignInManager class has a CreateUserIdentityAsync method that does exactly what I was wanting. The following code added the IsBirthday claim to my identity but didn't store it in the database.
public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
{
public override async Task<System.Security.Claims.ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
var identity = await base.CreateUserIdentityAsync(user);
identity.AddClaim(new System.Security.Claims.Claim("IsBirthday", user.DOB.GetShortDateString() == DateTime.Now.GetShortDateString()));
return identity;
}
// ... EXCLUDING OTHER STUFF LIKE CONSTRUCTOR AND OWIN FACTORY METHODS ...
}
I have a WPF application that connects to a WCF service. I need users to be authenticated to call any method of my service but I also need users to be able to register if thy don't have an account.
I first thought about using a usernamePasswordvaldator but I couldn't find a way to create a register method that doesn't go through the validate method of my validator class.
I then saw MembershipPorvider but didn't find any example that matches my case.
You can create a special user in the database. Add this user to a special membership role.
This special user can only create new users, it doesn't have any other permissions to the service methods.
You should add a PriciplePermission attirbute on all of your methods, and allow your new role (CreateUserRole) to access just the CreateUser method.
[PrincipalPermission(SecurityAction.Demand, Role = "CreateUserRole")]
public void CreateUser(string username, string password)
All other methods should have different roles:
[PrincipalPermission(SecurityAction.Demand, Role = "ADMINISTRATORS")]
public bool DeleteUser(string username)
so that this special user can only access the CreateUser method.
I'm very curious as to why I need to create a custom MembershipProvider and not supposed to do something like this to login a potential user.
[HttpPost]
public ActionResult Login(string username, string password)
{
UserUnitOfWork unitOfWork = new UserUnitOfWork();
User user = unitOfWork.UserRepository.GetByUsername(username);
if (user != null)
{
SaltedHashHelper saltHelper = new SaltedHashHelper();
if (saltHelper.VerifyHashString(username, user.Password, user.Salt))
FormsAuthentication.SetAuthCookie(user.Username, false);
}
else
{
// User cannot be verified.
}
return View();
}
If I create a custom MembershipProvider then I will have to create a custom MembershipUser because I am not using asp.net membership tables. I feel like that is more of a headache when your not using the aspnet membership tables. Maybe I am wrong.
Does anybody see anything wrong with my approach above? I'm curious.
Your approach is fine so long properly salt and hash the passwords and properly protect yourself from SQL injection.
The built in providers will be better tested than your custom built authentication provider, and may be more secure depending on your implementation.
You don't need to create a custom membership provider, you can use what ASP.NET gives you right out of the box. In fact implementing your own authentication scheme is always inadvisable if there's any other way out of it whatsoever. Worst case, just override the provider methods that you really need to behave differently.
Have a look at OWASP Top 10 for .NET developers part 7: Insecure Cryptographic Storage for some more background on why this is a bad idea.