Our solution (and we're hopelessly naive in this respect) is that we generate a unique key per session on the server in a non-uniform manner (ie. it is difficult to predict what the "next" value would be), and give that to the client code as part of its login process. It is then required to pass that value back to use for each request as the first parameter.
This ensures that:
- Logging out invalidates the authentication key
- Username and password is not sent in cleartext for the web service requests
This does not ensure that:
- Only our application code can talk to the server (the user might intercept the request, copy the key, and generate his own requests, as long as the key is still considered valid.)
What you're going to find is that as long as code on the users machine is talking to your server, you have no control over the code on that machine, only the code on that server. So if the users machine is sending you requests originating from a different program, that looks just like they would and should if your code generated them, you're going to have a hard time figuring out that this is what is happening.