Reputation: 127
my controller method consumes a JWT token which was enabled in ConfigureServices method in Startup.cs
.AddJwtBearer(options => { // some code }; });
The CreateUser() action in UserController consumes this token
[HttpPost, Authorize("JWT")]
public SaveResponse CreateUser(IUnitOfWork uow, UserRequest request) {
return new UserRepository().Create(uow, request);
}
The problem is as follows: A few methods deeper upon creating a new user, there's a method HasPermission() that checks logged in user's Administration permissions. However, in this particular case using JWT, there's no logged in user. The presence of valid JWT suffices. I am going to modify this HasPermission() in a way, that it also accepts JWT.
At CreateUser method level, the JWT is present inside HttpRequest's 'Authorization' header.
The question is - How can I deliver this JWT token to like a 8th method in a chain of methods executed by UserRepository().Create(uow, request) ? Is there a way to pull this off without modifying parameters of these methods?
thank you
Upvotes: 0
Views: 311
Reputation: 11091
This particular case using JWT, there's no logged in user. The presence of valid JWT suffices.
Assuming you have the auth middleware enabled, if the request is able to reach CreateUser
action, then [Authorize]
attribute makes sure that the token is valid. So you don't need to do another validation.
Second, you shouldn't flow the token down to the repository. Keep HTTP and data retrieval concerns separate.
The solution to not "passing a parameter down 8 level" is to use dependency injection throughout your application and let it keep track of dependencies.
To access the current user inside your repo, create an interface that exposes the user:
interface IPrincipalAccessor {
ClaimsPrincipal? Principal { get; }
}
then implement this with IHttpContextAccessor
private class HttpPrincipalAccessor : IPrincipalAccessor
{
private IHttpContextAccessor _httpContextAccessor;
public HttpPrincipalAccessor(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public ClaimsPrincipal? Principal => _httpContextAccessor?.HttpContext?.User;
}
You need to enable IHttpAccessor
and register this class in DI:
services.AddHttpContextAccessor();
services.AddScoped<IPrincipalAccessor, HttpPrincipalAccessor>();
Now you can inject this interface in your repo and use the user claims. The repo isn't aware, and doesn't care where the user comes from, it just needs to know the current user.
class MyRepo
{
private IPrincipalAccessor _principalAccessor;
public MyRepo(IPrincipalAccessor principalAccessor)
{
_principalAccessor = principalAccessor;
}
Task Create(/* some parameters */)
{
var user = _principalAccessor.Principal;
if (user.HasClaim("eyes", "2"))
{
// user has two eyes
}
// ...
}
}
But the problem with your code is that you're not using dependency injection, so you need to inject your repo, instead of new
ing it up.
Upvotes: 1
Reputation: 142288
If you use DI to instantiate service dependecies you can register IHttpContextAccessor
via services.AddHttpContextAccessor()
and use it to get information about request:
public SomeService(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public void SomeServiceMethod()
{
var auth = _contextAccessor.HttpContext.Request.Headers[HeaderNames.Authorization].ToString(); // possibly will need to remove scheme from the header
}
Upvotes: 1