tracking users RESTfully

2007-12-02 @ 22:37#

there was a thread on rest-discuss this week on the RESTful way to track authenticated users and present them w/ personalized information where appropriate. lots of talk of cookies and rendering resources using 'secret' info; making URIs non-bookmarkable; etc. turns out a rather simple way to do this is to let the *server* tell the client who the logged in user actually is.

you can do this with a URI like /users/current. it returns personal data or generic (anonymous) data, basedon the status of HTTP Auth. the big advantage of using the logged in user provided by HTTP Auth is that you don't need to add a cookie (and keep it in sync with the logged in user from HTTP Auth). you can still keep close to the REST constraints regarding URI naming and caching, too.

this is really pretty simple. you just need to provide a resource that returns (at minimum) the username of the authenticated user. here's how i do it in exyus:

[UriPattern(@"/users/current/(\.xcs)(.*)?")]
[MediaTypes("text/html","text/xml","text/plain")]
public class userCurrent : GetHandler
{
    public userCurrent()
    {
        this.ContentType = "text/html";
        this.LocalMaxAge = 0;
        this.MaxAge = 0;
        this.UseValidationCaching = false;
    }

    public override void Get()
    {
        Utility util = new Utility();

        string user = this.Context.User.Identity.Name;  // returns 'guest' for anon user
        string out_text = string.Empty;

        string mtype = util.GetMediaType(this);
        if (mtype != this.ContentType)
        {
            this.ContentType = mtype;
            this.Status.ContentType = mtype;
        }

        switch (mtype)
        {
            case "text/xml":
                out_text = "<?xml version=\"1.0\"?><user><username>{uid}</username></user>".Replace("{uid}", user);
                break;
            case "text/html":
                out_text = "<div class=\"user\"><span class=\"username\">{uid}</span></div>".Replace("{uid}", user);
                break;
            case "text/plain":
                out_text = user;
                break;
            default:
                out_text = string.Empty;
                break;
        }

        this.Response = out_text;
    }
}

a more RESTful approach would be to have the service return a document that contains all the important links to personal data.

<ul class="personal-data">
<li class="preferences"><a href="/users/user1/preferences">preferences</a></li>
<li class="favorites"><a href="/users/user1/favorites">favorites</a></li>
<li class="shoppingcart"><a href="/shopping/cart/Dt$2x">shopping cart</a></li>
</ul>    

code