views:

32

answers:

1

I am writing a remote service for an application using WCF, in which login information is kept in a database. The service requires session establishment through a login or account creation call. There is no ASP involved.

Now, when a client starts a session by calling an exposed IsInitiating method, I check the account data provided against the information on the database and, if it is not correct, I want to invalidate that session and force the client to start again with a call to an IsInitiating method.

Looking at some other questions, I have found pros and cons for two ways to invalidate a session. One does so the hard way, by throwing a FaultException; the other with softer manners, storing accepted session IDs.

Now, the first one, although achieving what I desire, is way too aggressive, given that incorrect logins are part of the normal flow of the application. The second one, on the other hand, allows the client to continue calling non-initiating methods, eventhough they will be rejected, while also incurring in a considerable code overhead on the service due to the added thread safety requirements.

So, the question: Is there a third path which allows the service to invalidate the session initialization and communicate it to the client, so it is forced to make a new IsInitiating call?

A reduced version of the code I have:

[DataContractAttribute]
public class AccountLoginFault
{
    public AccountLoginFault (string message)
    {
        this.Message = message;
    }

    [DataMemberAttribute]
    public string Message { get; set; }
}

[ServiceContract (SessionMode = SessionMode.Required)]
public interface IAccountService
{
    [OperationContract (
        IsInitiating = true)]
    [FaultContractAttribute (
        typeof (AccountLoginFault),
        ProtectionLevel = ProtectionLevel.EncryptAndSign)]
    bool Login (AccountData account, out string message);
}

[ServiceBehavior (
    ConcurrencyMode = ConcurrencyMode.Single,
    InstanceContextMode = InstanceContextMode.PerSession)]
public class AccountService : IAccountService
{
    public bool Login (AccountData account, out string message)
    {
        UserManager userdb = ChessServerDB.UserManager;
        bool result = false;
        message = String.Empty;

        UserData userData = userdb.GetUserData (account.Name);

        if (userData.Name.Equals (account.Name)
            && userData.Password.Equals (account.Password))
        {
            // Option one
            // Get lock
            // this.AcceptedSessions.Add (session.ID);
            // Release lock

            result = true;
        } else
        {
            result = false;

            // Option two
            // Do something with session context to mark it as not properly initialized.
            // message = "Incorrect account name or password. Account provided was " + account.Name;

            // Option three
            throw new FaultException<AccountLoginFault> (
                new AccountLoginFault (
                    "Incorrect account name or password. Account provided was " + account.Name));
        }

        return result;
    }
}
A: 

Throwing an exception is by far the easiest option because WCF enforces that the session cannot be re-used. From what I gather, what you would like the third party component to accomplish comes quite close to this functionality. But, instead of forcing the client to call IsInitialized again, you would force the client to create a new connection. This looks like a very small difference to me.

An alternative would be to have a private variable bool _authorised and check this variable at every method call.

Pieter
FaultException is the way I am implementing it, but I keep wondering if there is a more elegant way to do it using WCF mechanisms. Also, exceptions have a high impact on performance, if the user base scales considerably, and I am trying to keep them to a minimum.
Elideb
Even though a lot of users will log on, I can't believe the logon request will be a large percentage of your total requests; certainly not the failed logon requests. I would not expect this to become an issue. If you're really concerned about performance issues because of this, choose the second strategy with the private variable.
Pieter
It looks like there is not a better solution than using the FaultException. Thanks.
Elideb