My scenario:
A client app (Net Core WPF) should somehow find out the current user's identity (for example using System.Security.Principal.WindowsIdentity.GetCurrent()) and authenticate with a REST server application (Net Core) which has access to AD (it knows the address, name and password of root AD DirectoryEntry). The authentication should be successful if and only if the user from the client app is found among users in AD. This is an intranet setup btw.
Solutions to similar questions here on SO (for example How to get the current user's Active Directory details in C#) generally propose using DirectorySearcher and filtering on user name "(sAMAccountName=theUserIWantToMatch)".
But IMHO this is not sufficient:
1) It is not secure enough, you can easily impersonate anybody just by creating a user with a similar name. Not to mention man-in-the-middle attacks.
2) It needn't even be malicious, plenty of people have similar names. I might have connected to the intranet network via VPN using a computer with a similar user name (similar to somebody else already on that network).
Can you think of a better way to match the users (using some GUID or token for example) or completely different authentication method? Just to reiterate: I can't use usual ASP.NET windows auth because my client is a WPF app that communicates with the server using HttpClient instance.
Thank you.
A fail-proof way of getting the exact user that's logged in is by using the SID, which is available from WindowsIdentity.GetCurrent().User.
From there, you can bind directly to the AD object using the LDAP SID binding syntax of LDAP://<SID=XXXXX>.
That will look something like this:
var sid = WindowsIdentity.GetCurrent().User;
var currentUser = new DirectoryEntry($"LDAP://<SID={sid}>");
If the computer you're running this from is not joined to the same domain as the user (or trusted domain), then you will need to include the domain name in the LDAP path:
var currentUser = new DirectoryEntry($"LDAP://example.com/<SID={sid}>");
This method is also faster than any other method, since you're not performing a search and then binding to the object. It's all done in one network request.
Related
I have a requirement to get the AD samAccountName in an MVC C# application that's deployed to an Azure Web App.
The app is Windows Authenticated against Azure AD which is synced with our local on premise AD servers using ADConnect.
When we run the Web App locally (Visual Studio 2017), the value that's returned from:
User.Identity.Name
is returned in the format DOMAIN\UserName
But when looking at the WebApp in Azure, the same code returns it in the format firstname.lastname#domain.co.uk
I appreciate that we won't be able to use User.Identity.Name to achieve a consistent result, but we do need a way of getting this information when the site is running Azure.
We've looked various ways of achieving this using Claims Descriptions and Extended Properties but have had no luck so far.
I've tried to give as much information as possible, but I'm working in conjunction with our infrastructure team so may not have provided enough, please let me know if more info is required.
The "firstname.lastname#domain.co.uk" format would be the userPrincipalName attribute of the account. So if you see an "#" in the name, you can connect to AD and search for the account and pull the sAMAccountName. Something like this:
var searcher =
new DirectorySearcher(
new DirectoryEntry("LDAP://domain.co.uk"),
$"(&(objectClass=user)(userPrincipalName={User.Identity.Name}))");
searcher.PropertiesToLoad.Add("sAMAccountName");
var result = searcher.FindOne();
var sAmAccountName = result.Properties["sAMAccountName"][0] as string;
I'm working on a web application based on ASP.NET Core MVC 2.1. It provides the ability to execute several Active Directory related operations. One of them is a web based LAPS client. To communicate with AD, I'm using System.DirectoryServices from Microsoft.Windows.Compatibility.
Since LAPS stores its data in a computer objects AD attributes (ms-Mcs-AdmPwd), I need to query this attribute, e.g. like this:
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, targetDomain)) {
ComputerPrincipal computer = ComputerPrincipal.FindByIdentity(principalContext, IdentityType.Name, computerName);
string password = (computer.GetUnderlyingObject() as DirectoryEntry).Properties["ms-Mcs-AdmPwd"].Value.ToString();
}
So my problem is: I need to do this in context of the authenticated user, because the attribute security permissions already control access to the LAPS passwords. I implemented cookie authentication without Identity querying PrincipalContext.ValidateCredentials() against AD in order to authenticate users. What would be the best way to achieve this without asking the user for login data a second time?
There is a PrincipalContext constructor PrincipalContext(ContextType contextType, string name, string userName, string password), but this would require a way to store the password for each session. Storing the password in the session itself would be a bad idea, I guess.
Furthermore, I could just query the data on server side as ApplicationPoolIdentity or as a dedicated service account (which would then need full access to the relevant attributes of all computer objects) and then implement some logic to determine, if the logged in user is permitted to access this specific password. But this would result in unneccessary effort, since all authorization information already exist as AD DACLs.
I hope this makes sense somehow. So if anyone can push me in the right direction, I would be very grateful. Thanks in advance!
After all, I went with a mapping approach in order to determine, if a user is allowed to access data. If the user is member of a certain AD group containing the admins of a certain department and the target computer object is located in the corresponding OU, the password will be queried and subsequently displayed. ApplicationPoolIdentity executes the actual AD query. Works flawlessly so far.
I have a simple wpf client (few text boxes) that uploads some data to a web service. And I want to use windows authentication to go with my application.
I am checking in OnStartup of App.xaml, whether or not the user is authenticated. My question is around what is the meaning of Thread.CurrentPrincipal.Identity.IsAuthenticated.
I don't want my application to be used from outside my network as it is connecting to a web service and uploads data. But my assumption is as long as you run this application from inside any windows network the above mentioned property will always return true?
So how do I find out if the application is being run from inside my network. I don't think checking domain name or role name is any different, because I can always setup a domain and name it whatever I want. I don't want to prompt user for username or password of any sort.
How do you check Identity of user against a particular AD (AD might not be publically available). Basically the application should only works from my local network or through VPN.
var context = new PrincipalContext(ContextType.Domain, "DOMAINNAME");
var result = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, userName);
If the result is null, then the user does not exists in the AD domain.
You can also user DirectorySearcher class to query AD based on a filter criteria. This is more useful only if you would like to retrieve additional details about the user like contact, email address etc.
I have a login form written in C# and I want only AD users to be able to login.
How should I do this?
string UserName = "";
string Pass = "";
Although it is not an ASP.Net app the active directory membership provider will work just fine.
Here is info on how to use this library:
http://msdn.microsoft.com/en-us/library/system.web.security.activedirectorymembershipprovider.aspx
and here is some more information:
http://msdn.microsoft.com/en-us/library/ff650308.aspx
I am sure that this is not a best practice, but, depending on your security needs, you could allow all domain users and exclude local users by checking just the UserDomainName in the Form_Load. This simple approach piggybacks on their computer login, and does not have the complexity of any LDAP/AD calls.
if (SystemInformation.UserDomainName.ToString() == "myDomain")
{
// your normal form load code here
}
else
{
form1.Close(); //this is a simple but effective to pull the rug out from
//under them if they do not have the permissions
//TODO email the application administrator the `SystemInformation.UserName` of the user who was not given permissions
}
In my environment, since our in-house apps are deployed via ClickOnce (installed per user per computer), a similar approach (we compare usernames too) has always been sufficient for us.
If you want to know how to verify credentials to Active Directory in order to allow AD users in you application, you should check this.
You'll find how to verify the content of your textboxes and verify if username and passowrd matches (directly with the AD).
Although I am currently developing this WinForms application on our Sharepoint server I intend for the finished program to function from any computer on the Domain. I'm using the WSS web services to get all the information I use from Sharepoint.
I have written some code which will check Sharepoint Permission masks, with logical OR against mask, for all the permissions it covers but I am having trouble returning the Sharepoint mask for the current user. I would like users to be able to log right in through Windows Authentication so this was my immediate idea.
NetworkCredential credentails = CredentialCache.DefaultNetworkCredentials;
var userInfo = userGroupService.GetUserInfo(credentails.UserName);
However although I am able to return the permission collection for the entire Sharepoint site with DefaultNetworkCredentials (as in bellow snippet) the properties are empty strings, so I can't use it to get the UserName.
permissionService.Credentials = CredentialCache.DefaultNetworkCredentials;
permissionService.Url = "http://localhost/mySite/_vti_bin/Permissions.asmx";
// Web service request works
XmlNode node = permissionService.GetPermissionCollection(siteName, "Web");
// But I need to identify current user from this collection somehow still
I read that Windows Authentication suffers from a double-hop issue, which I want to avoid, but as I am developing on the server Sharepoint & IIS are running, I can't see this causing an immediate issue.
Is there a way around this or a better way to get the current users permission mask?
If the current user for wss will always be the same as the user currently logged on to the pc
var userInfo = userGroupService.GetUserInfo(Environment.UserDomainName +#"\"+ Environment.UserName);
or to get the permissions for the currently logged on user
XmlNode currentUserPermission = userGroupService.GetRolesAndPermissionsForCurrentUser();
You are dealing with an issue where the authentication cannot move beyond one remote host; this is known as the "one-hop" limitation.
To overcome this, you have to get into "Constrained Delegation," where a computer/account are expressly authorized to receive and accept authentication credentials from another computer/account. This is set up in Active Directory by defining the appropriate Service Principal Names (SPN's) on either "end" of the delegation.
You can get more details about Constrained Delegation here.
Good luck! CD can be a bit of a pain to set up, so tread carefully.