ASP.NET Web API Authentication

40,676

Solution 1

allow a user to log in to the API

You need to send a valid Forms Authentication cookie along with the request. This cookie is usually sent by the server when authenticating (LogOn action) by calling the [FormsAuthentication.SetAuthCookie method (see MSDN).

So the client needs to perform 2 steps:

  1. Send an HTTP request to a LogOn action by sending the username and password. In turns this action will call the FormsAuthentication.SetAuthCookie method (in case the credentials are valid) which in turn will set the forms authentication cookie in the response.
  2. Send an HTTP request to an [Authorize] protected action by sending along the forms authentication cookie it retrieved in the first request.

Let's take an example. Suppose that you have 2 API controllers defined in your web application:

The first one responsible for handling authentication:

public class AccountController : ApiController
{
    public bool Post(LogOnModel model)
    {
        if (model.Username == "john" && model.Password == "secret")
        {
            FormsAuthentication.SetAuthCookie(model.Username, false);
            return true;
        }

        return false;
    }
}

and the second one containing protected actions that only authorized users can see:

[Authorize]
public class UsersController : ApiController
{
    public string Get()
    {
        return "This is a top secret material that only authorized users can see";
    }
}

Now we could write a client application consuming this API. Here's a trivial console application example (make sure you have installed the Microsoft.AspNet.WebApi.Client and Microsoft.Net.Http NuGet packages):

using System;
using System.Net.Http;
using System.Threading;

class Program
{
    static void Main()
    {
        using (var httpClient = new HttpClient())
        {
            var response = httpClient.PostAsJsonAsync(
                "http://localhost:26845/api/account", 
                new { username = "john", password = "secret" }, 
                CancellationToken.None
            ).Result;
            response.EnsureSuccessStatusCode();

            bool success = response.Content.ReadAsAsync<bool>().Result;
            if (success)
            {
                var secret = httpClient.GetStringAsync("http://localhost:26845/api/users");
                Console.WriteLine(secret.Result);
            }
            else
            {
                Console.WriteLine("Sorry you provided wrong credentials");
            }
        }
    }
}

And here's how the 2 HTTP requests look on the wire:

Authentication request:

POST /api/account HTTP/1.1
Content-Type: application/json; charset=utf-8
Host: localhost:26845
Content-Length: 39
Connection: Keep-Alive

{"username":"john","password":"secret"}

Authentication response:

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Wed, 13 Jun 2012 13:24:41 GMT
X-AspNet-Version: 4.0.30319
Set-Cookie: .ASPXAUTH=REMOVED FOR BREVITY; path=/; HttpOnly
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Content-Length: 4
Connection: Close

true

Request for protected data:

GET /api/users HTTP/1.1
Host: localhost:26845
Cookie: .ASPXAUTH=REMOVED FOR BREVITY

Response for protected data:

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Wed, 13 Jun 2012 13:24:41 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Content-Length: 66
Connection: Close

"This is a top secret material that only authorized users can see"

Solution 2

I take android as example.

public abstract class HttpHelper {

private final static String TAG = "HttpHelper";
private final static String API_URL = "http://your.url/api/";

private static CookieStore sCookieStore;

public static String invokePost(String action, List<NameValuePair> params) {
    try {
        String url = API_URL + action + "/";
        Log.d(TAG, "url is" + url);
        HttpPost httpPost = new HttpPost(url);
        if (params != null && params.size() > 0) {
            HttpEntity entity = new UrlEncodedFormEntity(params, "UTF-8");
            httpPost.setEntity(entity);
        }
        return invoke(httpPost);
    } catch (Exception e) {
        Log.e(TAG, e.toString());
    }

    return null;
}

public static String invokePost(String action) {
    return invokePost(action, null);
}

public static String invokeGet(String action, List<NameValuePair> params) {
    try {
        StringBuilder sb = new StringBuilder(API_URL);
        sb.append(action);
        if (params != null) {
            for (NameValuePair param : params) {
                sb.append("?");
                sb.append(param.getName());
                sb.append("=");
                sb.append(param.getValue());
            }
        }
        Log.d(TAG, "url is" + sb.toString());
        HttpGet httpGet = new HttpGet(sb.toString());
        return invoke(httpGet);
    } catch (Exception e) {
        Log.e(TAG, e.toString());
    }

    return null;
}

public static String invokeGet(String action) {
    return invokeGet(action, null);
}

private static String invoke(HttpUriRequest request)
        throws ClientProtocolException, IOException {
    String result = null;
    DefaultHttpClient httpClient = new DefaultHttpClient();

    // restore cookie
    if (sCookieStore != null) {
        httpClient.setCookieStore(sCookieStore);
    }

    HttpResponse response = httpClient.execute(request);

    StringBuilder builder = new StringBuilder();
    BufferedReader reader = new BufferedReader(new InputStreamReader(
            response.getEntity().getContent()));
    for (String s = reader.readLine(); s != null; s = reader.readLine()) {
        builder.append(s);
    }
    result = builder.toString();
    Log.d(TAG, "result is ( " + result + " )");

    // store cookie
    sCookieStore = ((AbstractHttpClient) httpClient).getCookieStore();
    return result;
}

Attention please: i.localhost cannot be used. Android device look localhost as itself host. ii.If deploy the web API in IIS, the Form authentication must be opened.

Share:
40,676
Mujtaba Hassan
Author by

Mujtaba Hassan

I am a Software engineer having experience in variety of technologies including SharePoint ASP.Net, C#.Net desktop apps, MS SQL Server, MS Sharepoint Server, Android application development, Webservices, Javascript/Jquery, HTML and CSS.

Updated on December 06, 2020

Comments

  • Mujtaba Hassan
    Mujtaba Hassan over 3 years

    I am looking to authenticate a user from a client application while using the ASP.NET Web API. I have watched all the videos on the site and also read this forum post.

    Putting the [Authorize] attribute correctly returns a 401 Unauthorized status. However, I need to know how to allow a user to log in to the API.

    I want to provide user credentials from an Android application to the API, get the user logged in, and then have all subsequent API calls pre-authenticated.

  • Mujtaba Hassan
    Mujtaba Hassan almost 12 years
    Is it going to maintain a session for Android application?
  • Mujtaba Hassan
    Mujtaba Hassan almost 12 years
    Got the point but can you please post a sample code for the second point. Thanks for your answer.
  • Darin Dimitrov
    Darin Dimitrov almost 12 years
    Writing an Android HTTP client is a subject for another question. It is unrelated to ASP.NET MVC and ASP.NET MVC Web API which is what your question was about. I would recommend you start a new thread explicitly tagging with Java and Android in which you ask about how to write an HTTP client which sends requests using cookies.
  • Mujtaba Hassan
    Mujtaba Hassan almost 12 years
    Actually in literature of MVC4 WebApi they have written that WebAPI is target for third party clients specially mobile clients (and ofcourse it is). Lets say we have a desktop application client, can you post a simple code snippet please. Thanks
  • Darin Dimitrov
    Darin Dimitrov almost 12 years
    Sure, I have posted an example.
  • Abhijit-K
    Abhijit-K almost 12 years
    Can there be a way to do it with a single direct call without the Logon API call? Is there not a way to embed credentials in the same request headerby default? I believe it can be done in a custom way by deriving from a the default [Authorize] attribute or make your own [Authorize] attribute and extract the credentials from the HTTP request header. In WCF REST when we call REST API using HttpClient we specify credentials. How is WebApi different than WCF Authentication? With token things will be fast with subsequent calls I believe hence we use it with API's, right?
  • Darin Dimitrov
    Darin Dimitrov almost 12 years
    You could use custom headers for tokens. But you will be reinventing wheels. That's what the forms authentication cookies already do for you.
  • Abhijit-K
    Abhijit-K almost 12 years
    Happy to see the complete code you posted including the HTTP request response chain; cant give more than +1. About custom headers that is for per request separate authentication not for providing tokens, I say. Some things are new with this shift from WCF to Web API. With WCF REST there was no ASP.net infra to get the token, is there? Every call has to embed the credentials I believe?
  • Abhijit-K
    Abhijit-K almost 12 years
    One more thing is Web-Api with standalone application and NOT hosted with MVC web App. Is there FormsAuthentication module available in this case of Web-api with standalone app like Windows service?
  • Jim Harte
    Jim Harte almost 12 years
    Also see this question (and answer) about using HTTP basic authentication: stackoverflow.com/questions/10987455/…
  • Abhijit-K
    Abhijit-K almost 12 years
    Thanks for this link. I have written a windows service recently that hosts WCF REST services. I will port this service to use WEb-Api (as standalone app). As I do not use IIS hence I wonder, can I use these things which requires ASP.net infra.
  • Mujtaba Hassan
    Mujtaba Hassan almost 12 years
    Thanks Darin Dimitrov, that really helped. :)
  • markmnl
    markmnl almost 10 years
    There are a number of disadvantages to using FormsAuthentication as listed here: asp.net/web-api/overview/security/forms-authentication