dotnet / dotnet/aspnetcore

PolicyEvaluator mutates AuthenticateResult, deletes AuthenticationProperties when policy uses AuthenticationSchemes

Open
#47,059 4 comments 5 reactions 0 assignees View on GitHub
area-auth enhancement
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.