views:

236

answers:

1

This question is a bit of a structural/design question as I'm having trouble working out the best way to perform the task.

In my MVC app, I am using DotNetOpenAuth (3.4) as my login information provider and just using the standard FormsAuthentication for cookies etc.

The current user table in the DB has:

  • UserId (PK, uniqueidentifier)
  • OpenIdIdentifier (nvarchar(255))
  • OpenIdDisplay (nvarchar(255))
  • Displayname (nvarchar(50))
  • Email (nvarchar(50))
  • PhoneNumber (nvarchar(50))

As the UserId is the clear identifier for a user (they should be able to change their OpenId provider at a later date), it is the key that other tables link to (for a user).

This is the current code, that on a successfull authentication, creates a temporary user and redirects to Create Action.

        switch (response.Status)
        {
            case AuthenticationStatus.Authenticated:

                FormsAuthentication.SetAuthCookie(response.ClaimedIdentifier, false);

                var users = new UserRepository();
                if (!users.IsOpenIdAssociated(response.ClaimedIdentifier))
                {
                    var newUser = new DueDate.Models.User();
                    newUser.OpenIdIdentifer = response.ClaimedIdentifier;
                    newUser.OpenIdDisplay = response.FriendlyIdentifierForDisplay;

                    TempData["newUser"] = newUser;

                    return this.RedirectToAction("Create");
                }

And now for the crux of the question:

  1. Is the response.ClaimedIdentifier the correct piece of information to be storing against a user?

  2. Is FormAuthentication.SetAuthCookie the preferred way to forms authentication? Or is there a better way?

  3. When I call SetAuthCookie, there is no data relating to the user except for the ClaimedIdentifier. If I'm consistently referring to their UserId, is a better idea to create the user, then store that UserId in the cookie instead of the ClaimedIdentifier?

  4. If I'm using that UserId in a number of places, how do I either retrieve it from the cookie, or store it somewhere else more logical/useful?

A bit long winded but I've been having trouble trying to work out the best way to do this/

+7  A: 

1.Is the response.ClaimedIdentifier the correct piece of information to be storing against a user?

Yes. And make sure the column you store it in the database with is case sensitive. Here is a table schema that demonstrates how to make sure it is case sensitive. This comes out of the DotNetOpenAuth project template's database schema. The "CS" bit of the specified collation stand for Case Sensitive.

CREATE TABLE [dbo].[AuthenticationToken] (
    [AuthenticationTokenId]    INT            IDENTITY (1, 1) NOT NULL,
    [UserId]                   INT            NOT NULL,
    [OpenIdClaimedIdentifier]  NVARCHAR (250) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL,
    [OpenIdFriendlyIdentifier] NVARCHAR (250) NULL,
    [CreatedOn]                DATETIME       NOT NULL,
    [LastUsed]                 DATETIME       NOT NULL,
    [UsageCount]               INT            NOT NULL
);

2.Is FormAuthentication.SetAuthCookie the preferred way to forms authentication? Or is there a better way?

For MVC apps it definitely is, since you still can return your preferred ActionResult from the method.

3.When I call SetAuthCookie, there is no data relating to the user except for the ClaimedIdentifier. If I'm consistently referring to their UserId, is a better idea to create the user, then store that UserId in the cookie instead of the ClaimedIdentifier?

That sounds like personal preference. But I would typically go with user_id, since it might result in a faster database lookup every time an HTTP request comes in that requires you to look up any user information.

4.If I'm using that UserId in a number of places, how do I either retrieve it from the cookie, or store it somewhere else more logical/useful?

FormsAuthentication does provide a way to store more information in its encrypted cookie than just username, but it is harder than you'd expect to use it. This snippet comes out of DotNetOpenAuth's web SSO RP sample:

const int TimeoutInMinutes = 100; // TODO: look up the right value from the web.config file
var ticket = new FormsAuthenticationTicket(
    2, // magic number used by FormsAuth
    response.ClaimedIdentifier, // username
    DateTime.Now,
    DateTime.Now.AddMinutes(TimeoutInMinutes),
    false, // "remember me"
    "your extra data goes here");

HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
Response.SetCookie(cookie);
Response.Redirect(Request.QueryString["ReturnUrl"] ?? FormsAuthentication.DefaultUrl);

Then you can get at that extra data in a future HTTP request with this:

var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (cookie != null) {
    var ticket = FormsAuthentication.Decrypt(cookie.Value);
    if (!string.IsNullOrEmpty(ticket.UserData)) {
        // do something cool with the extra data here
    }
}
Andrew Arnott
Thank you very much, a great answer and much appreciated :)
Alastair Pitts