I am using c# code in MVC web app to validate a user and find the list of user groups that particular user belongs to and using the below code
try
{
List<string> user_groups= new List<string>(); //save the groups the user belongs to
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "mydomain.com",model.Email,model.Password))
{
bool valid=ctx.ValidateCredentials(model.Email, model.Password); //validate the user
if(valid)
{
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, model.Email);
if (user != null)
{
// get the user's groups
var groups = user.GetAuthorizationGroups();
foreach (GroupPrincipal group in groups)
{
// save those groups to session for further processing after login
if ((bool)group.IsSecurityGroup)
{
user_groups.Add(group.Name);
}
}
}
_groups = string.Join(",", user_groups);
ViewBag.Message = _groups;
}
else
{
ViewBag.Message = "Error while validating";
}
ctx.Dispose();
}
}
catch (PrincipalServerDownException)
{
//If server is down or some exception happends ,
// ad_verification = false;
ViewBag.Message = "Error at groups fetching as server is down ";
}
catch (Exception ex)
{
ViewBag.Message = "Error at groups fetching as "+ex.Message;
}
I deployed this to server and try to login as user1 and all went well The code validate the user credentials and returned the list of user groups the user1 belongs to
Now i logged in as user2 on server , then it returned the below error
Error at groups fetching as Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again.
It looks like i can still login as user1, but for all other users the error is same as above . While testing on local IIS no such problems
Any known reasons why the above lines is breaking for second user onwards and any suggestions to resolve this
You didn't say which line is throwing the exception, but it might not like that you're using the user's credentials to pull all the data.
If you're running this from a computer that is joined to the same domain (or a trusted domain) then you don't need to put the credentials here:
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "mydomain.com"))
The ValidateCredentials() line will validate the credentials, and everything else will be done with the credentials that the application is running under.
On a side note, you don't need to call ctx.Dispose() when ctx is the subject of your using block. The whole purpose of using is that it will call Dispose() after it leaves the using block. Take a look at the documentation.
Related
I'm trying to connect to SharePoint online in a console App and print the title of the site.
Its giving me the error : "The sign-in name or password does not match one in the Microsoft account system."
I have checked and made sure the username and password are 100% right.
I dont know what else to check
Heres my code:
private static void SPCredentialsConnect()
{
const string SiteUrl = "https://tenant.sharepoint.com/sites/mysite";
const string pwd = "appPassword";
const string username = "username#tenant.onmicrosoft.com";
SecureString securestring = new SecureString();
pwd.ToCharArray().ToList().ForEach(s => securestring.AppendChar(s));
ClientContext context = new ClientContext(SiteUrl);
context.Credentials = new SharePointOnlineCredentials(username, securestring);
try
{
var web = context.Web;
context.Load(web);
context.ExecuteQuery();
Console.WriteLine($"web title: {web.Title}");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Have your issue fixed? “The sign-in name or password does not match one in the Microsoft account system” Error will occur sometimes and fixed after a while with nothing changed.
AppOnly Authentication for sharepointonline can't be registed in Azure Active Directory.
It should be register in
https://contoso.sharepoint.com/_layouts/15/appregnew.aspx
And grant permission in
https://contoso-admin.sharepoint.com/_layouts/15/appinv.aspx
You can refer to following document
https://learn.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azureacs
Consider using the PnP.Framework (a NuGet package), and use the AuthenticationManager object for SPO sites. This method bypasses MFA (which is mandatory in our organization, FWIW). You can find a lot more information and examples here, including steps on getting the client id and client secret for a site. Here is what we use to log into SPO sites:
using (ClientContext context =
new AuthenticationManager().GetACSAppOnlyContext(SiteUrl, clientID, clientSecret))
{
...
}
Also, once you connect, you should adjust the Context.Load to grab the title if you want to use that value right away. Here's what I used in my code:
context.Load(web, p => p.Id, p => p.Title);
context.ExecuteQuery();
Console.WriteLine($"Logged into source {web.Title} ({web.Id})");
Good luck!
Steve in Spain
I'm writing a program (WinForms, C#) that runs on a Win 7 client machine. It obtains credentials from the user (user id, password, and subdomain name) and uses them to authenticate (via Active Directory) to other servers to which the program remotely connects. The other servers are on a domain different than the domain the Win 7 client machine is on.
Using the NetworkCredential, LdapDirectoryIdentifier, and LdapConnection classes, I can test the credentials with no more than the user id, password, and subdomain name (See answer for S.O. Why does Active Directory validate last password?).
For example, for the user account ssmith#xyz.gov, I need only provide ssmith (user id), the password for ssmith, and xyz (the subdomain). I don't need to provide the top-level domain name (gov in this case).
Now, I want to programmatically obtain the top-level domain name for this user account (gov in this case). I've examined the properties and methods of the NetworkCredential, LdapDirectoryIdentifier, and LdapConnection classes. I've looked over the other classes in the System.DirectoryServices.Protocols Namespace. I don't see a way to programmatically obtain the top-level domain name.
Given user id, password, and subdomain name, how can I obtain the top-level domain name for a user account?
Here is my code. Given a user account of ssmith#xyz.gov, My call looks like this (asterisks represent a SecureString password)
bool result = ValidateCredentials("ssmith","******", "xyz");
Here is my code for the method.
private const int ERROR_LOGON_FAILURE = 0x31;
private bool ValidateCredentials(string username, SecureString ssPassword, string domain)
{
//suports secure string
NetworkCredential credentials = new NetworkCredential(username, ssPassword, domain);
LdapDirectoryIdentifier id = new LdapDirectoryIdentifier(domain);
using (LdapConnection connection = new LdapConnection(id, credentials, AuthType.Kerberos))
{
connection.SessionOptions.Sealing = true;
connection.SessionOptions.Signing = true;
try
{
// The only way to test credentials on a LDAP connection seems to be to attempt a
// Bind operation, which will throw an exception if the credentials are bad
connection.Bind();
}
catch (LdapException lEx)
{
credentials = null;
id = null;
if (ERROR_LOGON_FAILURE == lEx.ErrorCode)
{
return false;
}
throw;
}
}
credentials = null;
id = null;
return true;
}
After a successful bind, then the full DNS name of the domain will be in the LdapConnection object:
var domain = connection.SessionOptions.DomainName;
In this case, that would be "xyz.gov". If you need just "gov", then you can just take everything after the last dot:
var tld = domain.Substring(domain.LastIndexOf('.') + 1);
I'm trying to authenticate users in an application using a domain controller with code like this:
PrincipalContext pcon = new PrincipalContext(ContextType.Domain, domain);
password_ok = pcon.ValidateCredentials(username, password);
That works fine as long as the domain server is online but if there is no domain server the previous code fails with a PrincipalServerDownException exception.
What I'm trying to do is to make my application use the windows cached credentials if the server is down just like Windows do: you may login into windows with a domain user even if the domain server is down.
Can I do that in C#?
Thank you very much in advance.
For posterity, this is how I solved my problem:
bool dmainNotAvailable = false;
PrincipalContext pcon = null;
try
{
pcon = new PrincipalContext(ContextType.Domain, domain);
}
catch (System.DirectoryServices.AccountManagement.PrincipalServerDownException ex)
{
domainNotAvailable = true;
try
{
pcon = new PrincipalContext(ContextType.Machine, Environment.MachineName);
}
catch (Exception ex2)
{
throw new Exception(ex2);
}
}
string realUserName = !domainNotAvailable ? username : $"{domain}\\{username}";
passwordOk = pcon.ValidateCredentials(realUserName, password);
This will also work:
string userName = $"{domain}\\{user}";
PrincipalContext pcon = new PrincipalContext(ContextType.Machine, Environment.MachineName);
passworkOk = pcon.ValidateCredentials(userName, password);
The second solution will try lo login using the cached credentials and if there is no info then will try to login using the domain controller.
On both solutions the domain user must be added first to the local computer in order to have the credentials cached.
That's all.
I have attempted this with not much success. Basically I need to login to Exchange using EWS remotely.
The issue is I don't know if the user has logged in OK or if the credentials are wrong as I get nothing back! If I provide wrong credentials the software just carries on!
Is there something I'm missing, I've checked the MSDN stuff about EWS which shows you how to connect to exchange but nothing about validating credentials!
Below is the code I currently have to connect.
public void connect(string Email, string Password)
{
try
{
_useremail = Email;
_userpass = Password;
// Define the credentials to use.
var credentials = new WebCredentials(_useremail, _userpass);
_ExchangeServer = new ExchangeService(ExchangeVersion.Exchange2010_SP1);
_ExchangeServer.Credentials = credentials;
_ExchangeServer.Url = new Uri(_ExchangeEWSURL);
_ExchangeServer.Timeout = 60;
_ExchangeServer.KeepAlive = true;
_ExchangeConnected = true;
}
catch (Exception ex)
{
_ExchangeConnected = false;
throw ex;
}
}
as you can see at present I just set a bool value to true in the class. Any ideas?
In order to check whether the given credentials are valid, you must query resources you expect the user to have access to (calendar, inbox, contacts, etc.). There is no explicit login method - the authentication occurs implicitly when you request user resources (via FindItems, FindFolders, FindAppointments, etc.).
Hi i want to create a system user in window server 2003 active directory for which i use following C# code ,
DirectoryEntry AD = new DirectoryEntry("WinNT://"+Environment.MachineName+",computer");
DirectoryEntry NewUser = AD.Children.Add(username, "user");
NewUser.Invoke("SetPassword", new object[] { password });
NewUser.Invoke("Put", new object[] { "Description", "Test User from .NET"});
NewUser.CommitChanges();
DirectoryEntry grp;
grp = AD.Children.Find("Guests", "group");
if (grp != null)
{
grp.Invoke("Add", new object[] { NewUser.Path.ToString() });
}
this code makes user when i run this application locally on visualy studio web engine but when i deploy this application on iis on window server 2003 this will give exception
Exception:
General Access Denied Error
kindly help me what im doing wrong which permission is required to create user in window server and how we give this perssion. thanks.
Look at System.DirectoryServices.AccountManagement namespace. Utilizing classes from it you can do this in such way:
using (var context = new PrincipalContext(ContextType.Machine, "MachineName", "AdminUserName", "AdminPassword"))
{
UserPrincipal user = new UserPrincipal(context, username, password, true);
user.Description = "Test User from .NET";
user.Save();
var guestGroup = GroupPrincipal.FindByIdentity(context, "Guests");
if (guestGroup != null)
{
guestGroup.Members.Add(user);
guestGroup.Save();
}
}
Also, you may configure impersonation on your application and skip all parameters in the PrincipalContext constructor exception the first one if this functionality allowed for administrators only.