Active directory authentication using vpn in c# - c#

I am developing a web application that authenticate the user against an Active Directory Server. Now if I run my code from the development PC under the domain of that AD server, my code is running smoothly. We need to run the code from a totally different network using VPN and here the development PC is not into that AD. I am getting following error while trying to access the AD server.
The specified domain either does not exist or could not be contacted.
My VPN is working fine. I could access remote desktops using this VPN. I know a little tweak is required to solve the problem but could not find it. I went through following links but could not find any solution.
Domain Authentication from .NET Client over VPN
How do I get the Current User identity for a VPN user in a Windows forms app?
Following is my settings in web.config
<appSettings>
<add key="LDAPPath" value="LDAP://DC=MYSERVER,DC=COM" />
<add key="ADGroupName" value="Analyst"/>
</appSettings>
and here is my code
public class LdapAuthentication
{
private string _path;
private string _filterAttribute;
public LdapAuthentication()
{
_path = System.Configuration.ConfigurationManager.AppSettings["LDAPPath"].ToString();
}
public bool IsAuthenticated(string username, string pwd)
{
try
{
DirectoryEntry entry = new DirectoryEntry(_path, username, pwd);
entry.Path = _path;
entry.Username = username;
entry.Password = pwd;
// Bind to the native AdsObject to force authentication.
object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if (null == result)
{
return false;
}
// Update the new path to the user in the directory.
_path = result.Path;
_filterAttribute = (string)result.Properties["cn"][0];
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message);
}
return true;
}
}
Any help would be appreciated. Thank you.

I had a similar, though simpler problem. I had success in using the following code:
private bool DoLogin(string userName, string password)
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "DomainName.com")) {
bool isValid = pc.ValidateCredentials(userName, password);
if (isValid) {
// authenticated
...
return true;
}
else {
// invalid credentials
...
return false;
}
}
}
Using the ".com" at the end of the domain name was important to get it working for me. Without it I got the same symptoms you describe.

I've just been grappling with this for a couple of hours. No problems when on the network, lots of problems when connecting via VPN. It seems that when you are connecting over a VPN, the 'connection string' for DirectoryEntry has to be a lot more precise. I finally got it to work with an LDAP address/connection string like this:
LDAP://ip_of_primary_domain_controller/fully qualified path of the container object where the binding user is located
So for example something like this worked for me:
DirectoryEntry directoryEntry = new DirectoryEntry(
"LDAP://192.168.0.20/OU=Service Accounts,OU=Admin Accounts,DC=myserver,DC=com",
"username#myserver.com", "password");
... where "username#myserver.com" is located in OU=Service Accounts,OU=Admin Accounts,DC=myserver,DC=com. If you use SysInternals ADExplorer (or similar) to search for your username, it will tell you the correct fully qualified path for the container.
See here for a long answer about exactly whats should be in the 'connection string': https://serverfault.com/a/130556

Related

Trying to test LDAP-based authentication from forumsys?

