Custom IPrincipal together with WindowsAuthentication - asp.net

Is there any good way of combining ASP.NET Windows Authentication with a custom IPrincipal/IIdentity object? I need to store the user's email address and have done so for Forms Authentication using a custom IIdentity/IPrincipal pair that I added to the Context.CurrentUser during the AuthenticateRequest event.
How would I best go by to accomplish this using WindowsAuthentication?

Maybe you could create your "ExtendedWindowsPrincipal" as a derived class based on WindowsPrincipal, and just add your extra data to the derived class?
That way, your ExtendedWindowsPrincipal would still be recognized anywhere where a WindowsPricinpal is needed.
OR: since you're talking about using Windows Authentication, you're probably in a Windows network - is there an Active Directory or a user database somewhere, where you could look up your e-mail address that you're interested in instead of storing it in the principal?
Marc

I ended up refactoring my initial solution into replacing the Principal instead of the Identity as I originally thought. Replacing the Identity proved troublesome, since i ran into security problems when creating an instance of a new extended WindowsPrincipal.
public class ExtendedWindowsPrincipal : WindowsPrincipal
{
private readonly string _email;
public ExtendedWindowsPrincipal(WindowsIdentity ntIdentity,
string email) : base(ntIdentity)
{
_email = email;
}
public string Email
{
get { return _email; }
}
}
In my Authentication module i replaced the principal on the HttpContext like this:
var currentUser = (WindowsIdentity)HttpContext.Current.User.Identity;
HttpContext.Current.User =
new ExtendedWindowsPrincipal(currentUser, userEmail);

Related

Using claims or just roles in ASP.NET Core MVC

In my ASP.NET Core MVC project, I have this scenario: a manager can do anything in the web application, you can call it super manager, and there is only one user. This "super" manager can add other managers that are more restricted, for example these managers cannot create users of type manager, or cannot see some information.
Technically, they have a lot in common. I already have many role types so I do not want to create another one called super manager where I will be creating only one user. So in this case should I use claims? or is it better to just create two roles? I know it will be less complicated but I want to know the best practice.
I'm new to ASP.NET Core so I appreciate examples or articles that could help me, thank you!
In my opinion, there is no difference between add the superadmin by claims or role claim, the role claim is also a type of claims.
In my opinion, if you don't have special requirement which need to add the user superadmin by claims, the best way is using claims. Since you could directly use the [Authorize(Roles = "Superadmin")] and no need to write another codes to add the claims by using identity factory.
If you want to add the superadmin by claims, you should use UserClaimsPrincipalFactory like this answer and add the claims policy like this article shows.
I suggest if you need to create many roles, you use
Claims-based authorization .
You can use Authorize[Roles = "Admin"] property or you can create a custom AuthorizeAttribute, for example:
public class AuthorizeAccess : AuthorizeAttribute, IAuthorizationFilter
{
public string UniqueCode { get; set; }
public void OnAuthorization(AuthorizationFilterContext context)
{
if (!context.HttpContext.User.HasClaim(c => c.Type == "CustomAccess" && c.Value.Equals(UniqueCode)))
{
// Redirect AccessDenied when the claim not exist.
context.Result = new RedirectToPageResult("/AccessDenied");
return;
}
}
}
And we can use it
[AuthorizeAccess(UniqueCode = "EDIT")]
public class IndexModel : PageModel
{
....
}
In this case you need to load a claim list in the login access
identity.AddClaim(new Claim("CustomAccess", "HOME_PAGE"));
identity.AddClaim(new Claim("CustomAccess", "EDIT"));
identity.AddClaim(new Claim("CustomAccess", "DELETE"));
identity.AddClaim(new Claim("CustomAccess", "INSERT"));

Are there any packages etc implementing dynamic role controller access for MVC3?

I have statically allowed access to controllers/action methods using the standard Authorize attribute with roles. I am using the default ASP.Net Membership Provider.
One of our clients wants finer grained access control. They would like to be able to dynamically assign which roles can access which controllers/actions etc. I've seen answers saying implement a CustomAuthorize Attribute.
Just wondered if there were any toolkits etc to this. It seems a reasonably standard feature. I guess something like this http://kbochevski.blogspot.com/2009/11/mvc-custom-authorization.html
Try a custom attribute like this:
public class DynamicAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var controllerName = httpContext.Request.RequestContext.RouteData.Values["controller"];
var actionName = httpContext.Request.RequestContext.RouteData.Values["action"];
// Get this string (roles) from a database or somewhere dynamic using the controllerName and actionName
Roles = "Role1,Role2,Role3"; // i.e. GetRolesFromDatabase(controllerName, actionName);
return base.AuthorizeCore(httpContext);
}
}
Just put this attribute on any action method that requires authorization and do a look up in a database with the controller name and action name to get the required roles.
Hope this helps,
Mark

Very simple single user login in ASP.NET MVC2?

