Storing user’s data

In my post ‘Of Claims and Public Identities’ I have pointed out that web sites that use the ACS usually augment the information provided by the IP (typically a meaningless token) with user inputted personal information which would be stored by the application against the IP’s token.

As part of a demo I’ve been working on I’ve decided to implement this approach and, initially at least, everything seems to work fine (using my Live ID)

Below is the initial code I’ve written (it’s not pretty, but it does the job Smile ) –

First I check that the user has indeed been authenticated (if not, it has not been through the ACS, which should never have happened), next I extract the claims I expect – name identifier and identity provider – before performing a lookup in an Azure table to see if a user with this token (from this particular provider) already exists.

If I find the user in my table I redirect the request to the home page, if I don’t I redirect to the registration page which would ask the user for more details, add it to the table and then re-run the code below to verify.

    //Check that user is authenticated
    if (!User.Identity.IsAuthenticated)
    {
        throw new ApplicationException("User is not authenticated");
    }

    //get user identity
    ClaimsIdentity ci = User.Identity as ClaimsIdentity;
    if (ci == null)
        throw new ApplicationException("Identity is not ClaimsIdentity");

    //read claims from security token token
    Claim id = ci.Claims.FirstOrDefault(claim => claim.ClaimType == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
    Claim provider = ci.Claims.FirstOrDefault(claim => claim.ClaimType == "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider");

    if (id == null || provider == null)
        throw new ApplicationException("Security token did not contain expected information");

    //check user identity against user store
    UserLineDataServiceContext context = getUserContext();
    context.IgnoreResourceNotFoundException = true;

    var result = from UserLine u in context.Users where (u.RowKey == id.Value) && (u.PartitionKey == provider.Value) select u;
    UserLine user = null;
    //TODO: lookup user - this causes exception for some reason
    user = result.FirstOrDefault();

    //if user is known return view
    if (user != null)
        return Redirect("/Home/Index");
    else
    {
        //if user is not known return register view.
        UserLineModel userModel = new UserLineModel(provider.Value, id.Value);
        //TODO: should move HOme Controller's Register to the account controller?
        return View("Register", userModel);
    }
}

When I ran this with my Google id, however, I got a DataServiceQueryException on the result.FirstOrDefault() call, which took me a while to figure out.

The inner exception’s message was an xml describing an Invalid Input error with the message – “One of the request inputs is not valid”

Turns out that the Google identity is represented as a Uri (absolutely nothing wrong with that, of course), but that key fields in Azure tables do not allow certain characters, see this for more details.

The solution was to base-64-encode both partition and row key fields before performing the lookup, as well as, naturally, before storing them in the registration controller executing after the user had filled in the registration form and hit ok.

About Yossi Dahan
I work as a cloud solutions architect in the Azure team at Microsoft UK. I spend my days working with customers helping be successful in the cloud with Microsoft Azure.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: