Home > Enterprise >  Passing Auth0 User Id to service layer using middelware in an ASP.NET Core 6.0 Web API
Passing Auth0 User Id to service layer using middelware in an ASP.NET Core 6.0 Web API

Time:12-07

I am trying to find a way to get a UserId string to my service layer without having to modify the request passed from the FE.

I am using Auth0 to authenticate users for an application. I use middleware to access the users id and set it to the context.items enumerable using this code:

app.Use(async (context, next) =>
{
    var userIdClaim = context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
    if (userIdClaim is not null)
    {
        context.Items["UserId"] = userIdClaim.Value;
    }

    await next.Invoke();
});

And this works fine, so I get get the UserId in my controller using

(string)HttpContext.Items["UserId"];

Currently, I modified the request object received from the front end to include a nullable string UserId so I can pass it along with this code:

[HttpGet]
public async Task<AddressResponse.GetIndex> GetIndexAsync([FromQuery] AddressRequest.GetIndex request)
{
    request.UserId = (string)HttpContext.Items["UserId"];
    return await addressService.GetIndexAsync(request);
}

public class AddressRequest 
{
    public class GetIndex : AuthenticatedData {}
}

public class AuthenticatedData
{
    public string? UserId { get; set; }
}

Now for my detail and delete functions I only pass a primitive int to the route/body so I cannot modify it to include a UserId. Should I make every request an object with UserId even if it seems a bit overkill? Or is there another way to receive the UserId in the service layer?

I am using a shared interface between my Blazor app and the api, so I cannot just modify the interface to include a UserId (I think).

And is there a way to set the request UserId in the middleware itself and not with every controller function?

CodePudding user response:

First of all I would argue that in general case you should not add any properties on incoming model that you are not expecting to receive from the frontend otherwise your service can become a subject for kind of over posting attacks (for example for some reason in one service you forgot to set the UserId from the claims and malicious user provided one).

As for answering your question - one option is to try looking into custom model binding and implementing a special binder for the UserId property (see this answer).

Personally I would go with wrapping IHttpContextAccessor into some service like IUserIdProvider and use it in services. Something along this lines:

interface IUserIdProvider
{
    string? GetUserId();
}

class UserIdProvider : IUserIdProvider
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public UserIdProvider(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }
    public string? GetUserId() => _httpContextAccessor.HttpContext?.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
}

builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<IUserIdProvider, UserIdProvider>();

Though see the notes on IHttpContextAccessor usage. Alternatively you can create pair of interfaces IUserIdSetter and IUserIdProvider (implemented by the same scoped class) and set user id for provider in the middleware.

  • Related