I'm building my site, and I want to restrict a part of my site (The admin parts) from normal public display.
I am using LINQ for database access.
I have a Service class to handle calls to the database through LINQ
I have the whole site running, except for the Login part.
So far I have only been able to find examples using MembershipProvider and/or RoleProviders etc. And to be honest, it does seem like too much work for what I want. All this has to do is to let you in if you type the correct password in the input fields.
Can i really not avoid the Providers?
Since you only have a single user you don't need to create a database dependency. You can make a very simple authorization service based off of a hard coded credentials. For example,
public class AuthorizationService{
private AuthorizationService(){}
public static readonly AuthorizationService Instance = new AuthorizationService();
private const string HardCodedAdminUsername = "someone";
private const string HardCodedAdminPassword = "secret";
private readonly string AuthorizationKey = "ADMIN_AUTHORIZATION";
public bool Login(string username, string password, HttpSessionStateBase session){
if(username.ToLowerInvariant().Trim()==HardCodedAdminUsername && password.ToLowerInvariant().Trim()==HardCodedAdminPassword){
session[AuthorizationKey] = true;
return true;
}
return false;
}
public void Logout(HttpSessionStateBase session){
session[AuthorizationKey] = false;
}
public bool IsAdmin(HttpSessionStateBase session){
return session[AuthorizationKey] == true;
}
}
Then you can build a custom IAuthorizationFilter like:
public class SimpleAuthFilterAttribute: FilterAttribute, IAuthorizationFilter{
public void OnAuthorization(AuthorizationContext filterContext){
if(!AuthorizationService.Instance.IsAdmin(filterContext.HttpContext.Session)){
throw new UnauthorizedAccessException();
}
}
}
Then all you have to do is decorate the protected controller actions with the SimpleAuthFilter and you're application's login suddenly works. Yay! (Note, I wrote all this code in the StackOverflow answer window, so you may need to clean up typos, etc. before it actually works)
Also, you could refactor this to omit the username if you find that unnecessary. You will need to create a controller action for Login and Logout that make the corresponding calls to the AuthorizationService, if you want your protected controller actions to ever be accessible.
Its worth building a light-weight Membership Provider with minimal implementation; GetUser, ValidateUser etc methods. YOu dont need to implement the whole thing. It just helps with authorising pages and checking User.Identity etc when needed. You also dont need the RoleProvider or ProfileProvider to do this.
Its also scalable for the future.
UPDATE
You just need to implement the core methods to valudate and get the user and insert your own validation/data access code.
Something like this....
web.config settings:
<membership defaultProvider="ApplicationMembershipProvider">
<providers>
<clear/>
<add name="ApplicationMembershipProvider" type="YourNamespace.ApplicationMembershipProvider"/>
</providers>
</membership>
Login Code:
if (Membership.ValidateUser(username, password))
{
FormsAuthentication.SetAuthCookie(username, false);
}
You can set the status (logged in or not) in a session variable. Set the variable to true if the user entered the correct password, then on every page you want to restrict access, check if the variable is true.
#KristianB a while ago I gave an answer to this SO question. I believe it may be useful since it's very straightforward to implement and at the same time it's better than hardcoding a username and a password in your code.
Good luck!

ChangePassword control without provider