I've not done any LDAP-based authentication before and also I've not worked with any LDAP server before. So I need a free online LDAP server to play with, I've found this https://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
However my code is not working (or the info there has become invalid, I'm not sure), the result of authen is always false, here is my code:
path = "ldap.forumsys.com:389/dc=example,dc=com";
using (var pc = new PrincipalContext(ContextType.Domain, null, path))
{
//this always returns false
var ok = pc.ValidateCredentials("read-only-admin", "password");
}
Could you make it work on your side? Or at least please assert that the info there is invalid, in that case if possible please give me some other info (from other free LDAP servers for testing).
I don't think the server is Active Directory. You can refer to this question for how to connect to a LDAP server in C#.
Second Edit:
Checked with MS people. They also suggest LdapConnection.
https://github.com/dotnet/corefx/issues/31809
Edit:
I can use DirectoryEntry to bind to the server. I am not sure why PrincipalContext does not work, but you can try this way.
Here is a sample code for validating user and password.
Tested on .Net Core 2.1, with System.DirectoryServices package 4.5.0.
using System;
using System.DirectoryServices;
namespace LDAPTest
{
class Program
{
static void Main(string[] args)
{
string ldapServer = "LDAP://ldap.forumsys.com:389/dc=example,dc=com";
string userName = "cn=read-only-admin,dc=example,dc=com";
string password = "password";
var directoryEntry = new DirectoryEntry(ldapServer, userName, password, AuthenticationTypes.ServerBind);
// Bind to server with admin. Real life should use a service user.
object obj = directoryEntry.NativeObject;
if (obj == null)
{
Console.WriteLine("Bind with admin failed!.");
Environment.Exit(1);
}
else
{
Console.WriteLine("Bind with admin succeeded!");
}
// Search for the user first.
DirectorySearcher searcher = new DirectorySearcher(directoryEntry);
searcher.Filter = "(uid=riemann)";
searcher.PropertiesToLoad.Add("*");
SearchResult rc = searcher.FindOne();
// First we should handle user not found.
// To simplify, skip it and try to bind to the user.
DirectoryEntry validator = new DirectoryEntry(ldapServer, "uid=riemann,dc=example,dc=com", password, AuthenticationTypes.ServerBind);
if (validator.NativeObject.Equals(null))
{
Console.WriteLine("Cannot bind to user!");
}
else
{
Console.WriteLine("Bind with user succeeded!");
}
}
}
}
Reference:
https://www.c-sharpcorner.com/forums/ldap-authentication2
I figure it out too, and having no LDAP knowledge I´ve come up with this.
The problem in your solution may be first, you are using "ldap://" instead of "LDAP://", since it was something I came into when coding this. But I use System.DirectoryServices library.
I tested against this magnificent free to test LDAP server
var path = "LDAP://ldap.forumsys.com:389/dc=example,dc=com";
var user = $#"uid={username},dc=example,dc=com";
var pass = "password";
var directoryEntry = new DirectoryEntry(path, user, pass, AuthenticationTypes.None);
var searcher = new DirectorySearcher(directoryEntry);
searcher.PropertiesToLoad.Add("*");
var searchResult = searcher.FindOne();
I don´t understand exactly what all of this lines does, however, and lookign for a solution I found some recommendations.
on the path the "LDAP://" string should be on block mayus.
in the user, sometimes you need to use "cn=username-admin" for validating admins, be sure to also set Authentication type to ServerBind.
It seems as if read-only-admin is not a valid user. Try replacing:
var ok = pc.ValidateCredentials("read-only-admin", "password");
with
var ok = pc.ValidateCredentials("tesla", "password");
If that does not work, the other other issue would be on the LDAP's server side.
A good option regardless is to set up an Amazon Web Services EC2 server (it is free) and load Windows Server onto it. This gives you your own server and you learn how to set up an LDAP server (which is pretty easy).

Login Validation Against Active Directory C# - Visual Studio for Mac

I'm trying to validate credentials in Active Directory for an MVC Web App (C#) I'm writing on Visual Studio for Mac. In searching for answers, I've noticed a lot of NotImplementedExceptions and other strange occurrences.
Here is a (non-comprehensive) list of things I've tried and things that have failed.
First, this code:
string domainAndUsername = domain + #"\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
try
{
//Bind to the native AdsObject to force authentication.
// This line throws a not implemented exception
object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry)
{
Filter = "(SAMAccountName=" + username + ")"
};
search.PropertiesToLoad.Add("cn");
Console.WriteLine(search.ToString());
SearchResult result = search.FindOne();
//Debug.WriteLine(result.ToString());
if (null == result)
{
return false;
}
//Update the new path to the user in the directory.
_path = result.Path;
_filterAttribute = (string)result.Properties["cn"][0];
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message);
}
return true;
The line object obj = entry.NativeObject throws a NotImplementedException I've tried commenting out the line (since obj is never used elsewhere) but to no avail. I have tried other variations of very similar code as well.
The other route I attempted was this code:
var creds = new NetworkCredential(username, password);
var srvid = new LdapDirectoryIdentifier(adPath);
// This line throws a NotImplementedException
var conn = new LdapConnection(srvid, creds);
try
{
conn.Bind();
}
catch (Exception)
{
return false;
}
conn.Dispose();
return true;
And other variations of the same idea. The line var conn = new LdapConnection(srvid, creds); throws a NotImplementedException
Finally, I went a simpler route and used
Membership.ValidateUser(model.username, model.password)
Since this is a Web Api and I am using a controller. This requires some code in the Web.config available here. It, too, throws a NotImplementedException.
So do these three common methods all rely on the same underlying function that hasn't been implemented in VS for Mac yet? Or is there something I'm missing? Also if there is any workaround someone could offer, it would be very well appreciated.
Thanks!
First of all you need to reference Microsoft.DirectoryServices.dll
System.DirectoryServices
Second, you need to run this queries with a domain account with grant access. Take care when you publish in server (iis) or other because the service run this code and it had to have permission on it.
check this
Sorry for my English :D
Thanks to the kind efforts of Javier Jimenez Matilla, bnem, and Lexi Li, I got it to work. Part of the issue was a configuration problem that I unfortunately can't disclose (confidentiality and whatnot), but here's the final code:
using Novell.Directory.Ldap;
try
{
var conn = new LdapConnection();
conn.Connect(domain, 389);
conn.Bind(LdapConnection.Ldap_V3, $"{subDomain}\\{username}", password);
return true;
}
catch (LdapException ex)
{
Console.WriteLine(ex.Message);
return false;
}
A few things to note. First, that LdapConnection is actually Novell.Directory.Ldap.LdapConnection. Secondly, the hard-coded 389 is the port which LDAP likes to communicate over. Thirdly, the parameter $"{subDomain}\\{username}" is the way the username appears to AD. For my particular case, the domain is of the form "corporation.mycompany.com", and in AD the username appears corporation\username. So in this example, string subDomain = "corporation"

