Programmatically log a user in to asp.net membership and roles?
Asked Answered
E

2

6

I'm working on an Asp.Net 4.0 web application which is using the Membership and Roles features of Asp.Net.

In the application, there will be three roles which have access to the login page and one role which doesn't.

The way it is supposed to work is as follows.

Back end user (in one of the three privileged roles) creates a user. In the code, this is done programmatically rather than using the Register User page. When the user is created, they are added to the unprivileged role, which, for the sake of clarity is called the Visitor role.

Back end user then creates a link for the new user which is in the form of https://www.example.com?link=cc82ae24-df47-4dbf-9440-1a6923945cf2

When the link is visited, the application will locate the user associated with the query string, get the username and password (which are suitably hashed and salted) and logs the user in.

This is where I'm coming undone. So far, the user creation is working just fine, the database query based on the value of the query string is doing its job and returning the relevant bits of information, it all seems to be flowing pretty well, except for the actual logging in. There doesn't seem to be a clear method of logging a user in programmatically, without going through the rigmarole of getting the user to log themselves in, which I have been explicitly told to avoid.

Here is my code, the "pers" object is a class which is populated from the database when they land on the default page of the link:

protected void LogUserIn(string p)
{
    SqlConnection conn = UtilityMethods.GetConnection();
    Guid par = Guid.Parse(p);
    BioProspect pers= new BioProspect(conn);
    pers.FillDetailsFromDb(par);

    testlable.Text = pers.ToString();
    Response.Cookies.Remove(FormsAuthentication.FormsCookieName);

    try
    {
        if (Membership.ValidateUser(pers.Email, pers.Pword))
        {

            FormsAuthentication.SetAuthCookie(pers.Email, true);

            Response.Redirect(Request.Url.Scheme + "://" + Request.Url.Host + "/About.aspx");
            testlable.Text = "Logged in!";
        }
        else
        {
            throw new Exception("Something went wrong");
        }
    }
    catch (Exception ex)
    {
        StringBuilder sb = new StringBuilder();
        foreach (DictionaryEntry d in ex.Data)
        {
            sb.Append(d.Key.ToString());
            sb.Append(": ");
            sb.Append(d.Value.ToString());
            sb.Append("\r\n");
        }

        AMessage(sb.ToString());
    }
}

Now, I've checked the values of the username and password against the database table itself and it is all correct. I understand the password stored in the aspnet tables has been salted and hashed, so I'm checking the values in a temporary table I created to hold the clear text. It's right. In addition, in the code above, the FormsAuthentication.SetAuthCookie method is asking for a username and password - in this instance, the username is the email address. This information is also correct when inspected during debugging.

I should point out, the links we are sending are pretty much one time links. Every time a change is made relevant to that particular user, the value of the link parameter will change and the old link will be come completely useless. The page they will be redirected to will hold documents which are directly relevant to that particular user and to no others.

However, we still need the benefits of the Asp.Net Membership, Profiles and Roles framework, since the "Visitor" may well have several links sent to them, with different documents and versions of documents being added and changed over time.

Can anyone think of a better way? I have so far looked at most of the relevant entries here and here but they all seem somewhat incomplete.

EDIT

I seem to have at least partially resolved this, using information gleaned from the accepted answer here

Essentially, the problem was my web.config membership section needed to be told which hashing algorithm to use. At the moment, I have no idea what the default one is, if there is one, but adding

<membership hashAlgorithmType="SHA1">

to the web.config has at least allowed me to log in users created after the above line was added. The next step is for me to understand why I can't get the other users logged in.

I am however still getting a ThreadAbortException as suggested by Joe, which I am now busily working to resolve.

Embrasure answered 18/5, 2012 at 15:29 Comment(0)
O
11

I'm not sure I completely understand your problem, but a call to FormsAuthentication.SetAuthCookie will essentially log the user in programatically.

In addition, in the code above, the FormsAuthentication.SetAuthCookie method is asking for a username and password

No, SetAuthCookie needs a username, but not a password.

