How does HttpContext.Current.User.Identity.Name know which usernames exist? - c#

This is not necessarily an issue, I am just curious as to how it works. I have a method:
public static bool UserIsAuthenticated()
{
bool isAuthed = false;
try
{
if (HttpContext.Current.User.Identity.Name != null)
{
if (HttpContext.Current.User.Identity.Name.Length != 0)
{
FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
isAuthed = true;
string MyUserData = ticket.UserData;
}
}
}
catch { } // not authed
return isAuthed;
}
The HttpContext.Current.User.Identity.Name returns null if the user does not exist, but how does it know which usernames exist or do not exist?

For windows authentication
select your project.
Press F4
Disable "Anonymous Authentication" and enable "Windows Authentication"

The HttpContext.Current.User.Identity.Name returns null
This depends on whether the authentication mode is set to Forms or Windows in your web.config file.
For example, if I write the authentication like this:
<authentication mode="Forms"/>
Then because the authentication mode="Forms", I will get null for the username. But if I change the authentication mode to Windows like this:
<authentication mode="Windows"/>
I can run the application again and check for the username, and I will get the username successfully.
For more information, see System.Web.HttpContext.Current.User.Identity.Name Vs System.Environment.UserName in ASP.NET.

How does [HttpContext.Current.User] know which usernames exist or do
not exist?
Let's look at an example of one way this works. Suppose you are using Forms Authentication and the "OnAuthenticate" event fires. This event occurs "when the application authenticates the current request"
(Reference Source).
Up until this point, the application has no idea who you are.
Since you are using Forms Authentication, it first checks by parsing the authentication cookie (usually .ASPAUTH) via a call to ExtractTicketFromCookie. This calls FormsAuthentication.Decrypt (This method is public; you can call this yourself!). Next, it calls Context.SetPrincipalNoDemand, turning the cookie into a user and stuffing it into Context.User (Reference Source).