How to connect to Active Directory via LDAPS in C#?

Found a documentation (here) in an answer thread on this site but i can´t get an connection to an AD. When i use a program like Active Directory Explorer i can connect. I think, because i am trying to connect to a LDAPS i need a different approach?
I have the server IP, a domain, username/pwd and the port 636.
I tried various combinations # new DirectoryEntry but couldn´t get it to connect. Always get a COMException Domain is not existing .
static DirectoryEntry createDirectoryEntry()
{
DirectoryEntry ldapConnection = new DirectoryEntry("LDAP://192.168.2.59", USER, PWD);
ldapConnection.AuthenticationType = AuthenticationTypes.SecureSocketsLayer;
return ldapConnection;
}
Background Infos:
User places his card to a Card Reader Unit. Porgram gets ID from card and searches the DB for this ID and returns the eMail address belonging to the ID/User
.
And here the working solution:
private string getEmail(string userID)
{
try
{
string ldapfilter = "(&(otherPager=" + userID + "))";
DirectoryEntry myLdapConnection = new DirectoryEntry("LDAP://" + SERVER, USER, PWD);
DirectorySearcher search = new DirectorySearcher(myLdapConnection);
search.Filter = ldapfilter;
/*search.PropertiesToLoad.Add("mail");
SearchResult result = search.FindOne();*/
string[] requiredValue = new String[] { "mail" };
foreach (String value in requiredValue)
search.PropertiesToLoad.Add(value);
SearchResult result = search.FindOne();
if (result != null)
{
foreach (String value in requiredValue)
foreach (Object myCollection in result.Properties[value])
{
return myCollection.ToString();
}
}
else
{
return "No Entry fround";
}
}
catch (Exception e)
{
Console.WriteLine("Exception Problem: " + e.ToString());
return null;
}
return null;
}
private void cmdClose_Click(object sender, EventArgs e)
{
Close();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
label1.Text = getEmail(textBox1.Text);
}
You need to specify the port, since 636 is the default LDAPS port.
new DirectoryEntry("LDAP://192.168.2.59:636", USER, PWD)
I do this in some of my code, and using "LDAP://" (not "LDAPS://") is what works.
If that doesn't work, then there may be a certificate error. You can test this with a browser. If you use Chrome, open Chrome with this (so it lets you use port 636):
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --explicitly-allowed-ports=636
Then go to https://192.168.2.59:636. If you get a big fancy certificate error, then the problem is that the certificate is not trusted. View the certificate from Chrome and see what the problem is. It could be issued by an authority that is not in the Windows cert store.

Can not access Active Directory domain controller from remote server

