runtime: WindowsIdentity.RunImpersonated sending wrong credentials
Hi all!
I’m experiencing the problem described in issue https://github.com/dotnet/runtime/issues/38414.
I have a ASP.NET Core app with a Middleware, that authenticate user via windows authentication and runs under his account an http-request to get a jwt-token.
Startup.cs
public class Startup
{
//...
//omitted for brevity
//...
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedMemoryCache();
services.AddSession(conf => conf.Cookie.MaxAge = TimeSpan.FromMinutes(30));
services.AddAuthentication(IISDefaults.AuthenticationScheme).AddIdentityServerAuthentication(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Authority = _configuration.GetValue<string>("IdentityServer");
options.SupportedTokens = SupportedTokens.Both;
options.ApiSecret = _configuration.GetValue<string>("ClientSecret");
options.RequireHttpsMetadata = false;
});
services.AddHttpClient(ServiceConsts.ImpersonatedHttpClientName)
.ConfigurePrimaryHttpMessageHandler(_ =>
new SocketsHttpHandler()
{
UseProxy = false,
Credentials = CredentialCache.DefaultCredentials
});
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
//...
//omitted for brevity
//...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSession();
app.UseAuthentication();
app.UseAuthorization();
app.UseForwardedHeaders();
app.Use(async (context, next) =>
{
string ip = context.Connection.RemoteIpAddress.ToString();
context.Request.Headers[ForwardedIpToken] = ip;
await next.Invoke();
});
app.UseMiddleware<WinTokenMiddleware>();
app.UseOcelot().Wait();
}
//...
//omitted for brevity
//...
}
WinTokenMiddleware.cs
[SupportedOSPlatform("windows")]
public class WinTokenMiddleware
{
private readonly RequestDelegate _next;
private readonly IConfiguration _configuration;
private readonly IHttpClientFactory _httpClientFactory;
private static readonly ILogger Logger = Log.ForContext<WinTokenMiddleware>();
private const string TokenSessionKey = "AuthToken";
public WinTokenMiddleware(RequestDelegate next, IConfiguration configuration, IHttpClientFactory httpClientFactory)
{
_next = next;
_configuration = configuration;
_httpClientFactory = httpClientFactory;
}
// ReSharper disable once UnusedMember.Global
public async Task InvokeAsync(HttpContext context)
{
//...
//... omitted for brevity
//...
AuthenticateResult winAuthenticateResult = await context.AuthenticateAsync(IISServerDefaults.AuthenticationScheme);
if (!winAuthenticateResult.Succeeded)
{
await context.ChallengeAsync(IISDefaults.AuthenticationScheme);
return;
}
if (!(winAuthenticateResult?.Principal?.Identity is WindowsIdentity windowsIdentity))
{
Logger.Warning("Couldn't get user windows account!'");
await WriteErrorResponse(context,
"Couldn't get user windows account! Please enable windows authentication on server.");
return;
}
//...
//... omitted for brevity
//...
TokenResponse tokenResponse = await WindowsIdentity.RunImpersonated(
windowsIdentity.AccessToken,
() =>
{
Logger.Debug($"Current user {WindowsIdentity.GetCurrent().Name}");
HttpClient impersonatedClient = _httpClientFactory.CreateClient(ServiceConsts.ImpersonatedHttpClientName);
//получаем токен под пользователем
return impersonatedClient.RequestTokenAsync(new TokenRequest()
{
Address = disco.TokenEndpoint,
ClientId = _configuration.GetValue<string>("ClientId"),
ClientSecret = _configuration.GetValue<string>("ClientSecret"),
GrantType = "windows"
});
});
//...
//... omitted for brevity
//...
}
}
The Code above gets jwt-Token authenticated under user account with windows authentication. When we deploy application, we see that users get tokens of other users. For example, if user A gets authenticated correctly and received his token, and few seconds later comes user B, he gets authenticated by user A and therefore gets the wrong token.
If I set PooledConnectionLifetime to TimeSpan.Zero, it works as expected
services.AddHttpClient(ServiceConsts.ImpersonatedHttpClientName)
.ConfigurePrimaryHttpMessageHandler(_ =>
new SocketsHttpHandler()
{
UseProxy = false,
Credentials = CredentialCache.DefaultCredentials,
PreAuthenticate = false,
PooledConnectionLifetime = TimeSpan.Zero
});
The service is running on net5.0 on windows server 2016, IIS 10.
Is this a bug or am I doing it the wrong way?
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 33 (18 by maintainers)
Ok, I’ll do it in one or two days. I’ll try to create a minimal solution to check it.
@karelz, ok, I’ll install it, thank you!