In your sample code, the call to Response.Redirect will throw a ThreadAbortException, so that your catch block will execute. Maybe that's confusing you.

In response to comment:

Essentially, the problem I am having is that the user appears not to be getting logged on. The FormsAuthenticate method is returning false

There is no FormsAuthenticate method in the above code.

Having said that, I don't see why you're trying to authenticate the user (Membership.ValidateUser) in this scenario. I'd have thought you'd want to locate the username associated with the querystring, then simply call FormsAuthentication.SetAuthCookie to log him in.

Oldie answered 18/5, 2012 at 15:38 Comment(8)
Hi Joe, thanks for the answer. You are right, it doesn't ask for a password and in the code, I'm not passing it one. And again, the ThreadAbortException is happening, but I have to admit I don't understand why. I've used that method to redirect between pages in the past and it seemed to work. That said though, the try..catch block isn't catching that particular exception. If I leave the else block out, the try..catch doesn't catch anything.Embrasure
Essentially, the problem I am having is that the user appears not to be getting logged on. The FormsAuthenticate method is returning false.Embrasure
Apologies, I meant Membership.ValidateUser, which was indeed the consistently returning false. The reason I'm using the ValidateUser method is simply because the research I have done so far seems to indicate that user validation is performed before setting the auth cookie - I don't want to authenticate an inactive user, which could happen without that validation.Embrasure
"I don't want to authenticate an inactive user" - in which case you should call Membership.GetUser and check if the user is active. But I don't see why you'd want to authenticate, as this would need you to have access to the unencrypted password. If Membership.ValidateUser is returning false, and your username is valid, it follows that your password is incorrect.Oldie
Hiya @Joe, thanks again. In my handling, I'm calling GetUser after the user has been validated and before the user is authenticated. This is all fairly hazy for me because I'm fairly new to Membership. From what I can work out, I want to validate the user initially because the user isn't arriving from an explicit login page, it is arriving from a link which has a Guid as a parameter. That parameter is stored within a table I have created myself. I could have used a Profile here, but chose instead to create my own table.Embrasure
My basic plan is to ensure the user is valid, then use GetUser to check for status, then authenticate the user once all the t's are crossed and the i's are dotted. I've edited the question to show that I now appear to have the authentication happening as it should. I'm still baffled by the ThreadAbortException though - a) how did you know it was going to happen and b) how do I stop it please? Am currently researching it.Embrasure
@inkysmithy, the Response.Redirect overload you are using will immediately terminate the current request by throwing a ThreadAbortException. This behavior means that code in your page lifecycle following the Response.Redirect doesn't get executed. If you don't want this behavior, use the Response.Redirect overload that takes an endResponse parameter, which you can set to false.Oldie
Aha, that does explain it. That's brilliant, thank you very much.Embrasure
K
1

I understand the password stored in the aspnet tables has been salted and hashed, so I'm checking the values in a temporary table I created to hold the clear text. It's right. In addition, in the code above, the FormsAuthentication.SetAuthCookie method is asking for a username and password - in this instance, the username is the email address. This information is also correct when inspected during debugging.

Please do not store the password or any password in plain text.

Essentially, the problem I am having is that the user appears not to be getting logged on. The FormsAuthenticate method is returning false.

Are you 100% sure this simply isn't a cookie issue?

It seems to be you should be able to provide the stored password hash and the username and log into said user account. Just override the method that hashes the "password" and instead forward the protected password onwards to the authentication system instead.

Kristalkristan answered 18/5, 2012 at 17:14 Comment(1)
Hi, thanks for your reply. At the moment, for debugging purposes, I am using a temporary table to store clear text passwords in. The production application will indeed have salted and hashed passwords. In answer to your question, no, I'm not 100% sure this simply isn't a cookie issue. Currently, I'm going through every possible method to figure it out - if I can get a login working, I'll then start to figure out why it is working and why the others didn't. At the moment though, Membership.ValidateUser(uname, pwd) seems to give nothing but false.Embrasure

© 2022 - 2024 — McMap. All rights reserved.