The World’s Leading Microsoft .NET Magazine
   
 
The .NET Addict's Blog

My Top Tags

                                                           

My RSS Feeds








Latest Diggs - Programming

Internet Blogs - Blog Top Sites

Site Hits

Total: 2,502,482
since: 19 Jan 2005

Application Authentication on a Peer Network using WCF

posted Fri 08 Dec 06

When figuring out how I was going to do authentication for Ulysses Agenda, I had a couple of design issues. The first is that the user needs to be persisted somewhere other than the game client itself - can't have players modifying their own data like the good old days of Nethack and Trek for Unix (yes, I am THAT old.. now get off my yard you whippersnappers!!).

So, I really had two competing alternatives as far as patterns go:

  1. Client/Server or "Traditional" service-based approach - In this approach, the Universe Server would self-host an authentication service. The game clients could then be built by using automated tools to suck the metadata out of the service, build proxy clients, and finally allow them to connect at runtime and invoke the remote methods like Authenticate or some such.
    Pros: None that I can think of :)
    Cons: Not very scalable (client needs to know exactly where the US resides, including its IP and port), Requires me to suck metadata out of the service in order to build the client, which means anytime I change the service I have to bend over backwards to rebuild the client, and its difficult to decouple the auth service from the US in case I want to handle authentication elsewhere.
  2. The Bus Approach - Using this model, clients drop a set of credentials onto the bus at the mesh net.p2p://ulyssesagenda/auth. Any application listening on the bus at that particular mesh address can pick up the credentials, validate them, and drop an authentication ack (acknowledgement) message back onto the bus. The clients are, of course, listening for authentication acks. If they get one for the user that is using that particular client, features become unlocked and the player is "logged in".
    Pros: Unbelievably scalable, pluggable, and maintainable. Loosely coupled and can be detached, re-attached, and swapped out. Authenticators can even be created by third parties and hosted in separate applications that happen to be listening on the auth mesh of the bus. Also, this approach is freaking cool.
    Cons: Admittedly it is hard for some programmers to wrap their heads around this style of programming (however, anybody who has done any middleware programming is extremely familiar with this approach as it wreaks of ESB), takes a little more know-how and a few more lines of code to implement.

So I opted for approach #2. The use case is like this:

User fires up their Ulysses Agenda client and is prompted for their credentials. They hit OK after submitting their credentials. The credentials go out on the wire and the client has no idea about the location or physical configuration of the authenticator. All it knows is that some service, sitting somewhere on the wire, conforms to the IAuthenticator interface. The client application then greys out the GUI and says something like "Waiting for Network Authentication...". What should hopefully only be a few milliseconds later, a message comes into the client that invokes the AuthenticatorAck method on the client. This method is invoked when an authenticator validates a set of credentials on the net.p2p://ulyssesagenda/auth mesh.

So how does it all work. First, we start with the contract for the IAuthenticator service and for the channel it resides on, IAuthenticatorChannel:

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;





namespace UlyssesAgenda.NetworkLibrary
{
    [ServiceContract(CallbackContract=typeof(IAuthenticator))]
    public interface IAuthenticator
    {
        [OperationContract(IsOneWay=true)]
        void Authenticate(string userName, string password);

       
[OperationContract(IsOneWay = true)]
        void AuthenticateAck(string userName, Guid authToken);
    }

   
public interface IAuthenticatorChannel : IAuthenticator, IClientChannel
    {  
    }
}

What's interesting is that the game clients will have an implementation for the AuthenticateAck method while the authentication server will have an implementation for the Authenticate method, which actually calls the AuthenticateAck method on the mesh (remember that calling a method on a mesh is essentially equivalent to invoking a remote method on every client in a peer network.. though WCF optimizes the call paths immensely).

So, here's the server-side implementation that can be found inside the Universe Server application:

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;

using UlyssesAgenda.NetworkLibrary;


namespace UlyssesAgenda.UniverseServer.NetLibImplementations
{
    public class Authenticator : IAuthenticator
    {
        #region IAuthenticator Members
        // When this receives an authenticate message, send out the ack
        // note that the authenticator has no implementation for the ack, that
        // should be in the clients.
        public void Authenticate(string userName, string password)
        {
            Console.WriteLine("Received auth request for {0}, sending Ack Back.", userName);
            CommCentral.Authenticator.AuthenticateAck(userName, Guid.NewGuid());
        }

       
public void AuthenticateAck(string userName, Guid authToken)
        {
            // ignore messages of this type.
        }

        #endregion       
    }
}

And here's the game client implementation:

using System;
using System.Collections.Generic;
using System.Text;


using UlyssesAgenda.NetworkLibrary;

namespace AuthTest.NetLibImplementations
{
    public class Authenticator : IAuthenticator
    {
        #region IAuthenticator Members

       
public void Authenticate(string userName, string password)
        {
            // no implementation on clients
        }

       
public void AuthenticateAck(string userName, Guid authToken)
        {
            Console.WriteLine("An auth server somewhere just authenticated {0} with Guid {1}",
                   userName, authToken.ToString());
        }
        #endregion
    }
}

I personally think that I'm OK here keeping the authentication client and authentication server halves of the same coin in the same interface because, well, they're two halves of the same coin. The other alternative would be to split things into an IAuthenticatorClient and IAuthenticatorServer interface, each with a single method. To me, a single-method interface screams of over-re-factoring and probably won't buy me much. With this, I can look at the authentication contract (IAuthenticator) and quickly see that I can have applications that either perform authentications, or respond to authentications, or both - its totally flexible.

On the client, it becomes pretty simple to instantiate the service and drop credentials on the wire:

