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:
UserId
Is there a different approach that I can take to simplify this?
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.