PolicyEvaluator mutates AuthenticateResult, deletes AuthenticationProperties when policy uses AuthenticationSchemes
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 290
Description
### Is there an existing issue for this?
- [X] I have searched the existing issues
### Describe the bug
If you set `AuthenticationSchemes` on a policy, the `PolicyEvaluator` will remove `AuthenticationProperties` on the `AuthenticateResult` and return only an `ExpiresUtc` property.
https://github.com/dotnet/aspnetcore/blob/v7.0.3/src/Security/Authorization/Policy/src/PolicyEvaluator.cs#L36-L57
This makes `SaveToken` on `AddJwtBearer` useless if you try to retrieve/forward the `access_token` property for upstream requests.
This is with dotnet `6.0.405` but seems to still be present in .net7.
### Expected Behavior
I wouldn't expect authorization pipeline to mutate the `AuthenticateResult`.
Not sure I understand the design of this well enough to know why the `ClaimsPrincipal` is a merged result from `SecurityHelper.MergeUserPrincipal`. But it might make sense to attempt to merge the `AuthenticationProperties` rather than remove them entirely.
### Steps To Reproduce
Add a JwtHandler with `SaveToken = true`:
```
services.AddAuthentication()
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, opts =>
{
opts.SaveToken = true;
});
```
Create a policy with `AuthenticationSchemes` set, even if there are not multiple schemes defined:
```
.AddAuthorization(options =>
{
options.AddPolicy("some-policy", policy =>
{
policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
policy.RequireAuthenticatedUser();
});
});
```
Try to retrieve the saved tokens, in our case we're trying to use a delegating handler:
```
internal class AccessTokenHttpMessageHandler : DelegatingHandler
{
private readonly IHttpContextAccessor _contextAccessor;
public AccessTokenHttpMessageHandler(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
protected override async Task SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var feature = _contextAccessor.HttpContext.Features.Get();
var token = feature?.AuthenticateResult.Properties.GetTokenValue("access_token");
if (!string.IsNullOrEmpty(token))
{
request.Headers.Authorization =
new AuthenticationHeaderValue(JwtBearerDefaults.AuthenticationScheme, token);
}
return await base.SendAsync(request, cancellationToken);
}
}
```
The `access_token` will never be available on the `IAuthenticateResultFeature` in the handler.
### Exceptions (if any)
_No response_
### .NET Version
6.0.405
### Anything else?
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.