In .NET, there appears to be several ways to get the current Windows user name. Three of which are:
string name = WindowsIdentity.GetCurrent().Name;
or
string name = Thread.CurrentPrincipal.Identity.Name;
or
string name = Environment.UserName;
What's the difference, and why choose one method over the other? Are there any other ways?
Environment.UserName calls GetUserName within advapi32.dll. This means that if you're impersonating another user, this property will reflect that.
Thread.CurrentPrincipal has a setter and can be changed programmatically. (This is not impersonation btw.)
WindowsIdentity is your current windows identity, if any. It will not necessarily reflect the user, think ASP.NET with FormsAuthentication. Then the WindowsIdentity will be the NT-service, but the FormsIdentity will be the logged in user. There's also a PassportIdentity, and you can build your own stuff to complicate things further.
You asked for alternative ways.
Of course, you can always use the native Windows API: GetUserName.
I believe the property was put in several places so that it would be easier for the programmer to find. There's only one logged in user, and only one respective name.
The three methods are described as follow:
HttpContext = HttpContext.Current.User, which returns an IPrincipal object that contains security information for the current Web request. This is the authenticated Web client.
WindowsIdentity = WindowsIdentity.GetCurrent(), which returns the identity of the security context of the currently executing Win32 thread.
Thread = Thread.CurrentPrincipal which returns the principal of the currently executing .NET thread which rides on top of the Win32 thread.
And they change in result depending on your IIS configuration as explained in this article:
http://msdn.microsoft.com/en-us/library/aa302377.aspx
Related
I am trying to make use of the active directory membership rather than SQL but there is very limited documentation available online. I have managed to connect my application to the domain controller without any problems but when you use "Context.User.Identity.Name" it comes up with DOMAIN\User. I want to basically drill down and get information such as full name, e-mail address, etc.
I just need a useful link and the searching I have done doesn't appear to have got me anywhere!
Many thanks
This should give you a bit of a clue: http://msdn.microsoft.com/en-us/library/ms973834.aspx
and here is a list of LDAP properties that you might want to play around with in the search result: http://www.computerperformance.co.uk/Logon/LDAP_attributes_active_directory.htm
Have you tried with this doc?
http://msdn.microsoft.com/en-US/library/system.web.security.activedirectorymembershipprovider%28v=vs.90%29.aspx
Can help?
If you are making use of Active Directory then you are likely using Windows Authentication. If so, all you need to do is:
Reference System.DirectoryServices.AccountManagement
In code (perhaps a controller action or model constructor)
// establishes your domain as the context for your user lookup
var principalContext = new PrincipalContext(ContextType.Domain, "domainName");
// gets the current user's UserPrincipal object
var userPrincipal.FindByIdentity(principalContext, #User.Identity.Name)
// example
var email = userPrincipal.EmailAddress;
Note:
This works because Windows Authentication means User.Identity on the current HttpContext is a WindowsIdentity and thus its Name property can be used to search AD.
You aren't limited to looking up the current user. You can use FindByIdentity() to search any value passed, and this method exists on other principals (ex. GroupPrincipal). You can also designate you wish to search by another type such as SID instead of Name.
Enjoy!
I'd like to utilize the Windows Authentication Model for authenticating users that use my C# 3.5 WinForms application:
The user that has logged on Windows is automatically logged in to my application.
If the user wants to log in explicitely, his user name and password should be checked by Windows, or even better, prompted by Windows with a standard Windows Dialog. The outcome should be another WindowsIdentity object.
The first was very easy to solve long time ago: I read the
WindowsIdentity identity = WindowsIdentity.GetCurrent();
Just to make sure, I check for the
if (identity.IsAuthenticated) { ... }
For the second case I've found some API calls in other SO Q&A's, but I'm pretty sure there must be a managed way for that, am I wrong?
Further I wonder whether my approach for 1. is save and appropriate. Thanks for your feedback!
Update: According to Ivan, I have to use the P/Invoke approach. This is basically alright, but then I still need a way to retrieve a WindowsIdentity object for that certain user, which has its IsAuthenticated property set to true. The P/Invoke call in itself doesn't return such an object. How can this be done?
I am not certain about this but it looks like you want to use the WindowsIdentity Ctor that takes an IntPtr. To get the parameter that is passed into this constructor you can PInvoke the Win32 API LogonUser() function. This will give you a WindowsIdentity for this user.
There is no managed way of doing this, you have to do pinvoke (api call) as you said. Approach #1 is totaly ok ... trust microsoft :)
How do I get an ICredential object within my web application?
I use Windows Authentication
I tried using the following code:
WindowsImpersonationContext securityContext =
Request.LogonUserIdentity.Impersonate();
After the last line of code both: CredentialCache.DefaultCredentials and CredentialCache.DefaultNetworkCredentials are still empty.
I know that the identity is right because the following property shows the right domain\user account I'm using:
Request.LogonUserIdentity.Name => "domain\user"
This is the authentication type of that object:
Request.LogonUserIdentity.AuthenticationType => "NTLM"
In order to get an ICredential implementation of the user, you first need to get a hold of the WindowsIdentity for the user (e.g. through Page.User.Identity). With the WindowsIdentity object you can impersonate the user. After successful impersonation you can get a hold of the credentials for the user through CredentialCache.DefaultCredentials.
The question now is, what do you want to do with the credentials? If you want to use those credentials to access some ressource on a different server you'd probably have to have Keberos delegation enabled to allow the server hop. I wouldn't know how to do that :-)
Where do you need it for?
You can retrieve the username from System.Threading.Thread.CurrentPrincipal.Identity
I'm having a lot of problems that whenever I call an ASP.Net Identity Async method I get access denied exceptions from SQL server.
The following returns a user:
var user = (from u in ctx.Users
where u.Id == 1
select u).FirstOrDefault();
Whereas
var user = await userManager.FindByIdAsync(1);
Triggers an exception
System.Data.SqlClient.SqlException: Login failed for user 'DOMAIN\MACHINE$'.
What seems to be happening is we have the following line in our web.config configuration/system.web section
<identity impersonate="true" userName="DOMAIN\Domain User" password="xxx" />
This impersonated user has permission to access the SQL Server database but the application pool user does not. It seems that whenever I call an async method in ASP.Net Identity, it falls back to the app pool user and loses the impersonation.
I did find a similar sounding question with an explanation here https://stackoverflow.com/a/30876110/1093406
Could this also be causing my problem?
Is there any way around this apart from changing the app pool user to one that has database access?
Is using web.config to set an impersonation user an old way of doing things and now bad practice?
Edit: After further investigation I've found these articles
http://www.hanselman.com/blog/AvoidUsingImpersonationInASPNET.aspx
http://blog.codeishard.net/2012/09/17/await-async-mvc-and-impersonation/
Looks as though using the impersonation is a bad idea unless someone can tell me otherwise.
Impersonation is no longer supported in Integrated Pipeline modes. Especially when using async methods.
The reason is that Impersonation happens on the thread, and when you call an async function, it may actually execute or return on a different thread.
You should use a WindowsImpersonationContext to wrap your database calls.
https://msdn.microsoft.com/en-us/library/system.security.principal.windowsimpersonationcontext(v=vs.110).aspx
using System.Security.Principal;
...
//Get the identity of the current user
IIdentity contextId = HttpContext.Current.User.Identity;
WindowsIdentity userId = (WindowsIdentity)contextId;
//Temporarily impersonate
using (WindowsImpersonationContext imp = userId.Impersonate())
{
//Perform tasks using the caller's security context
DoSecuritySensitiveTasks();
}
Make sure that you do this in a using block, since if an uncaught exception occurs in your code, you would end up not restoring the original context, and creating a security issue.
My solution in the end was to set the user on the App Pool rather than using Impersonation.
This seems to have exactly the same effect as setting impersonation in the Web.config with equivalent security, just without the problems when Async is being used.
As #Erik said in his answer, it seems that impersonation is no longer supported and according to http://www.hanselman.com/blog/AvoidUsingImpersonationInASPNET.aspx
it was never encouraged in the first place.
I need to write a program used internally where different users will have different abilities within the program.
Rather than making users have a new username and password, how do I tie into an existing domain server's login system?
Assume .NET (C#, VB, ASP, etc)
-Adam
For WinForms, use System.Threading.Thread.CurrentPrincipal with the IsInRole() method to check which groups they are a member of. You do need to set the principal policy of the AppDomain to WindowsPrincipal first.
Use this to get the current user name:
private string getWindowsUsername()
{
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
return Thread.CurrentPrincipal.Identity.Name;
}
And then something like this to check a role:
if (Thread.CurrentPrincipal.IsInRole("Domain Users") == true)
{}
In ASP.NET, the thread will belong to IIS, so instead you should
Set the virtual folder or website to require authentication
Get the user name supplied by the browser with Request.ServerVariables("LOGON_USER")
Use the DirectorySearcher class to find the users groups
I would use LDAP
and the DirectorySearcher Class:
http://msdn.microsoft.com/en-us/library/system.directoryservices.directorysearcher.aspx
Assuming this is served through IIS, I would tell IIS to authenticate via the domain, but I would keep authorization (what roles a user is associated with, accessible functionality, etc) within the application itself.
You can retreive the username used to authenticate via
Trim(Request.ServerVariables("LOGON_USER")).Replace("/", "\").Replace("'", "''")
OR
CStr(Session("User")).Substring(CStr(Session("User")).LastIndexOf("\") + 1)