Assume a network environment where a "user" (aka you) has to logon. Usually this is a User ID (UID) and a Password (PW). OK then, what is your Identity, or who are you? You are the UID, and this gleans that "name" from your logon session. Simple! It should also work in an internet application that needs you to login, like Best Buy and others.
This will pull my UID, or "Name", from my session when I open the default page of the web application I need to use. Now, in my instance, I am part of a Domain, so I can use initial Windows authentication, and it needs to verify who I am, thus the 2nd part of the code. As for Forms Authentication, it would rely on the ticket (aka cookie most likely) sent to your workstation/computer. And the code would look like:
string id = HttpContext.Current.User.Identity.Name;
// Strip the domain off of the result
id = id.Substring(id.LastIndexOf(#"\", StringComparison.InvariantCulture) + 1);
Now it has my business name (aka UID) and can display it on the screen.

Also check that
<modules>
<remove name="FormsAuthentication"/>
</modules>
If you found anything like this just remove:
<remove name="FormsAuthentication"/>
Line from web.config and here you go it will work fine I have tested it.

Actually it doesn't! It just holds the username of the user that is currently logged in. After login successful authentication, the username is automatically stored by login authentication system to "HttpContext.Current.User.Identity.Name" property.
To check if the current user is authenticated, you MUST (for security reasons) check "HttpContext.Current.User.Identity.IsAuthenticated" boolean property that automatically holds this information instead of writing your own code.
If the current user is NOT authenticated, "HttpContext.Current.User.Identity.Name" property will be null or an empty string or "can take other values" (https://learn.microsoft.com/en-us/dotnet/api/system.security.principal.iidentity.name?view=netframework-4.8) obviously depending on the authentication mode used.
See: https://learn.microsoft.com/en-us/dotnet/api/system.security.principal.iidentity?view=netframework-4.8

Windows authentication gives the information about the user who is logged in. Here is how to set the windows authentication in your project:
you can select project from the menu bar, select yourProject Properties, select Debug, and check the "Enable Windows Authentication" as the image below,
then you will be able to know the user who is logged in by running this code in any controller
var strUserName = User;

Related

C# Get Logged in user email address from logged in user in Active Directory, IIS

I am developing an MVC app and I would like to auto login users into the system by retrieving their email address from Active Directory, use that email address to find a user in my database, if found log them into the system. The Idea is for the system to not force the user to input login details after already logging into their workstation that is setup on a work domain/Active Directory.
Please help me with pointers on this as I've tried most approaches described in many threads.
I've set my IIS app pool to Network Service
ASP.NET Impersonation is disabled..
I've enabled Load User Profile in app pool -> Advanced Settings
When I use the code below, the search fails:
using ( System.Web.Hosting.HostingEnvironment.Impersonate() )
{
PrincipalContext ctx = new PrincipalContext( ContextType.Domain );
UserPrincipal user = UserPrincipal.FindByIdentity( ctx, User.Identity.Name );
Response.Write( "User: " + User.Identity.Name );
Response.Write( user.EmailAddress );
Response.Flush();
Response.End();
}
Error: The (&(objectCategory=user)(objectClass=user)(|(userPrincipalName=)(distinguishedName=)(name=))) search filter is invalid.
Can someone please point me into the right direction of what I am trying to achieve? Am I approaching my requirement wrongly?
Thanks in advance.
For users to be logged in automatically, you need to use Windows Authentication. For that to work, there are a few prerequisites:
The users must be logged into Windows with the account they will use to authenticate to your website.
Your website must be added to the Trusted Sites in Internet Options in Windows (or considered an "Intranet Site") so that IE and Chrome will automatically send the credentials. Firefox has its own setting called network.negotiate-auth.trusted-uris.
Your server must be joined to a domain with a trust to the domain of the users (usually it's the same domain).
If all those things are true, then you can follow the instructions here to install the Windows Authentication feature in IIS (depending on your version). If you're using IIS Express for running it locally, then you can skip this step on your development machine.
Then enable the use of Windows Authentication in your website by modifying your web.config file. Add this under the <system.web> tag:
<authentication mode="Windows" />
And add this under <system.webServer>:
<security>
<authentication>
<windowsAuthentication enabled="true" />
<anonymousAuthentication enabled="false" />
</authentication>
</security>
Then, as long as your website is trusted by the browser (step 2 above), you will automatically be logged in. The username of the logged on user will be User.Identity.Name. All of the code you show will work, however you don't need to call System.Web.Hosting.HostingEnvironment.Impersonate().
public bool CheckUserInGroup(string group)
{
string serverName = ConfigurationManager.AppSettings["ADServer"];
string userName = ConfigurationManager.AppSettings["ADUserName"];
string password = ConfigurationManager.AppSettings["ADPassword"];
bool result = false;
SecureString securePwd = null;
if (password != null)
{
securePwd = new SecureString();
foreach (char chr in password.ToCharArray())
{
securePwd.AppendChar(chr);
}
}
try
{
ActiveDirectory adConnectGroup = new ActiveDirectory(serverName, userName, securePwd);
SearchResultEntry groupResult = adConnectGroup.GetEntryByCommonName(group);
Group grp = new Group(adConnectGroup, groupResult);
SecurityPrincipal userPrincipal = grp.Members.Find(sp => sp.SAMAccountName.ToLower() == User.Identity.Name.ToLower());
if (userPrincipal != null)
{
result = true;
}
}
catch
{
result = false;
}
return result;
}
I believe there are many ways on how we can retrieve AD users from C# MVC. http://www.codedigest.com/posts/5/get-user-details-from-active-directory-in-aspnet-mvc please take a look at this good example using the galactic API(free and open source package).

User.Identity fluctuates between ClaimsIdentity and WindowsIdentity

I have an MVC site that allows logging in using both Forms login and Windows Authentication. I use a custom MembershipProvider that authenticated the users against Active Directory, the System.Web.Helpers AntiForgery class for CSRF protection, and Owin cookie authentication middle-ware.
During login, once a user has passed authentication against Active Directory, I do the following:
IAuthenticationManager authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
authenticationManager.SignOut(StringConstants.ApplicationCookie);
var identity = new ClaimsIdentity(StringConstants.ApplicationCookie,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType);
if(HttpContext.Current.User.Identity is WindowsIdentity)
{
identity.AddClaims(((WindowsIdentity)HttpContext.Current.User.Identity).Claims);
}
else
{
identity.AddClaim(new Claim(ClaimTypes.Name, userData.Name));
}
identity.AddClaim(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "Active Directory"));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userData.userGuid));
authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, identity);
My SignOut function looks like this:
IAuthenticationManager authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
authenticationManager.SignOut(StringConstants.ApplicationCookie);
Logging in is performed via a jQuery.ajax request. On success, the Window.location is updated to the site's main page.
Logging in with both Forms and IntegratedWindowsAuthentication (IWA) works, but I've run into a problem when logging in with IWA. This is what happens:
The user selects IWA on the login page and hits the submit button. This is sent to the regular login action via an ajax request.
The site receives the request, sees the "use IWA" option and redirects to the relevant action. 302 response is sent.
The browser automatically handles the 302 response and calls the redirect target.
A filter sees that the request is headed to the IWA login action and that User.Identity.IsAuthenticated == false. 401 response is sent.
The browser automatically handles the 401 response. If the user has not authenticated using IWA in the browser yet, they get a popup to do so (default browser behavior). Once credentials have been received, the browser performs the same request with user credentials.
The site receives the authenticated request and impersonates the user to perform a check against Active Directory. If the user passes authentication, we finalize SignIn using the code above.
User is forwarded to the site's main page.
The site receives the request to load the main page. This is where things sometimes go awry.
The User.Identity at this point is of type WindowsIdentity with AuthenticationType set to Negotiate, and NOT as I would expect, the ClaimsIdentity created in the SignIn method above.
The site prepares the main page for the user by calling #AntiForgery.GetHtml() in the view. This is done to create a new AntiForgery token with the logged in user's details. The token is created with the WindowsIdentity
As the main page loads, ajax requests made to the server arrive with ClaimsIdentity! The first POST request to arrive therefore inevitably causes an AntiForgeryException where the anti-forgery token it sent is "for a different user".
Refreshing the page causes the main page to load with ClaimsIdentity and allows POST requests to function.
Second, related, problem: At any point after the refresh, once things are supposedly working properly, a POST request may arrive with WindowsIdentity and not with ClaimsIdentity, once again throwing an AntiForgeryException.
It is not any specific post request,
it is not after any specific amount of time (may be the first/second request, may be the hundredth),
it is not necessarily the first time that specific post request got called during that session.
I feel like I'm either missing something regarding the User.Identity or that I did something wrong in the log-in process... Any ideas?
Note: Setting AntiForgeryConfig.SuppressIdentityHeuristicChecks = true; allows the AntiForgery.Validate action to succeed whether WindowsIdentity or ClaimsIdentity are received, but as is stated on MSDN:
Use caution when setting this value. Using it improperly can open
security vulnerabilities in the application.
With no more explanation than that, I don't know what security vulnerabilities are actually being opened here, and am therefore loathe to use this as a solution.
Turns out the problem was the ClaimsPrincipal support multiple identities. If you are in a situation where you have multiple identities, it chooses one on its own. I don't know what determines the order of the identities in the IEnumerable but whatever it is, it apparently does necessarily result in a constant order over the life-cycle of a user's session.
As mentioned in the asp.net/Security git's Issues section, NTLM and cookie authentication #1467:
Identities contains both, the windows identity and the cookie identity.
and
It looks like with ClaimsPrincipals you can set a static Func<IEnumerable<ClaimsIdentity>, ClaimsIdentity> called PrimaryIdentitySelector which you can use in order to select the primary identity to work with.
To do this, create a static method with the signature:
static ClaimsIdentity MyPrimaryIdentitySelectorFunc(IEnumerable<ClaimsIdentity> identities)
This method will be used to go over the list of ClaimsIdentitys and select the one that you prefer.
Then, in your Global.asax.cs set this method as the PrimaryIdentitySelector, like so:
System.Security.Claims.ClaimsPrincipal.PrimaryIdentitySelector = MyPrimaryIdentitySelectorFunc;
My PrimaryIdentitySelector method ended up looking like this:
public static ClaimsIdentity PrimaryIdentitySelector(IEnumerable<ClaimsIdentity> identities)
{
//check for null (the default PIS also does this)
if (identities == null) throw new ArgumentNullException(nameof(identities));
//if there is only one, there is no need to check further
if (identities.Count() == 1) return identities.First();
//Prefer my cookie identity. I can recognize it by the IdentityProvider
//claim. This doesn't need to be a unique value, simply one that I know
//belongs to the cookie identity I created. AntiForgery will use this
//identity in the anti-CSRF check.
var primaryIdentity = identities.FirstOrDefault(identity => {
return identity.Claims.FirstOrDefault(c => {
return c.Type.Equals(StringConstants.ClaimTypes_IdentityProvider, StringComparison.Ordinal) &&
c.Value == StringConstants.Claim_IdentityProvider;
}) != null;
});
//if none found, default to the first identity
if (primaryIdentity == null) return identities.First();
return primaryIdentity;
}
[Edit]
Now, this turned out to not be enough, as the PrimaryIdentitySelector doesn't seem to run when there is only one Identity in the Identities list. This caused problems in the login page where sometimes the browser would pass a WindowsIdentity when loading the page but not pass it on the login request {exasperated sigh}. To solve this I ended up creating a ClaimsIdentity for the login page, then manually overwriting the the thread's Principal, as described in this SO question.
This creates a problem with Windows Authentication as OnAuthenticate will not send a 401 to request Windows Identity. To solve this you must sign out the Login identity. If the login fails, make sure to recreate the Login user. (You may also need to recreate a CSRF token)
I'm not sure if this will help, but this is how I've fixed this problem for me.
When I added Windows Authentication, it fluctuated between Windows and Claims identities. What I noticed is that GET requests get ClaimsIdentity but POST requests gets WindowsIdentity. It was very frustrating, and I've decided to debug and put a breakpoint to DefaultHttpContext.set_User. IISMiddleware was setting the User property, then I've noticed that it has an AutomaticAuthentication, default is true, that sets the User property. I changed that to false, so HttpContext.User became ClaimsPrincipal all the time, hurray.
Now my problem become how I can use Windows Authentication. Luckily, even if I set AutomaticAuthentication to false, IISMiddleware updates HttpContext.Features with the WindowsPrincipal, so var windowsUser = HttpContext.Features.Get<WindowsPrincipal>(); returns the Windows User on my SSO page.
Everything is working fine, and without a hitch, there are no fluctuations, no nothing. Forms base and Windows Authentication works together.
services.Configure<IISOptions>(opts =>
{
opts.AutomaticAuthentication = false;
});

