I have a WCF service which contains a Login
method that validates a username and password against the local machine credentials, and after a seemingly random period of time it will stop working for some users.
The actual login command looks like this:
public UserModel Login(string username, string password, string ipAddress)
{
// Verify valid parameters
if (username == null || password == null)
return null;
try
{
using (var pContext = new PrincipalContext(ContextType.Machine))
{
// Authenticate against local machine
if (pContext.ValidateCredentials(username, password))
{
// Authenticate user against database
using (var context = new MyEntities(Connections.GetConnectionString()))
{
var user = (from u in context.Users
where u.LoginName.ToUpper() == username.ToUpper()
&& u.IsActive == true
&& (string.IsNullOrEmpty(u.AllowedIpAddresses)
|| u.AllowedIpAddresses.Contains(ipAddress))
select u).FirstOrDefault();
// If user failed to authenticate against database
if (user == null)
return null;
// Map entity object to return object and assign session token
var userModel = Mapper.Map<User, UserModel>(user);
userModel.Token = Guid.NewGuid();
userModel.LastActivity = DateTime.Now;
// Authenticated users are added to a list on the server
// and their login expires after 20 minutes of inactivity
authenticatedUsers.Add(userModel);
sessionTimer.Start();
// User successfully authenticated, so return UserModel to client
return userModel;
}
}
}
}
catch(Exception ex)
{
// This is getting hit
MessageLog.WriteMessage(string.Format("Exception occurred while validating user: {0}\n{1}", ex.Message, ex.StackTrace));
return null;
}
// If authentication against local machine failed, return null
return null;
}
This appears to work fine for a few days, then it will abruptly stop working for some users and throw this exception:
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. (Exception from HRESULT: 0x800704C3)
at System.DirectoryServices.AccountManagement.CredentialValidator.BindSam(String target, String userName, String password)
at System.DirectoryServices.AccountManagement.CredentialValidator.Validate(String userName, String password)
at System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials(String userName, String password)
at MyNamespace.LoginService.Login(String username, String password, String ipAddress) in C:\Users\me\Desktop\somefolder\LoginService.svc.cs:line 67
Line 67 is: if (pContext.ValidateCredentials(username, password))
I'm not sure if it matters or not, but the final line of the error message is the path of the VS solution on my development machine, not the path to the files on the production server.
When it fails, it only fails for some users, while others can continue to login just fine. The only thing I have found to temporarily fix the error is running iisreset
. Stopping/starting the web site or recycling the app pool doesn't work.
I am unable to reproduce the error on demand. I've tried logging in with the same user from multiple sessions and IP addresses, logging in with different users from the same browser session at the same time, spamming the Login button to try and get it to run multiple times, etc but everything appears to work fine.
I can see from our logging that users have been successfully able to login in the past:
3/21/2013 o 9:03a I logged in o 1:54p UserB logged in o 1:55p UserA logged in o 2:38p UserB logged in o 3:48p UserB logged in o 5:18p UserA logged in o 6:11p UserB logged in 3/22/2013 o 12:42p UserA logged in o 5:22p UserB logged in o 8:04p UserB logged in 3/25/2013 (today) o 8:47a I logged in o 12:38p UserB tries logging in and fails. Repeated ~15 times over next 45 min o 1:58p I login successfully o 2:08p I try to login with UserB's login info and fail with the same error
The reason we authenticate against the local machine is because users have an account created locally for FTP access, and we didn't want to build our own custom login system or make our users remember two sets of credentials.
The code should only authenticate the user's credentials, and does not do do anything else with the user's credentials. There is no other code that uses System.DirectoryServices
, no file IO going on, and no access to anything locally on the file system other than the files required to run the web application.
What can cause that error to appear, seemingly at random, after a few days? And how can I fix it?
The server is Windows Server 2003, which runs IIS 6.0, and it is setup to use .Net Framework 4.0
The closest I can find online towards explaining this problem is this forum post, where the user experiencing the same error and got a replay stating:
The WinNT provider does not do well in a server environment. I am actually suprised you don't see this with a much smaller load. I have been able to get this with only 2 or 3 users.
and this SO comment stating
The BEST way to correctly authenticate someone is to use LogonUserAPI as @stephbu write. All other methods described in this post will NOT WORK 100%
where "all other methods" includes the top voted answer of using PrincipalContext.ValidateCredentials
Its sounding like PrincipalContext.ValidateCredentials
isn't completely 100% reliable on Windows Server 2003 and IIS6.0, so I rewrote my authentication code to use the LogonUser WinAPI method instead.
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken
);
IntPtr hToken;
if (LogonUser(username, "", password,
LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, out hToken))
{
...
}
User contributions licensed under CC BY-SA 3.0