User identity on Windows Phone 7

When you can challenge another person to a Faceted Reversi game, you provide their name. So Reversi needs a way of authenticating the user so it can send them the correct games. I didn’t want the user to have to enter a password to play my game, so I relied upon the anonymous Live ID already available on the phone. I just have to associate your anonymous ID with your chosen screen name.

Windows Live provides identity

Once I’ve gotten the anonymous Live ID using the technique I previously blogged about, I create an Identity fact. An Identity is defined in the factual model as:

fact Identity {
    string anonymousId;

    Claim* claims {
        Claim c : c.identity = this
    }
}

The anonymoudId string is the only field in an Identity. That means that it comprises the entire key of that fact. No matter how many times I construct an Identity with that anonymoudId, it will be the same instance. The Identity instance is created and added to the Community.

_community = new Community(storageStrategy)
    .AddAsynchronousCommunicationStrategy(new POXAsynchronousCommunicationStrategy(configurationProvider))
    .Register<CorrespondenceModule>()
    .Subscribe(() => _identity.Claims);

string anid = UserExtendedProperties.GetValue("ANID") as string;
string anonymousUserId = String.IsNullOrEmpty(anid)
    ? "test:user12"
    : "liveid:" + ParseAnonymousId(anid);
_identity = _community.AddFact(new Identity(anonymousUserId));

The first time you run the app, it creates this Identity instance. Every subsequent run is simply loading it from isolated storage.

Notice that I’m subscribing to _identity.Claims before the _identity is even instantiated. The subscription is a lambda expression, so it will be executed later. So what are Claims?

A user claims a name

A Claim is the desire for a person to have a specific user name. It is defined in factual as:

fact Claim {
    publish Identity identity;
    User user;
    publish IdentityService identityService;

    ClaimResponse* responses {
        ClaimResponse r : r.claim = this
    }

    bool isPending {
        not exists ClaimResponse r : r.claim = this
    }
}

The Claim refers to the Identity from the phone, and the User that is being claimed. A User is:

fact User {
    string userName;

    Claim* claims {
        Claim c : c.user = this
    }
}

Just like Identity, User has only one field. This field is its key, so any User object that I create with userName = “michael” will be the same instance. This is what makes it possible to challenge another user: the User instance I create on my phone will be the same as the one that he creates on his phone.

A service approves and denies claims

So when the user selects a name, a Claim is created. This Claim is published to an IdentityService, which is defined as:

fact IdentityService {
    Claim* pendingClaims {
        Claim c : c.identityService = this
            where c.isPending
    }
}

Notice that IdentityService has no fields. Since fields define identity in Correspondence, there is nothing to distinguish one IdentityService from another. Every time I create a new IdentityService, I get back the same instance. It is a singleton.

There is a subscriber on my server looking for Claims published to the IdentityService. When it finds a Claim, it verifies that the user name is not taken by another Identity. It will then generate a ClaimResponse:

fact ClaimResponse {
    publish Claim claim;
    int approved;
}

This response is published to the Claim. Remember the lambda expression in the Community Subscribe method? The phone has subscribed to all Claims attached to the Identity. That means that the ClaimResponse will be pushed to the phone, and the user will know whether it was accepted or rejected.

This is an example of the Service pattern in Historical Modeling. With this pattern, I can allow people to choose their own name, but still rely upon Windows Live to authenticate them for me.

Leave a Reply

You must be logged in to post a comment.