Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get user context during Web Api calls?

I have an web front end calling an ASP Web Api 2 backend. Authentication is managed with ASP Identity. For some of the controllers I'm creating I need to know the user making the call. I don't want to have to create some weird model to pass in including the user's identity (which I don't even store in the client).

All calls to the API are authorized using a bearer token, my thought is the controller should be able to determine the user context based on this but I do not know how to implement. I have searched but I don't know what I'm searching for exactly and haven't found anything relevant. I'm going for something like...

public async Task<IHttpActionResult> Post(ApplicationIdentity identity, WalkthroughModel data)

Update

I found the below which looked very promising... but the value is always null! My controller inherits from ApiController and has an Authorize header.

var userid = User.Identity.GetUserId();

Update 2

I have also tried all of the solutions in Get the current user, within an ApiController action, without passing the userID as a parameter but none work. No matter what I am getting an Identity that is valid and auth'd, but has a null UserID

Update 3

Here's where I'm at now.

    [Authorize]
    [Route("Email")]
    public async Task<IHttpActionResult> Get()
    {
        var testa = User.Identity.GetType();
        var testb = User.Identity.GetUserId();
        var testc = User.Identity.AuthenticationType;
        var testd = User.Identity.IsAuthenticated;


        return Ok();
    }
testa = Name: ClaimsIdentity,
testb = null,
testc = Bearer,
testd = true

The user is obviously authenticated but I am unable to retrieve their userID.

Update 4

I found an answer, but I'm really unhappy with it...

ClaimsIdentity identity = (ClaimsIdentity)User.Identity;
string username = identity.Claims.First().Value;

That gets me the username without any db calls but it seems very janky and a pain to support in the future. Would love if anyone had a better answer.

What if I need to change what claims are issued down the road? Plus any time I actually need the user's id I have to make a db call to convert username to ID

like image 893
Joshua Ohana Avatar asked Feb 22 '15 12:02

Joshua Ohana


People also ask

What is context in Web API?

The HttpContext encapsulates all the HTTP-specific information about a single HTTP request. When an HTTP request arrives at the server, the server processes the request and builds an HttpContext object. This object represents the request which your application code can use to create the response.

What is HttpContext user?

The User property provides programmatic access to the properties and methods of the IPrincipal interface. Because ASP.NET pages contain a default reference to the System. Web namespace (which contains the HttpContext class), you can reference the members of HttpContext on an .

How do you set HttpContext user identity for an application manually?

You can achieve this by manually settings HttpContext. User: var identity = new ClaimsIdentity("Custom"); HttpContext. User = new ClaimsPrincipal(identity);


2 Answers

A common approach is to create a base class for your ApiControllers and take advantage of the ApplicationUserManager to retrieve the information you need. With this approach, you can keep the logic for accessing the user's information in one location and reuse it across your controllers.

public class BaseApiController : ApiController
{
        private ApplicationUser _member;

        public ApplicationUserManager UserManager
        {
            get { return HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>(); }
        }

        public string UserIdentityId
        {
            get
            {
                var user = UserManager.FindByName(User.Identity.Name);
                return user.Id; 
            }
        }

        public ApplicationUser UserRecord
        {
            get
            {
                if (_member != null)
                {
                    return _member ; 
                }
                _member = UserManager.FindByEmail(Thread.CurrentPrincipal.Identity.Name); 
                return _member ;
            }
            set { _member = value; }
        }
}
like image 199
jake Avatar answered Oct 25 '22 11:10

jake


I use a custom user authentication (I dont use AspIdentity because my existing user table fields was far different from IdentityUser properties) and create ClaimsIdentity passing my table UserID and UserName to validate my bearer token on API calls.

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        User user;
        try
        {
            var scope = Autofac.Integration.Owin.OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext);
            _service = scope.Resolve<IUserService>();

            user = await _service.FindUserAsync(context.UserName);

            if (user?.HashedPassword != Helpers.CustomPasswordHasher.GetHashedPassword(context.Password, user?.Salt))
            {
                context.SetError("invalid_grant", "The user name or password is incorrect.");
                return;
            }
        }
        catch (Exception ex)
        {
            context.SetError("invalid_grant", ex.Message);
            return;
        }

        var properties = new Dictionary<string, string>()
        {
            { ClaimTypes.NameIdentifier, user.UserID.ToString() },
            { ClaimTypes.Name, context.UserName }
        };

        var identity = new ClaimsIdentity(context.Options.AuthenticationType);
        properties.ToList().ForEach(c => identity.AddClaim(new Claim(c.Key, c.Value)));

        var ticket = new AuthenticationTicket(identity, new AuthenticationProperties(properties));

        context.Validated(ticket);
        context.Request.Context.Authentication.SignIn(identity);
    }

And how I use the ClaimsIdentity to retrieve my User table details on User ApiController Details call.

    [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
    [Route("Details")]
    public async Task<IHttpActionResult> Details()
    {
        var user = await _service.GetAsync(RequestContext.Principal.Identity.GetUserId<int>());
        var basicDetails = Mapper.Map<User, BasicUserModel>(user);

        return Ok(basicDetails);
    }

Notice the ClaimTypes.NameIdentifier = GetUserId() and ClaimTypes.Name = GetUserName()

like image 28
pampi Avatar answered Oct 25 '22 09:10

pampi