PW_100190
PW_100190

Reputation: 31

How to get User ID inside a separate assembly

I'm working on an application which has two projects:

  1. Core - houses the data access layer using repository pattern and domain-driven design
  2. UI - using ASP.Net MVC. Currently, I am able to get the current logged in user's info(id, name, etc..) inside the UI controller via the User property like this:

    using Microsoft.AspNet.Identity;
    
    public class ExamController : Controller
    {
       IExaminationRepository _repository;
    
       public ExamController()
       {
         _repository = RepositoryFactory.Get<IExaminationRepository>();
       }
    
       [HttpPost]
       [Authorize(Roles = "Examiner")]
       public ActionResult Create(ExamViewModel viewModel)
       {
          try
          {
            ExaminationDomain domain = Mapper.Map<ExamViewModel, ExaminationDomain>(viewModel);
    
            //TODO: Move this to the repository
            domain.AuthorId = User.Identity.GetUserId();
    
            _repository.Add(domain);
    
            return RedirectToAction("Index");
        }
        catch
        {
            return View();
        }
      }
    }
    

I would like to move the line: domain.AuthorId = User.Identity.GetUserId(); to my repository concrete implementation like this:

using Microsoft.AspNet.Identity;
using System.Security.Principal;

internal class ExaminationRepository
{
    public DBEntities context;
    public IPrincipal User;

    public ExaminationRepository(DBEntities context)
    {
        this.context = context;
        //I'd like to instantiate User property here:
        this.User = "not sure what to instantiate with";
    }

    public void Add(ExaminationDomain domain)
    {
        Examination newExam = Mapper.Map<ExaminationDomain, Examination>(domain);

        newExam.AuthorId = User.Identity.GetUserId();

        newExam.CreatedBy = User.Identity.Name;

        newExam.CreatedDate = DateTime.Now;

        context.Examinations.Add(newExam);

        context.SaveChanges();

    }

But I am not sure what to instantiate the User property to in the constructor. I've read some suggestions to use WindowsIdentity.GetCurrent().User instead of creating a user property but this doesn't contain the user id, only user name.

Any other suggestions on getting user info?

I'd really appreciate some help on this..

Thanks,

Upvotes: 3

Views: 2887

Answers (4)

Ahmad Firdaus
Ahmad Firdaus

Reputation: 61

You need to instantiate this.User with identity information of current thread:

this.User = System.Threading.Thread.CurrentPrincipal;
var currentIdentity = (System.Security.Claims.ClaimsIdentity)User.Identity;
var userId = currentIdentity.Claims
    .Where(p => p.Type.EndsWith("nameidentifier")).Single().Value;

Note that the type of CurrentPrincipal.Identity is an IIdentity. You can cast it to System.Security.Claims.ClaimsIdentity, which contains a property named Claims. This property contains all your claims, including userid and 3rd party token (e.g. Facebook token).

To retrieve UserId, find a claims with Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"

Upvotes: 0

SHM
SHM

Reputation: 1952

use HttpContext class witch is a singleton class: first add a using to Microsoft.AspNet.Identity; and then you can do some thing like this:

HttpContext.Current.User.Identity.GetUserId();

since GetUserId is an extension method you have a reference to Microsoft.AspNet.Identity

but if you need to access user information in several places of you app I suggest to have a wrapper class with properties that you need and instantiate when user logs in then store it in session variable this way you have two benefits:

1- you don't need to query db to get username, email etc.. on each user info usage across the app.

2- you don't need assembly that your repository lives to aspnet identity.

Upvotes: 0

SebastianStehle
SebastianStehle

Reputation: 2459

I would decouple your repository from the httpcontext with a custom manager. For example I have a interface called IAUthenticationManager

public interface IAUthenticationManager
{
   string CurrentUserId();

   bool HasCurrentUserRole(string roleName),
}

Easy to test and fully decoupled.

Upvotes: 2

Wiktor Zychla
Wiktor Zychla

Reputation: 48250

This won't work easily since the repository can be used in many different contexts, even such contexts where user is not set. If you create a concrete dependency in your constructor, your repository will no longer be an independent data provider.

For example, referencing

HttpContext.Current.User.Identity

directly would create a dependency to a web context and the repository would be unusable in non-web contexts.

The best you could do is just to let the repository client provide this:

public void Add(ExaminationDomain domain, IPrincipal principal)
{
    Examination newExam = Mapper.Map<ExaminationDomain, Examination>(domain);

    newExam.AuthorId = principal.Identity.GetUserId();
    newExam.CreatedBy = principal.Identity.Name;
    newExam.CreatedDate = DateTime.Now;

    context.Examinations.Add(newExam);

    context.SaveChanges();
}

or (which could be possible)

public ExaminationRepository(DBEntities context, IPrincipal user)
{
    this.context = context;
    this.user    = user;
}

The latter case could still be correctly resolved by an IoC container if you tell the container how to resolve the dependency.

In a web context, you could set the container to resolve it to HttpContext.Current.User. In a non-web context, you could set the container to resolve it to WindowsIdentity.GetCurrent().User.

Upvotes: 0

Related Questions