C# Ask for Domain Admin credential and use them to perform some task

I need some help with examples how to use Credential of a current user running application.
So in windows 7 you can run application using user loged in by simply running application or you can use "Run as a different User" option and run it as another user.
In my Active Directory I have 2 account Domain User and one with Domain Admin rights. I'm login Windows as a Domain User and when I need I'm using "Run as a different User" to launch some task as a Domain Admin.
So the task is to get my Credential and use it to perform some task, lets say rename active directory user name.
Best way to do this as I can see is to ask user running application to enter Domain Admin credential on then start application and use them for various task. Of course I can easily run application with "Run as a different User" but I still need to get this credential and use them.
I've searched through the web and I can't find this, all i could find is using credential for a web auth.
If you can show me some examples how to:
1) Ask user for a Admin user credential ( i can leave without this )
2) Get and use credentials of a user running application
I don't want to know password I know I can't. Don't really want to add to a WPF form password box I prefer to use windows API to handle this i've already entered user name and password using "Run as a different User".
PS: I sorry if this topic exists :( I guess I'm bad at creating correct search requests.
ADDED: to be more clear what I need. In powershell it will look like this:
# This Asks user to enter credentials
$cred = Get-Credential;
# this checks if I have rights to use them.
Get-ADDomain “DOMAIN” –Server “Domain.com” –Credential $cred;
Of course it's simplified as hell though the point is that I can use credentials user entered when ever it's needed.
The equivalent C# to your Get-ADDomain is quite simple, it is just
public void PerformSomeActionAsAdmin(string adminUsername, string adminPassword)
{
//Null causes the constructor to connect to the current domain the machine is on.
// |
// V
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, null, adminUsername, adminPassword))
{
//do something here with ctx, the operations will be performed as whoever's username and password you passed in.
}
}
if you don't want to connect to the current domain and instead want to connect to Domain.com then replace the null with the appropriate string.
EDIT: if you want to use secure strings you can't use System.DirectoryServices.AccountManagement.PrincipalContext, you will need to go with the lower level calls in System.DirectoryServices.Protocols. Doing this process is quite complex, here is a link to the MSDN article "Introduction to System.DirectoryServices.Protocols (S.DS.P)" explaining how to use it. It is a big complex read and honestly I don't think it is worth it to be able to use encrypted strings.
public void PerformSomeActionAsAdmin(NetworkCredential adminCredential)
{
using(LdapConnection connection = new LdapConnection("fabrikam.com", adminCredential))
{
// MAGIC
}
}
Do you want to check if the current user is a doman admin? start by looking at his code, it should help you get started identifying what AD groups the current user is in. This will give you a list of strings that are each group's name the current user belongs to. Then you can check that list against whatever AD group you are trying to check for. Replace YourDomain with your domain name:
WindowsIdentity wi = WindowIdentity.GetCurrent();
List<string> result = new List<string>();
foreach (IdentityReference group in wi.Groups)
{
result.Add(group.Translate(typeof(NTAccount)).ToString().Replace("YourDomain\\", String.Empty));
}
Since i'm not quite sure what you're trying to do, this also might be helpful. You'd have to get the user name and password from a textobx, password box etc. This could be used for an "override" to use, for example, a manager's credentials etc. to do something the current user wasn't allowed to do because of AD group membership etc.
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "YourDomain"))
{
if (UserName.Contains("YourDomain\\"))
{
UserName = UserName.Replace("YourDomain\\", String.Empty);
}
//validate the credentials
bool IsValid = pc.ValidateCredentials(UserName, Password);
}

