Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Web API Login method

I'm wanting to build a RESTful web service using ASP.NET Web API that third-party developers will use to access my application's data.

In Visual Studio I decided to create a new ASP.NET project. I followed this tutorial but I choose a different template: Web API template. I use a MySQL database with the standard user role tables as explained in the tutorial.

The template come with many very interesting methods to register a new user but there is no default Login request. I wrote this without understanding what I'm doing:

    // POST api/Account/Login
    [Route("Login")]
    public IHttpActionResult Login(LoginBindingModel model)
    {
        ClaimsIdentity ci = new ClaimsIdentity();
        // ...
        // ...
        Authentication.SignIn(ci);
        return Ok();
    }

I've read quite a lot about security without finding a good sample with documentation explaining how it works. It seems to be incredibly difficult to implement a simple login method in Web API.

Could you explain me why there is no login method in this template. Do you have an sample of login method. And what should I sent back to the client application to authenticate the request. Is this working with a token ?

like image 550
Bastien Vandamme Avatar asked Oct 22 '14 08:10

Bastien Vandamme


4 Answers

Usually what you do is implement the login logic in that method, and return a token which will be then validated on each call to your api.

You can read this for more information

http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/

like image 84
dariogriffo Avatar answered Oct 02 '22 22:10

dariogriffo


If you have created a new ASP.NET Web Application -> Web API -> Change Authentication -> Individual User Accounts. Have a look at App_Start -> Startup.Auth.cs.

It should contain something like this:

PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
    // In production mode set AllowInsecureHttp = false
    AllowInsecureHttp = true
};

// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);

This means that you can send a request for an access token, example request:

enter image description here

You can then verify that the access token works:

enter image description here

With this token you can now access all protected resources that the user has access to.

like image 36
Ogglas Avatar answered Oct 02 '22 23:10

Ogglas


If you are going to build an API for third party developers then you need to secure it using OAuth 2.0 flows, I've written detailed post as @dariogriffo directed you which implements the resource owner password credentials flow which good for your case.

You do not need to create an end point for login, you will configure the API using Owin middle-wares to issue OAuth bearer tokens to the users when calling an end point such as "/token", and then the users keep sending this token along with each request in the Authorization header. Read more about this token based authentication.

like image 4
Taiseer Joudeh Avatar answered Oct 02 '22 22:10

Taiseer Joudeh


For Others, a helper class, to begin with:

namespace WeBAPITest
{



#region Using Statements:



using System.Net.Http;
using System.Collections.Generic;

using Newtonsoft.Json;



#endregion



public class HttpWebApi
{



#region Fields:



private static readonly HttpClient client = new HttpClient();



#endregion



#region Properties:



/// <summary>
/// The basr Uri.
/// </summary>
public string BaseUrl { get; set; }



/// <summary>
/// Username.
/// </summary>
protected internal string Username { get; set; }



/// <summary>
/// Password.
/// </summary>
protected internal string Password { get; set; }



/// <summary>
/// The instance of the Root Object Json Deserialised Class.
/// </summary>
internal Rootobject Authentication { get; set; }



/// <summary>
/// The Access Token from the Json Deserialised Login.
/// </summary>
public string AccessToken { get { return Authentication.access_token; } }



#endregion



public HttpWebApi(string baseurl)
{

    // Init Base Url:
    BaseUrl = baseurl;
}



/// <summary>
/// Get from the Web API.
/// </summary>
/// <param name="path">The BaseUrl + path (Uri.Host + api/Controller) to the Web API.</param>
/// <returns>A Task, when awaited, a string</returns>
public async System.Threading.Tasks.Task<string> Get(string path)
{

    if (Authentication.access_token == null)
    throw new System.Exception("Authentication is not completed.");

    // GET
    client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Authentication.access_token);
    return await client.GetStringAsync(BaseUrl + path);
}



/// <summary>
/// Logs In and populates the Authentication Variables.
/// </summary>
/// <param name="username">Your Username</param>
/// <param name="password">Your Password</param>
/// <returns>A Task, when awaited, a string</returns>
public async System.Threading.Tasks.Task<string> Login(string username, string password)
{

    // Set Username:
    Username = username;

    // Set Password:
    Password = password;

    // Conf String to Post:
    var Dic = new Dictionary<string, string>() { { "grant_type", "password" }, { "username", "" }, { "password", "" } };
    Dic["username"] = username;
    Dic["password"] = password;

    // Post to Controller:
    string auth = await Post("/Token", Dic);

    // Deserialise Response:
    Authentication = JsonConvert.DeserializeObject<Rootobject>(auth);

    return auth;
}



/// <summary>
/// Post to the Web API.
/// </summary>
/// <param name="path">The BaseUrl + path (Uri.Host + api/Controller) to the Web API.</param>
/// <param name="values">The new Dictionary<string, string> { { "value1", "x" }, { "value2", "y" } }</param>
/// <returns>A Task, when awaited, a string</returns>
public async System.Threading.Tasks.Task<string> Post(string path, Dictionary<string, string> values)
{

    // Add Access Token to the Headder:
    if (Authentication != null)
    if (Authentication.access_token != "")
        client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Authentication.access_token);

    // Encode Values:
    var content = new FormUrlEncodedContent(values);

    // Post and get Response:
    var response = await client.PostAsync(BaseUrl + path, content);

    // Return Response:
    return await response.Content.ReadAsStringAsync();
}



/// <summary>
/// Register a new User.
/// </summary>
/// <param name="username">Your Username, E-Mail</param>
/// <param name="password">Your Password</param>
/// <returns>A Task, when awaited, a string</returns>
public async System.Threading.Tasks.Task<string> Register(string username, string password)
{

    // Register: api/Account/Register
    var Dic = new Dictionary<string, string>() { { "Email", "" }, { "Password", "" }, { "ConfirmPassword", "" } };
    Dic["Email"] = username;
    Dic["Password"] = password;
    Dic["ConfirmPassword"] = password;

    return await Post("api/Account/Register", Dic);
}
}



/// <summary>
/// For Json Deserialisation.
/// </summary>
internal class Rootobject
{

/// <summary>
/// The Web Api Access Token. Gets added to the Header in each communication.
/// </summary>
public string access_token { get; set; }



/// <summary>
/// The Token Type
/// </summary>
public string token_type { get; set; }



/// <summary>
/// Expiry.
/// </summary>
public int expires_in { get; set; }



/// <summary>
/// The Username.
/// </summary>
public string userName { get; set; }



/// <summary>
/// Issued.
/// </summary>
public string issued { get; set; }



/// <summary>
/// Expiry.
/// </summary>
public string expires { get; set; }
}
}

Particularly designed for the default, unedited Web Api Template in Visual Studio.

Then:

HttpWebApi httpWebApi = new HttpWebApi("http://localhost/");
await httpWebApi.Login("email", "password");

richTextBox1.AppendText(await httpWebApi.Get("api/Account/UserInfo") + Environment.NewLine);

Hope this helps others some!

like image 1
Rusty Nail Avatar answered Oct 02 '22 23:10

Rusty Nail