Приложение работает правильно при локальном запуске, но в производственной среде (за обратным прокси-сервером) корреляция файлы cookie теряются после аутентификации.
Код: Выделить всё
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddConsole();
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build();
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll",
builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
string SecurityLevelClaimPolicy = "SecurityLevelClaimPolicy";
string SecurityLevelClaim = "Claims";
Settings _settings = new Settings();
_settings.IdConfiguration = new IdConfiguration();
//_settings.ClientType = ClientType.ApiAccess;
_settings.ClientType = ClientType.ApiAccessForMultiTenantClient;
_settings.ApiAudience1 = "URL";
_settings.IdConfiguration = new IdConfiguration();
_settings.IdConfiguration.ClientId = "clientid";
_settings.IdConfiguration.RsaPrivateKeyJwk = new TestIT.Authenticator.API.Helpers.SecurityKey("{'d':'XXXXXX','kid':'XXXXXX','use':'XXXXX','alg':'XXXXX'}", "XXXXX");
_settings.IdConfiguration.Scope = "scope";
_settings.IdConfiguration.StsUrl = "URL";
builder.Services.AddHttpContextAccessor();
builder.Services.AddMvc()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = new LowercaseContractResolver();
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
options.JsonSerializerOptions.Converters.Add(new TestIT.Authenticator.API.Helpers.DateTimeHelper("dd.MM.yyyyTHH.mm.ss"));
})
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix, opts =>
{
opts.ResourcesPath = "Resources";
})
.AddDataAnnotationsLocalization();
builder.Services.AddControllers();
builder.Services.AddRouting(options => options.LowercaseUrls = true);
builder.Services.AddEndpointsApiExplorer();
builder.Services.Configure(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.None;
options.Secure = CookieSecurePolicy.Always; // Ensure cookies are marked as Secure
});
// Create a settings instance. These can be injected into other objects, for instance the HomeController
builder.Services.AddSingleton(_settings);
// We need the IdConfiguration instance as a service as well:
builder.Services.AddSingleton(_settings.IdConfiguration);
// Services for calculating the expiration time for tokens
var dateTimeService = new DateTimeService();
builder.Services.AddSingleton(dateTimeService);
builder.Services.AddTransient();
builder.Services.AddSingleton();
// No request object is needed, so we inject a null object for payload claims creation instead
builder.Services.AddSingleton(new NullPayloadClaimsCreatorForRequestObjects());
//var clientAssertionPayloadClaimsCreator = new ClientAssertionPayloadClaimsCreator(dateTimeService);
//builder.Services.AddSingleton(clientAssertionPayloadClaimsCreator);
var clientAssertionPayloadClaimsCreator = new ClientAssertionPayloadClaimsCreator(dateTimeService);
// We need payload claims for the token request, both the "default" type and for the multi-tenant organization number:
var compositePayloadClaimsCreator = new CompositePayloadClaimsCreator(new List
{
clientAssertionPayloadClaimsCreator,
new PayloadClaimsCreatorForMultiTenantClient()
});
// We add this object as an instance of IPayloadClaimsCreatorForClientAssertion
builder.Services.AddSingleton(compositePayloadClaimsCreator);
builder.Services.AddTransient();
builder.Services.AddSingleton();
builder.Services.AddTransient();
// Builder for client assertions payloads
// Builder for JWT tokens used for client assertions
builder.Services.AddSingleton(new DiscoveryDocumentGetter(_settings.IdConfiguration.StsUrl));
builder.Services.AddSingleton();
// Builds token requests (in our case, refresh token requests)
builder.Services.AddTransient();
// Register services here
builder.Services.AddTransient();
builder.Services.AddScoped();
// A getter of user session data, uses the user session data store
builder.Services.AddTransient();
builder.Services.AddAuthentication(options =>
{
// Configure default authentication schemes for the application
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
//options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
// Configure JWT bearer authentication
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config.GetValue("Secret"))),
ValidateIssuer = true,
ValidIssuer = config.GetValue("Issuer"),
ValidateAudience = true,
ValidAudience = config.GetValue("Audience"),
ValidateLifetime = true,
};
});
builder.Services.AddSwaggerGen(c =>
{
// Configure Swagger documentation
c.SwaggerDoc("v1", new OpenApiInfo { Title = "TestIt .Net middleware", Version = "v1" });
// Add JWT bearer authentication to Swagger
var securityScheme = new OpenApiSecurityScheme
{
Name = "Authorization",
BearerFormat = "JWT",
Scheme = "bearer",
Description = "JWT Authorization header using the Bearer scheme.",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
};
c.AddSecurityDefinition("Bearer", securityScheme);
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty()
}
});
});
builder.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // Ensure cookies are sent over HTTPS
options.Cookie.SameSite = SameSiteMode.None; // Set the SameSite attribute to None
});
builder.Services.AddTransient();
builder.Services.AddTransient();
builder.Services.AddTransient();
builder.Services.Configure(OpenIdConnectOptions =>
{
OpenIdConnectOptions.MinimumSameSitePolicy = SameSiteMode.None;
OpenIdConnectOptions.Secure = CookieSecurePolicy.Always; // Ensure cookies are marked as Secure
});
//// Set authentication options (these will call the AuthenticationOptionsInitializer and OpenIdConnectOptionsInitializer instances)
builder.Services.AddAuthentication()
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectOptions =>
{
// Path to access denied endpoint. Used when authorization fails
OpenIdConnectOptions.AccessDeniedPath = "/authorization/access-denied";
OpenIdConnectOptions.AccessDeniedPath = "/authorization/access-denied";
// Ensure cookies are only sent over HTTPS
OpenIdConnectOptions.Cookie.SecurePolicy = CookieSecurePolicy.Always;
// Set SameSite policy. Adjust as necessary based on your requirements
OpenIdConnectOptions.Cookie.SameSite = SameSiteMode.None;
// Mark the cookie as HttpOnly
OpenIdConnectOptions.Cookie.HttpOnly = true;
})
.AddOpenIdConnect(openIdConnectOptions =>
{
// We need to extract the OpenID Connect options initializer from the service provider:
var serviceProvider = builder.Services.BuildServiceProvider();
var initializer = serviceProvider.GetService();
initializer!.Configure(nameof(UserAuthendication), openIdConnectOptions);
openIdConnectOptions.CallbackPath = "/signin-oidc";
openIdConnectOptions.NonceCookie.SecurePolicy = CookieSecurePolicy.Always;
openIdConnectOptions.CorrelationCookie.SecurePolicy = CookieSecurePolicy.Always;
});
var securityLevelClaimPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireClaim(IdSecurityLevelClaim, "4")
.Build();
builder.Services.AddDataProtection();
builder.Services.AddAuthorization(config =>
{
config.AddPolicy(SecurityLevelClaimPolicy, securityLevelClaimPolicy);
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseRouting();
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders.XForwardedFor | Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders.XForwardedProto
});
app.UseCookiePolicy(new CookiePolicyOptions
{
Secure = CookieSecurePolicy.Always,
});
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
//app.UseHttpsRedirection();
app.UseCors("AllowAll");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Изменить: забыл упомянуть, что приложение находится внутри контейнера докеров
Подробнее здесь: https://stackoverflow.com/questions/788 ... entication