none
WCF Custom Security Token Authenticator random Authentication error RRS feed

  • Question

  • We have a custom security token authenticator which is used to authenticate the token against our own user database. We are calling the WCF service from the web application. The web application works fine calling lot of WCF functions. But we have a fuctionality for the web user can change the password from the web interface. The change password changes the underlying password for the user using WCF function. The change password works very strange, if the user changes the password, logout and login again. This works sometime and some time throws authentication error from our custom validation function. When I put some trace log to log the user name and password, then I noticed that, even though the password is changed, in the database and custom service model, some time the call is done with the old user name and password. This is very random and no specific flow to regenerate the error.  Anybody has any idea what could be the problem?

    This is the code for Custom token authenticator

    public class WebProxyTokenAuthenticator : SecurityTokenAuthenticator
    	{
    		protected override bool CanValidateTokenCore(SecurityToken token)
    		{
    			return (token is UserNameSecurityToken);
    		}
    		protected override ReadOnlyCollection<IAuthorizationPolicy> ValidateTokenCore(SecurityToken token)
    		{
    			// Check validity time
    			if (token.ValidFrom > DateTime.UtcNow || token.ValidTo < DateTime.UtcNow)
    				throw new SecurityTokenValidationException("Security token is not valid because of validity time.");
    			UserNameSecurityToken usernameToken = (UserNameSecurityToken)token;
    			// Get identity
    			User identity = null;
    			SecurityService service = new SecurityService();
    			try
    			{
                    System.Diagnostics.Trace.WriteLine("Authenticating the request :: " + usernameToken.UserName + " :: " + usernameToken.Password);
    				if (usernameToken.UserName == "Guest")
    					identity = User.Guest;
    				else
    					identity = service.AuthenticateUser(usernameToken.UserName, usernameToken.Password);
    				Merit.ServiceModel.ServiceManager.Instance.ClientCredentials.UserName.UserName = usernameToken.UserName;
    				Merit.ServiceModel.ServiceManager.Instance.ClientCredentials.UserName.UserName = usernameToken.Password;
    			}
    			catch (LoginFailedException ex)
    			{
                    System.Diagnostics.Trace.WriteLine("Authenticating the request Logon failed :: " + usernameToken.UserName + " :: " + usernameToken.Password);
    				// Set failure policy to return correct fault to client.
    				DefaultClaimSet failedClaimSet = new DefaultClaimSet(WebProxyAuthorizationPolicy.IssuerClaim,
    					new Claim(WebProxyAuthorizationPolicy.IdentityClaim, null, Rights.Identity),
    					new Claim(WebProxyAuthorizationPolicy.IdentityClaim, null, Rights.PossessProperty),
    					new Claim(WebProxyAuthorizationPolicy.ExceptionClaim, ex, Rights.PossessProperty));
    				List<IAuthorizationPolicy> failedPolicies = new List<IAuthorizationPolicy>(1);
    				failedPolicies.Add(new FailedAuthenticationPolicy(failedClaimSet));
    				return failedPolicies.AsReadOnly();
    			}
    			if (identity == null)
    				throw new SecurityTokenValidationException("Bad credentials.");
    			// Setup authorization claim set
    			DefaultClaimSet claimSet = new DefaultClaimSet(WebProxyAuthorizationPolicy.IssuerClaim,
    					new Claim(WebProxyAuthorizationPolicy.IdentityClaim, identity, Rights.Identity),
    					new Claim(WebProxyAuthorizationPolicy.IdentityClaim, identity, Rights.PossessProperty),
    					new Claim(ClaimTypes.Upn, usernameToken.UserName, Rights.PossessProperty));
    			List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>(1);
    			policies.Add(new WebProxyAuthorizationPolicy(claimSet));
    			return policies.AsReadOnly();
    		}
    	}

    This is code for login

    public static User Authenticate(string userName, string password)
    		{
    			string oldUserName = ServiceManager.Instance.ClientCredentials.UserName.UserName;
    			string oldPassword = ServiceManager.Instance.ClientCredentials.UserName.Password;
    			ServiceManager.Instance.ClientCredentials.UserName.UserName = userName;
    			ServiceManager.Instance.ClientCredentials.UserName.Password = password;
    			MembershipProviderCache.Credentials credentials = new MembershipProviderCache.Credentials(userName, password);
                User user = MembershipProviderCache.GetUser(credentials);
    			if (user == null)
    			{
    				// Authenticate using service
    				try
    				{
    					user = ServiceManager.Instance.GetProxy<ISecurityService>().Authenticate();
                        MembershipProviderCache.CacheUser(user, credentials);
    				}
    				catch (FaultException<LoginFailedReason> ex)
    				{
    					// Login unsuccessful so put to the original state
    					ServiceManager.Instance.ClientCredentials.UserName.UserName = oldUserName;
    					ServiceManager.Instance.ClientCredentials.UserName.Password = oldPassword;
    					throw new LoginFailedException(ex.Detail);
    				}
    				if (user == null)
    					return null;
                    
    			}
               
                
    			// Set current principal
    			System.Threading.Thread.CurrentPrincipal = new WebProxyPrincipal(user);
    			HttpContext.Current.User = System.Threading.Thread.CurrentPrincipal;
    			return user;
    		}

    And this is the code for updating the password

    public static User UpdateUserInfo(Guid? userId, string newUserName, string oldPassword, string newPassword)
    		{
    			string targetUserName = null;
    			bool changeUser = false;
    			bool changePW = false;
    			
                targetUserName = System.Threading.Thread.CurrentPrincipal.Identity.Name;
                // Domain check for SiteCore
                string[] strSplitArr = targetUserName.Split('\\');
                if (strSplitArr.Length > 1)
                    targetUserName = strSplitArr[1];
                else
                    targetUserName = strSplitArr[0];
                User user = Authenticate(targetUserName, oldPassword);
    			if (user == null)
    				throw new InvalidOperationException();	
    			try
    			{
    				if (newUserName != targetUserName)
    				{
    					User currUser; 
    					changeUser = true;
    					if (newPassword != null)
    					{
    						changePW = true;
    						// Change password
    						ServiceManager.Instance.GetProxy<ISecurityService>().ChangePassword(userId, newPassword);
    						User newUser = Authenticate(targetUserName, newPassword);
                            if (newUser == null)
    							throw new InvalidOperationException();
    						currUser = ServiceManager.Instance.GetProxy<ISecurityService>().GetUser(newUser.Id);
    					}
    					else
    					{
    						currUser = ServiceManager.Instance.GetProxy<ISecurityService>().GetUser(((User)System.Threading.Thread.CurrentPrincipal.Identity).Id);
    						newPassword = oldPassword;
    					}
    					// Change username
    					currUser.UserName = newUserName;
    					ServiceManager.Instance.GetProxy<ISecurityService>().UpdateUser(currUser);
    					targetUserName = currUser.UserName;
    					//Logout();
    					//HttpContext.Current.Response.Redirect("Login.aspx");
    				}                  
    				else if (newPassword != null)
    				{
    					changePW = true;
    					// Change password
    					ServiceManager.Instance.GetProxy<ISecurityService>().ChangePassword(userId, newPassword);
    				}
    			}
    			catch (FaultException<LoginFailedReason> ex)
    			{
    				throw new LoginFailedException(ex.Detail);
    			}
    			catch (FaultException<ServiceFault> ex)
    			{
    				if (ex.Detail == ServiceFault.AccessDenied)
    					throw new InvalidOperationException();
    				else if (ex.Detail == ServiceFault.InvalidParameter)
    					throw new ArgumentException("newPassord");
    				throw;
    			}
    			if (targetUserName != null)
    			{
    				// Remove from cache and relogin
    				MembershipProviderCache.RemoveUserPassword(targetUserName);
    				bool rememberMe = false;
    				FormsAuthenticationTicket ticket = GetAuthenticationTicket();
    				if (ticket != null)
    					rememberMe = ticket.IsPersistent;
    				User newUser = DoLogin(targetUserName, newPassword, rememberMe);
                    return newUser;
    			}
                return null;
    		}

     

    Sunday, February 3, 2013 8:31 PM

All replies

  • Once the User Change the password can you make the service to generate the new token?

    or my view is wrong.


    Thanks Reddy - MCAD, MCP.

    Monday, February 4, 2013 11:15 AM