Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authentication approach to be use in asp.net Web api and angular js

I am creating 1 website using Web api and angular js and i am so much confused about Authentication to be used in my web site.

I have created one login.js in which there would be my Login method which will post my Username/Emailid and password to my Web Api and the method in web api will authenticate that user.

Code:

$scope.Login()
{
  $.post("api/Authentication/Login",Username,Password)
  {
  }
}

Web api code:

[Route]
Public Task<object> Login([FromBody] username,password)
{
   //method to authenticate user from database.
   //Now the tricky parts comes like after authenticating user should i 
   //just store the user id in my session like this Session["userid]=id or
   //Should i go for storing whole user object in my cookie as i have read 
   //that storing data in session is very bad idea and disaster too as session is very memory
   //and so i think i would go for cookie which will be saved on client side.
   //but what if cookie isnt supported by browser??
}

Using session is disaster as pointed out by Darin Dimitrov in his answer and comments. So i have decided to use cookie as per this answer and one of the ecommerce site that is Nop Commerce uses cookie too to store currently login customer object as per this question and answer Question

I am following this code suggested by LukeP in this Question for authentication purpose and maintaining currenlty login user object across my whole appilcation.

I have read about asp.net claim identity too but dont know whether i can use it in my asp.net web api and angular js.

So can anybody tell me whats the correct approach to use for authentication in asp.net web api and angular js and what all the changes to be done in LukeP code to work with web api and angular js??

Can anybody explain me about this appraoch which i have pointed above with some detail description and some codes too as it can help me and some others too if they are searching for the same.

Later i will offer 100 bounty to the answer addressing all above concern with some codes.

like image 720
Learning-Overthinker-Confused Avatar asked Mar 12 '23 21:03

Learning-Overthinker-Confused


1 Answers

The best way to do it is with token authentication. In summary it works like this:

  • A POST /api/login route on the server takes in a username + password, checks that they are valid against the database, then generates and returns a refresh token (which can just be a random string or GUID). The refresh token is also stored in the database next to the user, overwriting the previous refresh token
  • A GET /api/access-token route on the server takes in a username + refresh token, checks that they match in the database, then generates and returns an access token
  • Any other /api/* routes require a valid access token to be in the header of the request, otherwise they assume the user does not have a valid login

The access token is a data object that has been encrypted using a secret key that only the server knows. It should contain the username, an expiry date (usually ~10mins from when the token was generated), and any permissions or misc data about the user. Because it is encrypted with a secret key, it cannot be forged by an attacker.

You will need to implement these routes on your server.

If you are using OWIN, here is how you can use the Microsoft.Owin.Security.OAuth NuGet package to do the encryption bit for you:

Have this in your startup configuration:

using System.Web.Http;
using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using Owin;

[assembly: OwinStartup(typeof(MyProject.Startup))]
namespace MyProject
{
    public class Startup
    {
        public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }

        public void Configuration(IAppBuilder app)
        {
            var config = new HttpConfiguration();

            OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
            app.UseOAuthBearerAuthentication(OAuthBearerOptions);

            // Configure Web API to use only bearer token authentication.
            config.SuppressDefaultHostAuthentication();
            config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

            app.UseWebApi(config);
        }
    }
}

Then, you can generate a ticket (which is the unencrypted access token) and encrypt it like this:

var identity = new ClaimsIdentity(new[] {
    new Claim(ClaimTypes.Email, "users email"),
    // other business specific claims e.g. "IsAdmin"
});
var ticket = new AuthenticationTicket(identity, new AuthenticationProperties(
    {
        ExpiresUtc = DateTime.UtcNow.AddMinutes(10)
    }));
var accessToken = MyProject.Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);

In Angular you need to setup a way to login, a way to get a new access token when it expires, and a way to pass the access token in the header of every API request. I recommend storing the refresh token and access token in local storage (or cookie for old browsers), and using $httpProvider.interceptors.push to add an interceptor for every $http call. The interceptor can then add the access token to the header like this: config.headers['Authorization'] = 'Bearer ' + accessToken;

Define the interceptor in angular:

angular.module('app').service('authenticationInterceptor', ['$q', function($q) {
    this.request = function(config) {
        var accessToken = // ... get the access token from local storage / cookie
        config.headers['Authorization'] = 'Bearer ' + accessToken;
        return config;
    };
}]);

Add it to $httpProvider:

angular.module('app').config(['$httpProvider', ($httpProvider: ng.IHttpProvider) => {
    $httpProvider.interceptors.push('authenticationInterceptor');
}]);
like image 102
Mike Jerred Avatar answered Apr 07 '23 13:04

Mike Jerred