Is there a way to make ChangePassword control work without Membership provider? Like the same way Login control works through an Authenticate event, could I make this component to use my password changing function and then showing success view without me writing custom provider?
Thanks,
Eugene.
EDIT:
Just to clarify after some research through Reflector I came to conclusion that this control is totally useless without MembershipProvider. Every logic bit, like reading configuration file and validating user inputs is outsourced to providers, so you have to write this generic code as well.
This is the list of functions sufficient to make this control work:
public bool ChangePassword(string username, string oldPassword, string newPassword)
public MembershipUser GetUser(string username, bool userIsOnline)
public int MinRequiredNonAlphanumericCharacters { get; }
public int MinRequiredPasswordLength { get; }
The last two are only used for error message if you return false from ChangePassword function.
Looking at the .NET 3.5 source through reflector, when the ChangePassword button event gets detected by protected OnBubbleEvent, it calls AttemptChangePassword(). The implementation of that method looks roughly like this:
private void AttemptChangePassword() {
...
this.OnChangingPassword(loginCancelEventArgs);
if(!e.Cancel) {
MembershipProvider provider = LoginUtil.GetProvider(this.MembershipProvider);
...
}
It looks like you could:
Add a handler to the ChangingPassword event
In that event handler use the control's UserName and NewPassword properties to do your own custom work.
On success either redirect to a new URL or set the cancel flag on the event args and hide the ChangePassword control manually. There does not appear to be an easy way to use the SuccessView with this technique.
So it looks like it's somewhat possible, but the control definitely wasn't designed with this use in mind -- it's pretty well hard-wired to MembershipProvider.

How do you get the UserID of a User object in ASP.Net MVC?

I have some tables that have a uniqueidentifier UserID that relates to aspnet_Users.UserID. When the user submits some data for those tables, since the controller method has an [Authorize] I get a User object. I can get the username with User.Identity.Name, but how do I get the UserID to be able to establish (the ownership) relationship?
It seems you cannot get it from the User object but you can get it this way:
Guid userGuid = (Guid)Membership.GetUser().ProviderUserKey;
Here is the solution:
Include:
using Microsoft.AspNet.Identity;
Then use extension methods:
User.Identity.GetUserId();
Firstly, this answer is not strictly an MVC answer, but an ASP.NET answer. The fact that your site is MVC is irrelevant to solving the problem, in this case.
Hmm. I'm not very sure how you are handling your users in your system but it sounds like you using the (very evil) asp.net membership provider that comes out of the box with .net. This is hinted by the fact that you said
aspnet_Users.UserID
UserID is a uniqueidentifier (read: GUID).
With the default forms authentication system, which uses the default FormsIdentity, it only has a single property called Name (as you correctly noted). This means it has only one value where to place some unique user information. In your case, you are putting Name/UserName/DisplayName, in the Name property. I'm assuming this name is their Display Name and it is unique. Whatever value you are putting in here, it HAS TO BE UNIQUE.
From this, you can grab the user's guid.
Check this out.
using System.Web.Security;
....
// NOTE: This is a static method .. which makes things easier to use.
MembershipUser user = Membership.GetUser(User.Identity.Name);
if (user == null)
{
throw new InvalidOperationException("User [" +
User.Identity.Name + " ] not found.");
}
// Do whatever u want with the unique identifier.
Guid guid = (Guid)user.ProviderUserKey;
So, every time you wish to grab the user information, you need to grab it from the database using the static method above.
Read all about the Membership class and MembershipUser class on MSDN.
Bonus Answer / Suggestion
As such, i would CACHE that result so you don't need to keep hitting the database.
... cont from above....
Guid guid = (Guid)user.ProviderUserKey;
Cache.Add(User.Identity.Name, user.UserID); // Key: Username; Value: Guid.
Otherwise, you can create your own Identity class (which inherits from IIdentity) and add your own custom properties, like UserID. Then, whenever you authenticate (and also on every request) you can set this value. Anyway, this is a hard core solution, so go with the caching, right now.
HTH
User.Identity is an IPrincipal - typically of type System.Web.Security.FormsIdentity
It doesn't know anything about UserIDs - it's just an abstraction of the concept of an 'identity'.
The IIdentity interface only has 'Name' for a user, not even 'Username'.
If you're using MVC4 with the default SimpleMembershipProvider you can do this:
WebSecurity.GetUserId(User.Identity.Name) // User is on ControllerBase
(Where WebSecurity is in the nuget package Microsoft.AspNet.WebPages.WebData in WebMatrix
You can also use
WebSecurity.CurrentUserName
WebSecurity.CurrentUserId
(if you're using ASPNetMembershipProvider which is the older more complex ASPNET membership system then see the answer by #eduncan911)
If you are using the ASP.NET Membership (which in turn uses the IPrincipal object):
using System.Web.Security;
{
MembershipUser user = Membership.GetUser(HttpContext.User.Identity.Name);
Guid guid = (Guid)user.ProviderUserKey;
}
User.Identity always returns the state of the current user, logged in or not.
Anonymous or not, etc. So a check for is logged in:
if (User.Identity.IsAuthenticated)
{
...
}
So, putting it all together:
using System.Web.Security;
{
if (User.Identity.IsAuthenticated)
{
MembershipUser user = Membership.GetUser(HttpContext.User.Identity.Name);
Guid guid = (Guid)user.ProviderUserKey;
}
}
Best Option to Get User ID
Add Below references
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security;*
public myFunc()
{
.....
// Code which will give you user ID is
var tmp = User.Identity.GetUserId();
}
If you are using your own IPrincipal object for authorization, you just need to cast it to access the Id.
For example:
public class MyCustomUser : IPrincipal
{
public int UserId {get;set;}
//...Other IPrincipal stuff
}
Here is a great tutorial on creating your own Form based authentication.
http://www.codeproject.com/KB/web-security/AspNetCustomAuth.aspx
That should get you on the right path to creating an authentication cookie for your user and accessing your custom user data.
using System.Web.Security;
MembershipUser user = Membership.GetUser(User.Identity.Name);
int id = Convert.ToInt32(user.ProviderUserKey);
Its the ProviderUserKey property.
System.Web.Security.MembershipUser u;
u.ProviderUserKey
Simple....
int userID = WebSecurity.CurrentUserId;
Usually you can just use WebSecurity.currentUserId, but if you're in AccountController just after the account has been created and you want to use the user id to link the user to some data in other tables then WebSecurity.currentUserId (and all of the solutions above), unfortunately, in that case returns -1, so it doesn't work.
Luckily in this case you have the db context for the UserProfiles table handy, so you can get the user id by the following:
UserProfile profile = db.UserProfiles.Where(
u => u.UserName.Equals(model.UserName)
).SingleOrDefault();
I came across this case recently and this answer would have saved me a whole bunch of time, so just putting it out there.

Resources