Get the current WindowsPrincipal on a Forms authentication website - asp.net

I'm coming across a peculiar request: I have a website that uses Forms Authentication, but it now needs to grab the WindowsPrincipal (Windows Authentication) at some point in order to reuse it.
My first instinct was to create a new page, and to disable Anonymous Access on IIS for that precise page. When I'm on that page, Request.ServerVariables["LOGON_USER"] gives me the current Windows login name as expected.
Unfortunately, Context.User still gives me a GenericPrincipal.
Any idea on how I could get the current WindowsPrincipal in a FormsAuthentication Application? (recreating it by asking the user for his password is not an option)

Found it: Context.Request.LogonUserIdentity is what should be used in this scenario.
It will return the windows user that made the request if Anonymous Access is disabled on IIS (otherwise it'll return the IIS anonymous user).
For those interested on how to reuse it:
lblUserName.Text = WindowsIdentity.GetCurrent().Name;
// will return the ASP.Net user (that's useless to us)
WindowsIdentity id = Context.Request.LogonUserIdentity;
WindowsImpersonationContext ctx = id.Impersonate();
try
{
lblUserName.Text = WindowsIdentity.GetCurrent().Name;
// will return the correct user
// (...) do your stuff
}
finally
{
ctx.Undo();
}

Related

ASP.NET MVC Windows Authenticate with extending properties

I need help of experts!
I write ASP.NET MVC 4 Intranet application. I using Windows Authentication, the domain name get through User.Identity.Name. I have a database with the configured data context, where more complete information about the user: Last name, First name, E-mail, etc. As well as a list of user access groups.
Needed to make sure that a user open a program, has received the required access from the database, his name brought up in the upper right corner of the page.
Now I have realized that with the use of OWIN Asp.Net Identity. I created a base controller that inherits the other controllers, it ordered CurrentUser method for the user and his SignIn:
protected ApplicationUser CurrentUser
{
get
{
var curUser = AppUserManager.FindByName(User.Identity.Name);
if (curUser != null)
SignInManager.SignIn(curUser, true, true);
else
curUser = new ApplicationUser();
return curUser;
}
}
protected ApplicationSignInManager SignInManager
{
get
{
return HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
}
I understand that these operations are unnecessary, when you refresh the page every time it is necessary to drag into the database, users receive and make SignIn, which is unnecessary.
I'm sure you can realize all without using Asp.Net Identity. For example by expanding IPrincipal User, adding him to the field name, which are needed when displaying pages and adding once Roles from the database and save all in Cookies.
Please, help me! And sorry for my bad english...

.GetDirectoryEntry throws COM exception, code:0x800720720 when attempting to bind to object

My application is running on IIS 7.0, it is supposed to impersonate the authenticated user and unlock or reset other user accounts. It worked fine when I was developing it on my workstation, but when I uploaded it to the server the impersonation stopped working, it won't bind to the AD objects and keeps throwing the same exception. I had the same problem earlier with using PrincipalContext but I was able to get around that using using(HostingEnvironment.Impersonate()) because I didn't need the authenticated user for that action. But now I do so I can't use that workaround. I need an actual fix for the issue and I would really appreciate some input. I've been searching far and wide for a solution to the problem and so far none of them have worked. Here is the code I'm using that keeps throwing the exception.
using (DirectorySearcher search = new DirectorySearcher(directoryEntries[counter]))
{
//Sets the filter to find a user object with the target user username
search.Filter = "(&(objectClass=user)(sAMAccountName=" + username + "))";
//Sets the searchscope to search the whole AD
search.SearchScope = SearchScope.Subtree;
//Executes the search for one result and stores it in result.
SearchResult result = search.FindOne();
//Creates a directory entry from the result.
using (DirectoryEntry targetUser = result.GetDirectoryEntry())
{
//This if-else statement checks if the user is locked, if it is then
//the unlock is performed, and the unlockPerformed variable is set to
//true, if it isn't then unlockPerformed is set to false.
if (Convert.ToBoolean(targetUser.InvokeGet("IsAccountLocked")))
{
targetUser.InvokeSet("IsAccountLocked", false);
targetUser.CommitChanges();
unlockPerformed = true;
}
else
{
unlockPerformed = false;
}
}
}
This code worked perfectly before I uploaded it, any suggestions are greatly appreciated, I'll be monitoring this so I can get a fix asap.
Thanks in advance.
UPDATE: FIXED THE ISSUE
Apparently the program running from the hosting machine but not from a remote machine is actually a very telling symptom according to this article: http://msdn.microsoft.com/en-us/library/vstudio/ms730088(v=vs.100).aspx .
According to that article, the problem is that the impersonate setting was set to impersonation which causes this behaviour, I wanted DELEGATION. In order to do this I used this page to get information on different methods of delegation and impersonation, I used the "Impersonating Original Caller Temporarily" section.
In the web.config file:
<identity impersonate="false"/>
If this is set to false then it tries to impersonate the user for every action, which can cause issues like the one I was experiencing and isn't what I was trying to achieve.
In the code:
using System.Security.Principal;
...
// Obtain the authenticated user's Identity
WindowsIdentity winId = (WindowsIdentity)HttpContext.Current.User.Identity;
WindowsImpersonationContext ctx = null;
try
{
// Start impersonating
ctx = winId.Impersonate();
// Now impersonating
// Access resources using the identity of the authenticated user
}
// Prevent exceptions from propagating
catch
{
}
finally
{
// Revert impersonation
if (ctx != null)
ctx.Undo();
}
// Back to running under the default ASP.NET process identity
This fix is fairly easy, but its damn near impossible to find good clear information on this topic, I hope someone will find this useful some day, I can't be the only one experiencing these issues.
Apparently the program running from the hosting machine but not from a remote machine is actually a very telling symptom according to this article: http://msdn.microsoft.com/en-us/library/vstudio/ms730088(v=vs.100).aspx . According to that article, the problem is that the impersonate setting was set to impersonation which causes this behaviour, I wanted DELEGATION. In order to do this I used this page to get information on different methods of delegation and impersonation, I used the "Impersonating Original Caller Temporarily" section.
In the web.config file:
<identity impersonate="false"/>
If this is set to false then it tries to impersonate the user for every action, which can cause issues like the one I was experiencing and isn't what I was trying to achieve.
In the code:
using System.Security.Principal;
...
// Obtain the authenticated user's Identity
WindowsIdentity winId = (WindowsIdentity)HttpContext.Current.User.Identity;
WindowsImpersonationContext ctx = null;
try
{
// Start impersonating
ctx = winId.Impersonate();
// Now impersonating
// Access resources using the identity of the authenticated user
}
// Prevent exceptions from propagating
catch
{
}
finally
{
// Revert impersonation
if (ctx != null)
ctx.Undo();
}
// Back to running under the default ASP.NET process identity
This fix is fairly easy, but its damn near impossible to find good clear information on this topic, I hope someone will find this useful some day, I can't be the only one experiencing these issues.

Manually associate session with current request asp.net MVC

I have a MVC 5 asp.net website where I need to expose a number of REST APIs to a stand-alone mobile client. The rest of the site is using Forms based security where it sets the ASP.NET_SessionId as a cookie, and that is used to authenticate the user with the request after they log in. With my mobile application, I am not able to use the cookie method because of the cross-doman issue. What I would like to do is add a header "X-SessionId" with the value of the ASP.NET_SessionId, then on the server side, have a filter that looks for that field, and if it is present, associates the request with the given session. (Client will log in with an AJAX POST call which will return the ASP.NET_SessionId upon successful login).
Is this possible?
Something like this?
public sealed class CustomSecurityAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (string.IsNullOrEmpty(filterContext.HttpContext.Request.Headers["X-SessionId"]) && IsAuthenticated(ilterContext.HttpContext.Request.Headers["X-SessionId"]))
filterContext.Result = new HttpNotFoundResult();
}
private bool IsAuthenticated(string sessionId)
{
// get your user details from your database (or whatever)
var user = new UserRepository().Get(sessionId);
if (user == null)
return false;
// build up an identity, use your own or out of the box.
FormsIdentity itentity = new MyIdentity(user);
// Set the user
filterContext.HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(itentity , user.Roles);
return true;
}
}
You are going to have to store current sessions in your database, so for example when a user logs in grab the sessionid and stick it in the db, so you know they have 1..n current sessions.
Then you can look it up as part of your authentication.
Edit:
Let's take a step back, never mind cookies and sessions for the moment.
You have a website and a restful api, they both servce different purposes and clients and have different security requirements.
So what are the most common options for securing your Api?
Basic authentication.
Most restful APIs require a username/password to be sent through with each request, as part of the headers or in the request itself.
An authentication token
You can provide a token associated with a user account (a guid could suffice) when requests are made you check for the token.
Using an existing protocal like OAuth
I would recommend using these common scenarios to be sure you don't miss something and open your self up to security vulnerabilities.
Is there a reason you can't use any of these?

