user2415010
user2415010

Reputation:

Storing hash of username using ASP.NET identity

I'm writing an application which needs to have personally identifiable information removed/absent at all times from the database. Given that someone may use their real name in their username, and that an email address may be present in their AspUserIdentity records, I have decided one solution might be to hash these values. In simple terms: when someone logs in with a username, I hash the username they entered and see if that hash exists in the database; if it does, then I log them in. This is easy to do and works just fine by modifying the Login and Register methods in the AccountController. But now I am left with no knowledge of the entered username...

I could just store the username in session, but that seems jankety. What I'd like to do is to update the cookie that gets sent down upon successful login to use the username they entered (and not the hashed value stored in the DB). That way User.Identity.GetUserName() returns the plain text username (and not the hashed username). To the client the process ought to be transparent (and to me as the programmer too).

The question is: how? What's the best place to do this? I'm still relatively green when it comes to the latest ASP.NET Identity stuff. I see in Startup.Auth there's a lot of juicy stuff related to cookies, but I don't see anywhere I can modify the cookie itself upon login and prior to it being sent down.

Is all of this deep within Owin itself?

Thanks in advance,

Upvotes: 2

Views: 772

Answers (1)

trailmax
trailmax

Reputation: 35106

When user logs in and you compare the hash of username, you can add their real username as a claim to the identity. This is serialised into cookie and available with the user on every request, but not persisted in a DB:

public async Task SignIn(string userName, string password, bool rememberMe)
{
    var hashedUsername = getMyHash(username)
    var loggedInUser = await userManager.FindAsync(hashedUsername, password);
    if (loggedInUser == null)
    {
        // failed to login
        return FailedToLogin(); // whatever you do there
    }

    // Ok, from now on we have user who provided correct username and password.

    // and because correct username/password was given, we reset count for incorrect logins. This is for user lockout
    await userManager.ResetAccessFailedCountAsync(loggedInUser.Id);

    if (!loggedInUser.EmailConfirmed)
    {
        return EmailIsNotConfirmed(); // email is not confirmed - display a message
    }

    if (await userManager.IsLockedOutAsync(loggedInUser.Id))
    {
        return UserLockedOut(); // user is locked out - display a message
    }

    var identity = await userManager.CreateIdentityAsync(loggedInUser);
    identity.AddClaim(new Claim("OriginalUsername", originalUsername));

    var authenticationManager = context.GetOwinContext().Authentication;
    authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = rememberMe }, identity);

    //TODO redirect to a home page
}

Then when you need to display an actual username, not a hash do this:

public static String GetOriginalUsername(this IPrincipal principal)
{
    if (principal == null)
    {
        return String.Empty;
    }

    var claimsPrincipal = principal as ClaimsPrincipal;

    if (claimsPrincipal == null)
    {
        return String.Empty;
    }

    var originalUsernameClaim = principal.Claims.SingleOrDefault(c => c.Type == "OriginalUsername");

    if (originalUsernameClaim == null)
    {
        return String.Empty;
    }

    return originalUsernameClaim.Value;
}

And call this method on User.GetOriginalUsername() in *.cshtml files or in Controllers. Or HttpContext.Current.User.GetOriginalUsername() if you need it somewhere else.

Upvotes: 1

Related Questions