122

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.

Ryan Kohn
  • 13,079
  • 14
  • 56
  • 81
Mujtaba Hassan
  • 2,495
  • 2
  • 20
  • 29
  • Hi Mujtaba. Were you able to implement this? – Vivek Chandraprakash Aug 07 '13 at 21:39
  • First use CORS to prevent unwanted hit from others domain. Then send a valid Forms Authentication cookie along with the request and finally authorize request by token. This combination always make your web api secure and optimized. – Majedur Jan 27 '19 at 04:27

4 Answers4

137

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"
Ryan Kohn
  • 13,079
  • 14
  • 56
  • 81
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Is it going to maintain a session for Android application? – Mujtaba Hassan Jun 13 '12 at 12:42
  • Got the point but can you please post a sample code for the second point. Thanks for your answer. – Mujtaba Hassan Jun 13 '12 at 12:48
  • 2
    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. – Darin Dimitrov Jun 13 '12 at 12:49
  • 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 – Mujtaba Hassan Jun 13 '12 at 12:52
  • Sure, I have posted an example. – Darin Dimitrov Jun 13 '12 at 13:28
  • 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? – Abhijit-K Jun 13 '12 at 13:31
  • You could use custom headers for tokens. But you will be reinventing wheels. That's what the forms authentication cookies already do for you. – Darin Dimitrov Jun 13 '12 at 13:38
  • 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 Jun 13 '12 at 13:47
  • 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? – Abhijit-K Jun 13 '12 at 13:58
  • 2
    Also see this question (and answer) about using HTTP basic authentication: http://stackoverflow.com/questions/10987455/authenticating-via-http-using-the-web-api-in-asp-net – Jim Harte Jun 13 '12 at 15:16
  • 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. – Abhijit-K Jun 14 '12 at 04:38
  • Thanks Darin Dimitrov, that really helped. :) – Mujtaba Hassan Jun 14 '12 at 05:08
  • There are a number of disadvantages to using FormsAuthentication as listed here: http://www.asp.net/web-api/overview/security/forms-authentication – markmnl Jul 24 '14 at 02:44
12

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.

user2293998
  • 149
  • 1
  • 5
0

Use this code and access database

[HttpPost]
[Route("login")]
public IHttpActionResult Login(LoginRequest request)
{
       CheckModelState();
       ApiResponse<LoginApiResponse> response = new ApiResponse<LoginApiResponse>();
       LoginResponse user;
       var count = 0;
       RoleName roleName = new RoleName();
       using (var authManager = InspectorBusinessFacade.GetAuthManagerInstance())
       {
           user = authManager.Authenticate(request); 
       } reponse(ok) 
}
slfan
  • 8,950
  • 115
  • 65
  • 78
0

Select Single user Authentication - this will create database of Authentication to create users in App_Data>*.mdf sql file The regsitered users are stored in this table dbo.AspNetUsers --> Please note that password is stored in hash format here in this table using this it will be useful to login in the website that is attributed with [Autorize] --> it will call the auto created page function ==> /api/account/register

The registration page script can be used as below $(document).ready(function () {

        $('#lnkClose').click(function () {
             $('#divErrorText').hide('fade');
        });

        $('#btnRegister').click(function () {
            $.ajax({
                url: '/api/account/register',
                method: 'POST',
                data: {
                    Email: $('#txtEmail').val(),
                    Password: $('#txtPassword').val(),
                    ConfirmPassword: $('#txtConfirmPassword').val()

                },
                success: function () {
                    $('#successModel').modal('show');
                },
                error: function (jqXHR) {
                    $('#divErrorText').text(jqXHR.responseText);
                    $('#divError').show('fade');
                    $('#divErrorText').show('fade');
                }
            })



        });
    });