Setting domain on Authentication Cookie breaks authentication in MVC

I'd like to share authentication between two websites as I slowly rewrite functionality from the old one and transition to the new one. I found an answer on here to do that: Sharing Authentication between ASP.NET sites
The problem is that when I set the domain property, the authentication cookie stops working. The user still is authenticated succesfully, and the cookie appears to be created and set correctly... but when the site is loaded the user is forced to the login screen.
I'm using the standard MVC generated authentication code, which works fine as long as domain is not set:
public void SignIn(string userName, bool createPersistentCookie)
{
if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
}
If I debug and look at the FormsAuthentication object it looks like the domain and everything else is set correctly.
So what could be causing this, and how do I fix it?
Could you please try with setting a machine key in your web.config? It must be the same within every web site.
http://aspnetresources.com/tools/machineKey

User can log in without supplying password on localhost

I have a Asp.net web site built on C# with Forms Authentication. We use an Active Directory to authenticate the users, and everything works fine. But today we realized that it's possible to login to any account by just entering the username and click Login, without supplying any password! This is only happening on the development environment running on localhost (thank god!), but I don't like it...
I've never seen this behaviour before, and would really like someone to explain how this could happen. Is this a developer feature built by Microsoft? Or did someone at my office make a backdoor without telling the rest? I will investigate this last option further, but until then - have anyone encountered this before?
Big thanks in advance!
EDIT:
This is where the authentication returns true for every username I throw at it - with a blank password. Other passwords return false.
using (var context = new PrincipalContext(ContextType.Domain))
{
result = context.ValidateCredentials(username, password);
}
PrincipalContext is the default from System.DirectoryServices.AccountManagement
After some more investigation I found this on MSDN which states:
The ValidateCredentials method binds to the server specified in the constructor. If the username and password parameters are null, the credentials specified in the constructor are validated. If no credential were specified in the constructor, and the username and password parameters are null, this method validates the default credentials for the current principal.
and together with this information in the documentation of the constructor of PrincipalContext:
public PrincipalContext(System.DirectoryServices.AccountManagement.ContextType contextType, string name):
contextType: A System.DirectoryServices.AccountManagement.ContextType enumeration value specifying the type of store for the principal context.
name: The name of the domain or server for System.DirectoryServices.AccountManagement.ContextType.Domain context types, the machine name for System.DirectoryServices.AccountManagement.ContextType.Machine context types, or the name of the server and port hosting the System.DirectoryServices.AccountManagement.ContextType.ApplicationDirectory instance. If the name is null for a System.DirectoryServices.AccountManagement.ContextType.Domain context type this context is a domain controller for the domain of the user principal under which the thread is running. If the name is null for a System.DirectoryServices.AccountManagement.ContextType.Machine context type, this is the local machine name. This parameter cannot be null for System.DirectoryServices.AccountManagement.ContextType.ApplicationDirectory context types.
This leads me to conclude that since I don't use the name property in the constructor of the PrincipalContext, the domain controller will run under my own principal when on my dev machine. This could mean that it uses my users priveliges, which of course are much higher than the machine accounts the production servers are running as. This in turn could make all calls to Validate with nullas password automatically validate due to the higher level of privelige.
At least, this is my theory... Comments and thoughts are welcome, I will be closing this question soon.
Sounds like the problem is in the code. For a user to be AD authed the password needs to match. A security tokens is generated from AD, and this can't be done without the proper password or impersination (which also requires password).
Is the code using SELECT user FROM users WHERE password LIKE '%password%'? I've seen that done before! :(
Why don't you add null validation for password before calling ValidateCredentials? On a side note, client side authentication might help as well.
Why not you try to
handle NULL Parameter Exception.
check first both credentials have values.
using (var context = new PrincipalContext(ContextType.Domain))
{
if (string.IsNullOrEmpty(UserName) && string.IsNullOrEmpty(Password))
{
throw new ArgumentNullException();
result = null; // Or redirect to Login Page
}
else
{
result = context.ValidateCredentials(username, password);
}
}

Resources