50

How do I even begin coding authentication using ASP.NET Web API so it is cross-platform to support desktop, mobile and web? I'd read of some methods of doing RESTful authentication, such as using tokens in the header.

Are there any example projects out there that utilizes this method?

Questions:

  1. If not how do I fix the [Authorize] attribute to read the token?
  2. How do I generate this token? I dont think i can use formsauthentication because that uses cookies.
  3. How do I handle the actual authorization, do the client send raw password and username then I generate the token or is there some other way?
  4. How do I handle when my website is using it? I heard this is handled differently than when an app is using it, such as getting the domain and authorizing it.
Shawn Mclean
  • 56,733
  • 95
  • 279
  • 406

4 Answers4

41

I think tokens would be a solid way to go. Forms authentication is based on cookies for the web. Not the most idea situation for all non browser clients though.

What I'd suggest is creating a custom AuthorizationFilterAttribute and overriding the OnAuthorization method. In that method, you could check for the existence of a token that you've issued to the client after they've supplied valid credentials. You can use this attribute on any method or controller you want validated. Here's a sample you might reference

 public class AuthorizeTokenAttribute : AuthorizationFilterAttribute 
{      
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        if (actionContext != null)
        {                
                if (!AuthorizeRequest(actionContext.ControllerContext.Request))
                {
                    actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { RequestMessage = actionContext.ControllerContext.Request }; 
                }
                return;
        }
    }

    private bool AuthorizeRequest(System.Net.Http.HttpRequestMessage request)
    {
        bool authorized = false;
        if (request.Headers.Contains(Constants.TOKEN_HEADER))
        {               
            var tokenValue = request.Headers.GetValues("TOKEN_HEADER");
            if (tokenValue.Count() == 1) {
                var value = tokenValue.FirstOrDefault();               
               //Token validation logic here
               //set authorized variable accordingly
            }                
        }
        return authorized;
    } }

TOKEN_HEADER is just a string representing an HTTP header that the client should pass back for authenticated requests.

So let's walk through it

  1. Client requests secure data
  2. Client is not authorized, return a response with an Unauthorized status code
  3. Client sends credentials to authenticate, which should be secured via HTTPS
  4. Once validated, client receives a token via an HTTP header, or whatever works for you
  5. Client tries requesting secure data again, this time attached the token to the request
  6. The AuthorizeTokenAttribute will validate the token and allow the action to execute.

Also, check this post by John Petersen. Making your ASP.NET Web API’s secure

Richard Tingle
  • 16,906
  • 5
  • 52
  • 77
cecilphillip
  • 11,446
  • 4
  • 36
  • 40
  • 1
    Thanks, this helped alot, how do you recommend generating the token during authentication? generate a guid and store it for the user in the database? – Shawn Mclean Apr 06 '12 at 18:53
  • that's up to you I suppose. You can use a Guid or a Hash or some type of unique value. depends on what makes sense for your application really – cecilphillip Apr 08 '12 at 23:15
  • in your code comment you say "set authorized variable accordingly"....where would the authorized variable be set? on the Request.Properties? Somewhere else? – ChrisCa Aug 14 '12 at 17:29
  • there's an authorized bool in the method AuthorizeRequest. Based on your specific authorization logic, you would set it to true or false to grant access to class or action you put the AuthorizeTokenAttribute on. – cecilphillip Aug 16 '12 at 17:53
  • 2
    Also check this updated post by John Petersen where he uses Http message handlers instead of Action filters http://codebetter.com/johnvpetersen/2012/04/04/moving-from-action-filters-to-message-handlers/ – shashi Sep 17 '12 at 11:41
  • How do you request a token if you don't have access to the API due to lack of token? This is tripping me up right now. The only way I can think of is to use a different site entirely to provide the authentication/token piece. – Joe Phillips Nov 23 '12 at 16:41
  • 1
    the API should have some endpoint which allows for an authentication/authorization workflow. Just like you have a login page for websites – cecilphillip Nov 26 '12 at 06:58
  • I think in your walk through you should have authentication as step 2, then after you do authorization. – Ray Feb 01 '13 at 18:30
  • @ray247 You really suggest doing authentication after you do authorization? – Elisabeth Jan 19 '14 at 16:58
  • I like the way Constants.TOKEN_HEADER is used to check if the value is within the headers, but to retrieve the value the string literal "TOKEN_HEADER" is used. – Robinhopok Jul 05 '17 at 07:40
21

There are lots of ways to authenticate users for a REST service. Using tokens is possible but just using Basic Authentication is even simpler and about as standard and cross platform as you can go.

Don't confuse authorization with authentication. The [Authorize] attribute is all about authorization but only after a user has been authenticated using some other mechanism. Authorization is completely useless without doing proper authentication first.

The best resource to check is Dominick Baier who is an expert on the subject.

Maurice
  • 27,582
  • 5
  • 49
  • 62
2

I use a very simple approach:

  1. define an access profile with its unique accessId and accessKey (e.g. MD5 hashed GUID value)
  2. store such access profile in database
  3. every request (GET/POST/etc.) must supply accessId, queryHash (MD5 hash value represents the query) and signature (MD5 hash value of queryHash + accessKey). Of course the client needs keep the accessKey in a secure place!!!
  4. server gets the request will check the accessId and the signature using the same calculation algorithm to reject or grant the access (authenticate)
  5. further authorization can be done on request type basis utilizing the access profile

the service with this approach using the new ASP.NET MVC web API can serve whatever type of client: browser/javascript and native(desktop or mobile) etc.

  • 3
    You might consider something stronger than MD5. At this point it can be cracked quite quickly, think minutes or hours. You should be hashing with something stronger, like SHA256/512, or preferably something computationally slow, like bCrypt. http://www.codinghorror.com/blog/2007/09/rainbow-hash-cracking.html http://www.codinghorror.com/blog/2012/04/speed-hashing.html http://security.stackexchange.com/questions/211/ http://www.eweek.com/c/a/Security/SSL-Crack-Shows-You-Must-Advance-Your-Security/1/ – EBarr Jun 08 '12 at 15:07
  • That's some useful information @EBarr (I +1'd you), but I think he would be safe with MD5 in this instance, considering that what he's encrypting would be too large and obscure for a rainbow table. – Grinn Aug 20 '12 at 14:34
  • @Grinn rainbow tables are so 2005;-).Sure md5 probably does it. Md5 can, however, be brute forced in a few minutes on a strong GPU these days. For example an amazon ec2 GPU box for a few $$. – EBarr Aug 21 '12 at 19:27
0

U can use ActionFilterAttribute and override the OnActionExecuting method. Later on register this filter in global.cs to apply this filter for all the actions like this in Application Start method

var config = GlobalConfiguration.Configuration; config.Filters.Add(new CustomAuthAttribute ());

{ namespace Customss { Public class CustomAuthAttribute : ActionFilterAttribute

{

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        // To inforce HTTPS if desired , else comment out the code
        if (!String.Equals(actionContext.Request.RequestUri.Scheme, "https", StringComparison.OrdinalIgnoreCase))
        {
            actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest)
            {
                Content = new StringContent("HTTPS Required")
            };
            return;
        }

       // get toekn from the header

        var userToken = actionContext.Request.Headers.GetValues("UserToken");
         // Customer Logic to check the validity of the token.
        // U can have some DB logic to check , custom STS behind or some loca cache used to compare the values


    }


}

} }

Mian
  • 231
  • 5
  • 7