-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
Hi all!
I'm experiencing the problem described in issue #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?