I am very new to the LDAP and Active Directory integration portion. Although I successfully configured my local machine to access Active Directory domain controller, when I deployed it on one of our server neither through IP address of the domain nor through domain name it was accessible.
My website is in ASP.NET C#.
It was getting this error:
The specified domain either does not exist or could not be contacted.
My method written to access AD is here:
private SearchResultCollection sResults { get; set; }
sResults = null;
public void SearchByUsername(string username)
{
try
{
// initiate a directory entry
private const string ldapPath = "LDAP://192.168.0.190/OU=Domain Users,DC=mydomain,DC=net"
dEntry = new DirectoryEntry(ldapPath);
dSearcher = new DirectorySearcher(dEntry);
dSearcher.Filter = "(&(objectClass=user)(sAMAccountname= " + username + "))";
performSearch();
getValues();
}
catch (Exception)
{
throw;
}
}
private void performSearch()
{
// perform search in Active Directory
sResults = dSearcher.FindAll();
}
private void getValues()
{
// loop through results of search
foreach (SearchResult sResult in sResults)
{
Employee emp = new Employee();
emp.CN = getProperty(sResult, "cn");
emp.FirstName = getProperty(sResult, "givenName");
emp.LastName = getProperty(sResult, "sn");
emp.Username = getProperty(sResult, "sAMAccountname");
emp.Email = getProperty(sResult, "mail");
Employees.Add(emp);
}
}
Above method works very well on my local machine both with IP address and domain name. Server where I am trying this to work is Windows Server 2012 R2.
I ran command nltest /dclist:mydomain.net and made sure that the server is within the domain as it returned me details. i.e. DC name, IP address, domain name.
Is there any syntactical issue I have run into? OR is it related to configuration issue like DNS ?
Also, like to mention as I tried searching about this on www.serverfault.com but couldn't gather much details.
Please suggest me direction.
Finally things seemed working out.
Earlier I mentioned that I made sure that the machine I am working on is in the domain but things were slightly different there. The machine I am trying to make things work is not actually in the domain but in the workgroup.
Below two Netdom commands helped me identifying this situation.
netdom verify - with this command it is found that machine hasn't joined domain.
netdom join - with this command, was able to join machine to network domain.
Reference: https://technet.microsoft.com/en-us/library/cc772217.aspx
Once it is in domain, Active Directory became immediately accessible.

How to check if Windows user account name exists in domain?

What is the simplest and most efficient way in C# to check if a Windows user account name exists? This is in a domain environment.
Input: user name in [domain]/[user] format (e.g. "mycompany\bob")
Output: True if the user name exists, false if not.
I did find this article but the examples there are related to authenticating and manipulating user accounts, and they assume you already have a user distinguished name, whereas I am starting with the user account name.
I'm sure I can figure this out using AD, but before I do so I was wondering if there is a simple higher level API that does what I need.
* UPDATE *
There are probably many ways to do this, Russ posted one that could work but I couldn't figure out how to tweak it to work in my environment. I did find a different approach, using the WinNT provider that did the job for me:
public static bool UserInDomain(string username, string domain)
{
string path = String.Format("WinNT://{0}/{1},user", domain, username);
try
{
DirectoryEntry.Exists(path);
return true;
}
catch (Exception)
{
// For WinNT provider DirectoryEntry.Exists throws an exception
// instead of returning false so we need to trap it.
return false;
}
}
P.S.
For those who aren't familiar with the API used above: you need to add a reference to System.DirectoryServices to use it.
The link I found that helped me with this: How Can I Get User Information Using ADSI
The examples use ADSI but can be applied to .NET DirectoryServices as well. They also demonstrate other properties of the user object that may be useful.
The System.DirectoryServices namespace in the article is exactly what you need and intended for this purpose. If I recall correctly, it is a wrapper around the Active Directory Server Interfaces COM interfaces
EDIT:
Something like the following should do it (it could probably do with some checking and handling). It will use the domain of the current security context to find a domain controller, but this could easily be amended to pass in a named server.
public bool UserInDomain(string username, string domain)
{
string LDAPString = string.Empty;
string[] domainComponents = domain.Split('.');
StringBuilder builder = new StringBuilder();
for (int i = 0; i < domainComponents.Length; i++)
{
builder.AppendFormat(",dc={0}", domainComponents[i]);
}
if (builder.Length > 0)
LDAPString = builder.ToString(1, builder.Length - 1);
DirectoryEntry entry = new DirectoryEntry("LDAP://" + LDAPString);
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "sAMAccountName=" + username;
SearchResult result = searcher.FindOne();
return result != null;
}
and tested with the following
Console.WriteLine(UserInDomain("username","MyDomain.com").ToString());
Found a simple way to do this if you're on a high enough framework version:
using System.DirectoryServices.AccountManagement;
bool UserExists(string userName, string domain) {
using (var pc = new PrincipalContext(ContextType.Domain, domain))
using (var p = Principal.FindByIdentity(pc, IdentityType.SamAccountName, userName)) {
return p != null;
}
}

Categories

Resources