3

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.

asdf
  • 721
  • 2
  • 8
  • 24

1 Answers1

3

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.

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • Thank you @GabrielLuci. For some reason I couldn't make the LDAP SID binding syntax work. I have tried with and without domain. Nevertheless, using SID is IMHO much better than using name, great suggestion. Finally I ended up just getting all users with `DirectorySearcher` and manually filtering on their `objectsid` property (which you need to convert to string like this: `new SecurityIdentifier(searchResult.Properties["objectsid"][0] as byte[], 0).ToString()` btw.) to find the matching `SearchResult`. – asdf May 03 '20 at 18:14
  • That's strange. There's no reason the SID syntax shouldn't work. I would be curious to see your code and what the error message was. – Gabriel Luci May 03 '20 at 21:49
  • I must have messed up somehow because now it's working :D, sorry, my mistake. One thing that confused me though is the `Username` property of the `DirectoryEntry` being `null`. The data in `Properties` is there, so that's fine. Thank you once again. – asdf May 04 '20 at 07:49
  • The [`Username`](https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices.directoryentry.username) property of `DirectoryEntry` is used to specify the credentials to use to authenticate to the server. When it's `null` (the default), it just uses the credentials of the running process. – Gabriel Luci May 04 '20 at 12:11