Question

I'm trying to create a simple user authentication function but I just can't get it to work. Here is the code I'm working on:

public class LoginController : ApiController
{
    private void SetPrincipal(IPrincipal principal)
    {
        Thread.CurrentPrincipal = principal;
        if (HttpContext.Current != null)
        {
            HttpContext.Current.User = principal;
        }
    }

    public bool Login(string token)
    {                       
        //Check token
        if (.....) 
        {
            //Authenticate user
            var identity = new GenericIdentity("Test user");
            SetPrincipal(new GenericPrincipal(identity, new string[]{"Test role"}));
        }
    }

    [Authorize]
    public string TestFun()
    {
        return "Hello " + User.Identity.Name;       
    }
}

So, if I try to call method TestFun() first, it returns error code 401 like it should. However when I call method Login() it should somehow save user credentials, but this is where I get lost, I just can't get it to work.

TestFun() always returns error code 401 even if I call Login() first. If I try to put return "Hello " + User.Identity.Name; in the Login() function it returns correct username, but in the TestFun() the user is not available.

I've even tried using Sessions and FormsAuthentication but I just can't get it to work, even on this really simple example.

Can someone please tell me what am I missing?

Thanks!

Was it helpful?

Solution

The Login method sets the principal for current request only. Just after the request completes, the principal context is wiped out so that the server can handle other requests for other users. When a new request comes, eons later from the server perspective, the principal context no longer exists and if nothing restores it, the request is unauthenticated.

To fix this you have to return something from your login method to the client. Not only bool but rather - an authentication token. Something the client could use to authenticate further requests.

It could be anything. Forms cookie would be fine as long as the client remembers to append it to further requests. Another common practice is to have a custom authentication token returned to the client and then appended by the client in a custom authentication header. And as forms cookies are handled by the Forms Authentication module, custom headers would need a custom mvc authentication filter or custom asp.net authentication module so that the token is readed, the identity is extracted and restored just before the request is about to execute.

If you don't like to bake your own token infrastructure, I would also recommend OAuth2 tokens. There is a great book that contains easy to follow examples on this and other possible authentication methods:

http://www.amazon.com/Pro-ASP-NET-Web-API-Security/dp/1430257822/ref=sr_1_1?ie=UTF8&sr=8-1&keywords=web+api+security

OTHER TIPS

I just got the same issue, yes, I agreed we need to save that principal into somewhere (cookie, session) for other action to use, so, in SetPrincipal function I added

HttpContext.Current.Session["user"] = HttpContext.Current.User;

Now, the issue is how to get it back for other action, the idea popups in my mind is to extend AuthorizeAttribute and override IsAuthrized function, it will read the session first and if it found the session, it will return true, otherwise it will return false.

namespace BinZ
{
    public class MyAuthorizeAttribute:AuthorizeAttribute
    {        

        protected override bool IsAuthorized(HttpActionContext actionContext) {            
            HttpContext.Current.User = HttpContext.Current.Session["user"] as IPrincipal;
            return HttpContext.Current.User != null;
        }
    }
}

Please remember to replace [Authorize] to [MyAuthorizeAttribute] in WebApi controller.

It works for me very well.

Cheers

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top