_auth = new NetLibImplementations.Authenticator();
_authSite = new InstanceContext(_auth);
_authChannelFactory = new DuplexChannelFactory<IAuthenticatorChannel>(_authSite, "AuthEndPoint");
_authProxy = (IAuthenticatorChannel)_authChannelFactory.CreateChannel();


_authProxy.Authenticate("Kevin", "Password");

Calling Authenticate actually calls Authenticate on all applications that are listening on the net.p2p://ulyssesagenda/auth mesh. Some clients will ignore that message (such as other game clients), while other applications will take the information and send out a message in response, such as the Universe Server's built-in authenticator.

Thoughts? Opinions? Hate mail? (well you can leave the hate mail off.. I'd just censor you anyway)

tags:                

links: digg this    del.icio.us    technorati    reddit




1. Rob left...
Sat 09 Dec 06 10:14 am

This is a good post. I have been waiting to hear some of these types of details. One thing I have been wondering is (and this may be my lack of p2p knowledge): How do you prevent someone from maliciously acting as an auth service or sending bogus game state data to the universe server?


2. Kevin Hoffman left...
Sat 09 Dec 06 6:13 pm

There are two ways of dealing with it. The easiest way to deal with it is that the WCF allows for channel-level authentication via x.509 certificates, making it so that clients need a particular certificate to even get into the mesh. At this point, you can be assured that you have a private, secure, guaranteed mesh knowing that only code written by publisher X (e.g. code with signature Y) can get into the mesh. You can stop worrying there, or you can devise your own additional schemes above and beyond that, but I think thats really unnecessary. The channel-level security in WCF is fantastic. I'm hoping to have a discussion with Ravi Rao shortly to find out what kind of channel security he recommends for a situation like this.


3. Rob left...
Sun 10 Dec 06 11:05 am

Of course! I completely forgot about x.509 when I was posting. Great solution. Please post the results of your conversation with Ravi. I'm sure there are a lot of people who would be interested.


4. Roland Rodriguez left...
Fri 15 Jun 07 6:12 pm :: http://www.rolandrodriguez.net

That sounds great, but how might one get away from having to have a central server for credential management. If one wanted a truly decentralized network, is there a pattern for authentication and authorization. In other words, how could one take advantage of all the peers out there to do the authentication and authorization without requiring a database server or AD machine out there with credentials stored on it. Even though the bus architecture enables location transparency and unlimited scalabality, it doesn't enhance the redundancy. If my auth listeners are down, nobody can get authenticated on the mesh. In addition, if you scale up to two auth servers how would you determine which auth server a peer is going to in order to distribute load? If you put in a load balancer, once again you introduce a single point of failure. Any ideas on how you could truly have a trusted mesh with distributed account storage and management?

Regards,

Roland Rodriguez Solution Architect


5. Kevin English left...
Wed 09 Jan 08 8:21 pm

I see two flaws in this design. Nothing keeps a client from creating their own auth server and putting it on the mesh. Nothing keeps clients from sniffing the mesh to see all the other client's usernames and passwords. The password should be kept on the client side. The server should pass the client a nonce (random crap) to hash using the password. If the server agrees that the nonce has been properly hashed then the client is logged in.


Tag Related Posts

NYC SharePoint Developer Needed

Mon 12 May 08 12:09 P GMT-05

iPhone Underrated as a Gaming Device?

Fri 14 Mar 08 1:50 P GMT-05
tags:        

CLINQ v1 Demo - Network Message Filtering

Wed 09 Jan 08 7:47 P GMT-05
tags:        

Building a Ledger Style for WPF Grids

Tue 21 Aug 07 3:30 P GMT-05
tags:    

Continuous LINQ - Can I write games with it?

Mon 13 Aug 07 3:09 P GMT-05
tags:        

My 2008 Wishlist : A Transformers MMORPG

Tue 26 Jun 07 12:04 P GMT-05
tags:        

My first "Acropolis" Application

Mon 04 Jun 07 1:40 P GMT-05
tags:      

Exploring the Delegate Design Pattern

Mon 14 May 07 6:30 P GMT-05

Orcas' Hidden Gem - The managed PNRP stack

Fri 11 May 07 6:45 P GMT-05
tags:        

Core Data - Almost too Easy?

Wed 18 Apr 07 2:23 P GMT-05

Will Silverlight be DOA?

Mon 16 Apr 07 8:02 P GMT-05

Exploring the MVC Pattern in WPF

Tue 10 Apr 07 12:51 P GMT-05
tags:                      

My Little Pony .NET Unleashed 2007

Fri 30 Mar 07 1:59 P GMT-05

An experience with the Leopard beta

Mon 26 Mar 07 7:45 P GMT-05
tags:                

Authorness

Thu 15 Mar 07 1:44 P GMT-05

WPF Bindings == WTF Bindings?

Mon 12 Mar 07 6:31 P GMT-05

On MUDs

Thu 08 Mar 07 5:00 A GMT-05
tags:                    

Cocoa Programming vs. WPF : NIB vs XAML

Tue 20 Feb 07 2:09 P GMT-05

Cocoa Bindings vs. WPF Binding

Thu 15 Feb 07 5:41 P GMT-05
tags:                

What I think is a bug in WCF POX messaging

Thu 04 Jan 07 4:58 P GMT-05
tags:      

Ulysses Agenda Makes Redmond Developer News

Wed 29 Nov 06 7:10 P GMT-05
tags:                

A visit to Nintendo World on "Wii Day"

Mon 20 Nov 06 12:37 P GMT-05
tags:          

Ulysses Agenda - Network Engine Test 1

Mon 09 Oct 06 2:26 A GMT-05
tags: