Search code examples
c#inheritanceasp.net-core-mvcasp.net-core-webapi

What is the correct way to get the user Id to services?


I have a Web API controller with an action method that allows users to update their account.

I have a DTO that I bind to the action method. For security reasons, I do not want to get the UserId from the POST, so the DTO that I bind to the action method does not have the UserId property. Instead, it just has the properties that the user is allowed to update:

public class UpdateAccountRequest 
{
    public required string Email { get; set; } = string.Empty;
    public required string Password { get; set; } = string.Empty;
}

I then have another DTO that inherits from this Request DTO where I add the UserId:

public class UpdateAccountWithUserIdRequest : UpdateAccountRequest  
{
    /// <summary>
    /// Make sure it is only set by the controller and not accepted as a parameter from the client.
    /// Do _NOT_ include in the JavaScript client.
    /// </summary>
    public required int UserId { get; set; } = 0;
}

I do not want to access HTTP context from within my services, so in the controller's action method, I create the UpdateAccountWithUserIdRequest object by copying the values from the bound UpdateAccountRequest and using the ClaimsPrincipal to get the UserId.

This approach works, but it feels like a hack:

  • I need to create a derived class that has the UserId
  • Make sure that I bind the correct DTO to the action method and set the values manually.

Is there a different approach that I can take to simplify this?


Solution

  • This should be handled by your API's security. The client should have no awareness of the User ID, yet it should be provided in an API message credential, eg a signed access token or an encrypted HTTP-only cookie.

    As a best practice, User IDs (and other secure values) should never be sent in unprotected request payloads, URL path segments or HTTP headers, where a malicious party might be able to alter them.

    Your API should use an authorization filter of some kind that produces a claims principal containing the User ID and other secure values. Claims should then be injectable into the API logic and used for authorization. In this manner you never need to create duplicate objects.