Windows identity foundation - sign out or update claims

I am using Windows Identity foundation to manage login to our site.
When a user logs in i am using some information in his request to put into the claims.
It is all working fine, but now I need to manage this scenario:
user is already logged in, athenticated and has a valid token.
But user decides to browses in again (via a redirect from another site)
So his information in his request is different.
I want to either
Sign him out - so that he naturally creates a new token with his new information
OR update his existing token.
So my question is:
How do i Sign out of Windows Identity foundation?
Or How do I update the existing claims?
I have tried this code:
public void ExpireClaims(HttpContextBase httpContextBase)
{
var module =
httpContextBase.ApplicationInstance.Modules["WSFederationAuthenticationModule"] as
WSFederationAuthenticationModule;
if (module == null)
{
return;
}
module.SignOut(true);
}
But module is alway null.
and i tried this:
public void FederatedSignOut(string replyUrl)
{
WSFederationAuthenticationModule.FederatedSignOut(null, new Uri(replyUrl));
}
But i get a null reference execption when i do this.
Thanks very much.
Essentially sign-out is just deleting the cookie so:
FormsAuthentication.SignOut
or
FederatedAuthentication.SessionAuthenticationModule.SignOut
or
FederatedAuthentication.SessionAuthenticationModule.DeleteSessionTokenCookie
will work.
Or use the FederatedPassiveSignInStatus (should be in your Toolbox). Set the property SignOutAction to FederatedSignOut and the control will clear out your STS session as well.

SPFile.CheckoutBy gives System/account instead of my login

Description: i am user user1 (which is also the user of the app pool of sharepoint, so when i logon with user user1 it says welcome system account).
In my code, i want to test if a file is checked out by user 1, so the result of the following:
file.CheckedOutBy.LoginName.ToLower() == userName.ToLower())
is always false (which is not correct), CheckOutby value is (Sharepoint system) while username value is (user1).
How to resolve this?
Im using SP2010
You shouldn't use the user account which is used as a app pool account, because You will always see system account. In this case the best way is to change the app pool account to another which won't be used for another purposes.
Where does username come from?
Try this:
SPWeb web = SPContext.Current.Web; //get it from somewhere
if(file.CheckedOutBy == web.EnsureUser(username)) {
//do something
}
That should do the comparison on the SPUser.Id
Thanks all, this is how i solved it:
file.CheckedOutBy.LoginName.ToLower() == web.CurrentUser.LoginName.ToLower()
giving sharepoint\system on both sides, which was corrected.

Categories

Resources