Search code examples
angularjsauthenticationasp.net-web-api2access-token

How to store user information in one place after login and access in multiple WEB API controllers


I am working on Web API with AngularJS. I had implemented Web API token mechanism few days ago and able to login the application using the access token. I have used external DB table instead of ASP.NET identity table to authorize user.

I want to store user information in class so that it can be accessed easily from different controllers after User logged in. Currently I am using ClaimsIdentity in Controller Class to get the user information.

UserIdentityViewModel.cs

public class UserIdentityViewModel
    {
        public string UserName { get; set; }
        public Guid UserId { get; set; }
    }

Startup.cs

public class Startup
    {
        public void Configuration(IAppBuilder app)
        {                
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            var myProvider = new AuthorizationServerProvider();
            OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/Token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                Provider = myProvider
            };
            app.UseOAuthAuthorizationServer(options);

            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
        }
    }

AuthorizationServerProvider.cs

public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated(); // 
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {           
            string userId = context.UserName;
            string password = context.Password;

            EmployeeAccessBLL chkEmpAccessBLL = new EmployeeAccessBLL();
            EmployeeAccessViewModel vmEmployeeAccess = chkEmpAccessBLL.CheckEmployeeAccess(Convert.ToInt32(userId), password);

            if(vmEmployeeAccess != null)
            {
                var identity = new ClaimsIdentity(context.Options.AuthenticationType);
                identity.AddClaim(new Claim("username", vmEmployeeAccess.EmpName));
                identity.AddClaim(new Claim("userid", Convert.ToString(vmEmployeeAccess.EmployeeId)));

                UserIdentityViewModel vmUser = new UserIdentityViewModel();
                vmUser.UserId = vmEmployeeAccess.EmployeeId;
                vmUser.UserName = vmEmployeeAccess.EmpName;

                context.Validated(identity);
            }
            else
            {
                context.SetError("invalid_grant", "Provided username and password is incorrect");
                return;
            }
        }       
    }

EventController.cs

 public class StreamEventController : ApiController
    {
        [Authorize]
        [Route("api/addevent")]
        [HttpPost]
        public List<string> AddEvent(StreamEventViewModel vmEvent)
        {
            //Able to get User Information from Identity.Claims
            var identity = (ClaimsIdentity)User.Identity;
            string userId = identity.Claims
                            .Where(c => c.Type == "userid")
                            .Select(c => c.Value).FirstOrDefault();

            //Not able to get User Information from following as new object instance gets created
            UserIdentityViewModel vmUser = new UserIdentityViewModel();

            vmEvent.CreatedBy = vmUser.UserId;
            vmEvent.ModifiedBy = vmUser.UserId;
       }
}

Instead of writing "Identity.Claims" in each method of every controller I want to use simple get/set approach or any other methodology to get User Information . The use of Static class is also bad in my opinion as it will store one information of user and multiple user login information gets missed.

Please help me and share with me the best approach that has been used in other Web API projects for login.


Solution

  • You can add a private variable which will be set in the constructor of the controller, like this:

    // Should only be used in protected methods.
    private ClaimsIdentity ThisUser = null;
    
    public MyController()
    {
        if (User.Identity.IsAuthenticated)
            ThisUser = (ClaimsIdentity)User.Identity;
    }
    
    [Authorize]
    [Route("api/addevent")]
    [HttpPost]
    public List<string> AddEvent(StreamEventViewModel vmEvent)
    {
        string userId = ThisUser.FindFirstValue("userid");
    
    }
    

    Or create a User class where you load all properties:

    private UserClass ThisUser = null;
    
    public MyController()
    {
        if (User.Identity.IsAuthenticated)
            ThisUser = new UserClass(User);
    }
    
    [Authorize]
    [Route("api/addevent")]
    [HttpPost]
    public List<string> AddEvent(StreamEventViewModel vmEvent)
    {
        string userId = ThisUser.UserId;
    
    }
    

    Where UserClass is something like:

    public class UserClass
    {
        public string UserId { get; private set; }
    
        public UserClass(IPrincipal user)
        {
            UserId = user.FindFirstValue("userid");
        }
    }
    

    But this is just overhead for the same thing. You can consider to move things to an extension. In that case you get something like:

    public static class RequestExtensions
    {
        public static UserClass GetUser(this HttpRequestMessage request)
        {
            return new UserClass(request.GetOwinContext().Authentication.User);
        }
    
        public static ClaimsIdentiy GetUser2(this HttpRequestMessage request)
        {
            return new (ClaimsIdentity)request.GetOwinContext().Authentication.User;
        }
    }
    

    Which you can call:

    [Authorize]
    [Route("api/addevent")]
    [HttpPost]
    public List<string> AddEvent(StreamEventViewModel vmEvent)
    {
        string userId = Request.GetUser.UserId;
    
        string userId2 = Request.GetUser2.FindFirstValue("userid");
    
    }
    

    I think I would go for Request.GetUser2.FindFirstValue("userid");

    The code is meant to give you an idea. I didn't test the code but I think it should work.