Identity Server 4 - Audience validation failed error - asp.net-core

I encounter this error in my API IDX10214: Audience validation failed. Audiences: 'https://localhost:44337/resources'. Did not match: validationParameters.ValidAudience: 'joborderingapi' or validationParameters.ValidAudiences: 'null'
I've been trying to solve this for 2 days already and can't figure out yet about how to solve it.
I have the following applications:
Client App (Angular 7)
Identity Server
API
I was able to login successfully to Identity Server in my Client app and was able to get the token but when I used the token to connect to the API method it throws this error IDX10214: Audience validation failed. Audiences: 'https://localhost:44337/resources'. Did not match: validationParameters.ValidAudience: 'joborderingapi' or validationParameters.ValidAudiences: 'null'.
I followed the answer from Identity Server 4 with EF Identity DB Issue and checked the three tables (ApiResources, ApiScopes, ClientScopes), the values are correct, joborderingapiis enabled in ApiResources, in ApiScopes it is linked to ApiResource and in ClientScopes it is linked to the Client
Here is my API Startup.cs
public void ConfigureServices(IServiceCollection services)
{
var apiIdpAuthority = Configuration["AppSettings:IdpAuthority"];
var apiName = Configuration["AppSettings:ApiName"];
var apiSecret = Configuration["AppSettings:ApiSecret"];
var requireHttps = Convert.ToBoolean(Configuration["AppSettings:RequireHttps"]);
var httpsPort = Configuration["AppSettings:HttpsPort"];
var applicationUrl = Configuration["AppSettings:ApplicationUrl"];
services.Configure<ClientAppSettings>(Configuration.GetSection("ClientAppSettings"));
services.AddDbContext<JobOrderingDataContext>(options => options.UseSqlServer(Configuration.GetConnectionString("JobOrderingDB")));
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
IdentityModelEventSource.ShowPII = true;
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = apiIdpAuthority;
options.RequireHttpsMetadata = requireHttps;
options.ApiName = apiName;
options.ApiSecret = apiSecret;
});
services.AddCors(options =>
{
// this defines a CORS policy called "default"
options.AddPolicy("default", policy =>
{
policy.WithOrigins(apiIdpAuthority, applicationUrl)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
.AllowCredentials();
});
});
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
services.AddHsts(options =>
{
options.Preload = true;
options.IncludeSubDomains = true;
options.MaxAge = TimeSpan.FromDays(60);
});
services.AddHttpsRedirection(options =>
{
options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;
options.HttpsPort = Convert.ToInt32(httpsPort);
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseCors("default");
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseHttpsRedirection();
app.UseCookiePolicy();
var locale = Configuration["SiteLocale"];
var supportedCultures = new List<CultureInfo> { new CultureInfo("en-US") };
if (supportedCultures.Where(x => x.Name == locale).Count() == 0)
{
supportedCultures.Add(new CultureInfo(locale));
}
RequestLocalizationOptions localizationOptions = new RequestLocalizationOptions()
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
DefaultRequestCulture = new RequestCulture(locale)
};
app.UseRequestLocalization(localizationOptions);
app.UseAuthentication();
app.UseMvc();
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
Identity Server Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//var microsoftClientId = Configuration["MicrosoftClientId"];
// var microsoftClientSecret = Configuration["MircosoftClientSecret"];
var azureADClientId = Configuration["AzureADClientId"];
var azureADClientSecret = Configuration["AzureADClientSecret"];
var azureADEndPoint = Configuration["AzureADEndPoint"];
var issuerUri = Configuration["IssuerUri"];
string connectionString = Configuration.GetConnectionString("DefaultConnection");
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddDbContext<IdentityServerDataContext>(options => options.UseSqlServer(connectionString));
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddAuthentication()
// .AddCookie()
.AddOpenIdConnect("AAD", "Azure AD", options =>
{
options.Authority = string.Format("https://login.microsoftonline.com/{0}", azureADEndPoint);
options.ClientId = azureADClientId;
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
});
IdentityModelEventSource.ShowPII = true;
services.AddTransient<IProfileService, IdentityWithAdditionalClaimsProfileService>();
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddIdentityServer() .AddSigninCredentialFromConfig(Configuration.GetSection("SigninKeyCredentials"), _logger)
// this adds the config data from DB (clients, resources)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30;
})
.AddAspNetIdentity<ApplicationUser>()
.AddProfileService<IdentityWithAdditionalClaimsProfileService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// this will do the initial DB population
// InitializeDatabase(app);
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentityServer();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Note: I only encounter this issue when I use the local login. It is working fine if I use the Azure AD login, I was able to connect to the API using the authorization token from the client app

Related

Htttp 400 Bad Request Request Header is too long

I am currently developing an application using Asp.net core 5.0 and Identity server 4.My OIDC authentication flow handled by Microsoft.AspNetCore.Authentication.OpenIdConnect. I deployed my application into IIS and I am getting my login screen. But after login I got the Http 400 Bad request error.Error. I checked my application cookie it contains many AspNetCore.OpenIdConnect.Nonce cookies. I deleted the cookies but doesn't solve my issue. What is the proper solution to handle this solution. I tried this but doesn't help me. I will share the code below
MVC Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddScoped<RenewToken>();
services.AddAuthorization(options =>
{
options.AddPolicy("CreatePolicy", policy => policy.RequireAssertion(context => AuthorizeAccess(context, "RC", "UC")));
options.AddPolicy("ReadPolicy", policy => policy.RequireAssertion(context => AuthorizeAccess(context, "RR", "UR")));
});
services.AddControllersWithViews();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
}).AddCookie("Cookies", options =>
{
options.Cookie.Name = "Cookies";
options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
options.SlidingExpiration = true;
}).AddOpenIdConnect("oidc", options =>
{
options.BackchannelHttpHandler = new HttpClientHandler { ServerCertificateCustomValidationCallback = delegate { return true; } };
options.Authority = Configuration.GetSection("API:IDS4").Value;
options.SignInScheme = "Cookies";
options.SignedOutRedirectUri = Configuration.GetSection("API:WebClient").Value + "/signout-callback-oidc";
options.RequireHttpsMetadata = true;
options.ClientId = "mvc";
options.ClientSecret = "*****";
options.ResponseType = "code";
options.UsePkce = true;
options.Scope.Add("profile");
options.Scope.Add("mcApi");
options.Scope.Add("Api1");
options.Scope.Add("Api2");
options.Scope.Add("Api3");
options.Scope.Add("Api4");
options.Scope.Add("Api5");
options.Scope.Add("Api6");
options.Scope.Add("Api7");
options.Scope.Add("offline_access");
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.Events.OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.Prompt = "login";
return Task.CompletedTask;
};
options.Events = new OpenIdConnectEvents
{
OnRemoteFailure = (context) =>
{
context.Response.Redirect("/");
context.HandleResponse();
return Task.CompletedTask;
}
};
options.TokenValidationParameters = new TokenValidationParameters
{
RoleClaimType = JwtClaimTypes.Role
};
});
services.AddHttpClient<IAdminService, AdminService>();
services.AddSingleton<DataProtectionPurposeStrings>();
services.AddSingleton<GlobalConstants>();
}
private bool AuthorizeAccess(AuthorizationHandlerContext context, string roleClaim, string userClaim)
{
return context.User.HasClaim(claim => claim.Type == roleClaim && claim.Value == "True") &&
context.User.HasClaim(claim => claim.Type == userClaim && claim.Value == "True") ||
context.User.IsInRole("MyAdmin");
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
Identity Server Startup.cs
public class Startup
{
public IWebHostEnvironment Environment { get; }
public IConfiguration Configuration { get; }
public Startup(IWebHostEnvironment environment, IConfiguration configuration)
{
Environment = environment;
Configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
/****Register asp.net core Identity DBConetexts***/
var idenConnectionString = Configuration["DbContextSettings:IdentityConnectionString"];
var dbPassword = Configuration["DbContextSettings:DbPassword"];
var builder = new NpgsqlConnectionStringBuilder(idenConnectionString)
{
Password = dbPassword
};
services.AddDbContext<IdentityDBContext>(opts => opts.UseNpgsql(builder.ConnectionString));
services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.Password.RequiredLength = 8;
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._#+ ";
options.SignIn.RequireConfirmedEmail = false;
options.User.RequireUniqueEmail = false;
}).AddRoles<ApplicationRole>().AddEntityFrameworkStores<IdentityDBContext>()
.AddDefaultTokenProviders();
/****Identity Server implementation with asp.net core Identity***/
var idsServerConnectionString = Configuration["DbContextSettings:IdentityServer4ConnectionString"];
var migrationAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
var idsServerdbPassword = Configuration["DbContextSettings:DbPassword"];
var idsServerbuilder = new NpgsqlConnectionStringBuilder(idsServerConnectionString)
{
Password = dbPassword
};
var idBuilder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.UserInteraction.LoginUrl = "/Account/Login";
options.UserInteraction.LogoutUrl = "/Account/Login";
options.Authentication = new AuthenticationOptions()
{
CookieLifetime = TimeSpan.FromMinutes(10),
CookieSlidingExpiration = true
};
})
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b => b.UseNpgsql(idsServerbuilder.ConnectionString, sql => sql.MigrationsAssembly(migrationAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b => b.UseNpgsql(idsServerbuilder.ConnectionString, sql => sql.MigrationsAssembly(migrationAssembly));
options.EnableTokenCleanup = true;
}).AddAspNetIdentity<MembershipUser>()
.AddProfileService<ProfileService>();
X509Certificate2 cert = null;
using (var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
certStore.Open(OpenFlags.ReadOnly);
var certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,
"thumbprint",
false);
if (certCollection.Count > 0)
{
cert = certCollection[0];
}
}
if (Environment.IsDevelopment())
{
idBuilder.AddDeveloperSigningCredential();
}
else
{
idBuilder.AddSigningCredential(cert);
}
idBuilder.Services.ConfigureExternalCookie(options =>
{
options.Cookie.IsEssential = true;
options.Cookie.SameSite = (SameSiteMode)(-1);
});
idBuilder.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.IsEssential = true;
options.Cookie.SameSite = (SameSiteMode)(-1);
});
services.AddMediatR(typeof(Startup));
RegisterServices(services);
}
private void RegisterServices(IServiceCollection services)
{
services.AddSingleton<IEventBus, RabbitMQBus>(sp =>
{
var scopeFactory = sp.GetRequiredService<IServiceScopeFactory>();
return new RabbitMQBus(sp.GetService<IMediator>(), scopeFactory);
});
services.AddTransient<UserDBContext>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// uncomment if you want to add MVC
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
}
Can I store the cookies into SessionStore using MemoryCacheTicketStore ? Kindly share your thoughts.
A potential thing could be that IIS thinks the cookie header is too long.
By default ASP.NET Core chunks up the cookie in 4Kb chunks, like this picture shows:
So either you try to reduce the size of the cookie or look at the IIS settings, if you can increase the max header length?
Alternatively, you stop saving the tokens inside the cookie, by setting:
options.SaveTokens = false;
Now you of course need top store it somewhere else, like in a tokenstore.

ASP.net Core 3.1 web application Calling another asp.net core application url in IFrame resets identity and any other pages redirects to login page

I am creating 2 web applications using ASP.net core 3.1 .
I need to call 1st application in iFrame in second application it resets the login identity and when i click any other link in my website it redirects to login page. How to avoid this?
Below the jQuery code I use to load iFrame .
$(document).ready(function () {
$(function () {
$('#previewBot').on('click', function () {
var client = $("#client_id_Broker").html();
var secret = $("#session_secret").html();
var siteid = $("#site_id_Broker").html();
var site = window.location.hostname;
var chatpopContent = "<iframe src='";
var number = 1 + Math.floor(Math.random() * 6);
var URIChat = "https://localhost:44355/?client=" + client + "&siteid=" + siteid + "&secret=" + secret + "&site=" + site + "&r=" + number ;
chatpopContent += URIChat;
chatpopContent += "' id='iView' frameborder='1' class='frame-container' referrerpolicy='no-referrer' rel='noreferrer' async='false' ></iframe>";
$("#showpreview").empty();
$("#iView").remove();
$("#showpreview").append(chatpopContent);
});
});
});
Below is my startup config, is there anything wrong in this? or what should be added to avoid redirecting to login page after calling iframe?
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SqlConStringLocal")));
services.AddDefaultIdentity<ApplicationUser>(options =>
{
options.SignIn.RequireConfirmedAccount = true;
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstffgsfsdfsfsfff123456789-_";
options.User.RequireUniqueEmail = true;
}).AddEntityFrameworkStores<ApplicationDbContext>();
services.ConfigureApplicationCookie(o => {
o.ExpireTimeSpan = TimeSpan.FromDays(5);
o.SlidingExpiration = true;
});
services.AddMvc(o =>
{
//Add Authentication to all Controllers by default.
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
o.Filters.Add(new AuthorizeFilter(policy));
}).AddNToastNotifyNoty(new NotyOptions
{
Layout = "bottomRight",
ProgressBar = true,
Timeout = 5000,
Theme = "metroui"
});
services.Configure<DataProtectionTokenProviderOptions>(o =>
o.TokenLifespan = TimeSpan.FromHours(3));
services.AddAutoMapper(typeof(Startup));
services.AddRazorPages();
services.AddControllers();
services.AddSignalR();
services.AddTransient<IEmailSender, EmailSender>();
services.Configure<AuthMessageSenderOptions>(Configuration);
//services.AddSingleton<IRepository, Repository>();
services.AddScoped<ISitesRepository, SitesRepository>();
services.AddCors();
services.AddControllersWithViews()
.AddNewtonsoftJson()
.AddXmlDataContractSerializerFormatters();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseCors(builder =>
{
builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseNToastNotify();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapHub<ChatAdminHub>("/chatAdminHub");
});
}
I was able manage to solve the issue by adding below cookiePolicyOptions.
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
options.OnAppendCookie = cookieContext =>
CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
options.OnDeleteCookie = cookieContext =>
CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
});
services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "myappcookieName";
options.Cookie.SameSite = SameSiteMode.None;
});

.NetCore 5 session is not starting after publish

I'm working with .NetCore API and MVC. Everything works on the local server. But the session does not occur after publishing the site.
The operation of the project is as follows:
User come with token ID to website.
I'm parsing it and then saving important variables to session.
I'm getting values from session.
...
As you can see If I get error on session other steps will fail.
I tried too many ways but none of them worked.
I'm adding Startup.cs/ConfigureServices and Configure methods.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages().AddRazorRuntimeCompilation();
services.AddMvc().AddControllersAsServices();
services.AddWkhtmltopdf("wkhtmltopdf");
services.AddHttpContextAccessor();
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(600);//You can set Time
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
services.AddAuthentication();
services.AddScoped<Business.Business, Business.Business>();
services.AddSingleton<SharedViewLocalizer>();
#region Localization and Language
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.Configure<RequestLocalizationOptions>(options =>
{
var cultures = new[] {
new CultureInfo("tr-TR"),
new CultureInfo("en-US"),
new CultureInfo("de-DE"),
};
options.DefaultRequestCulture = new RequestCulture("en-US");
options.SupportedCultures = cultures;
options.SupportedUICultures = cultures;
});
#endregion
services.AddMvc(option =>
{
option.EnableEndpointRouting = false;
})
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix,
opts => { opts.ResourcesPath = "Resources"; })
.AddDataAnnotationsLocalization();
services.AddRazorPages();
services.AddServerSideBlazor();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseDeveloperExceptionPage();
//app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
#region Language Options
var options = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
var cookieProvider = options.Value.RequestCultureProviders
.OfType<CookieRequestCultureProvider>()
.First();
var urlProvider = options.Value.RequestCultureProviders
.OfType<QueryStringRequestCultureProvider>().First();
cookieProvider.Options.DefaultRequestCulture = new RequestCulture("tr-TR");
urlProvider.Options.DefaultRequestCulture = new RequestCulture("tr-TR");
cookieProvider.CookieName = CookieRequestCultureProvider.DefaultCookieName;
options.Value.RequestCultureProviders.Clear();
options.Value.RequestCultureProviders.Add(cookieProvider);
options.Value.RequestCultureProviders.Add(urlProvider);
app.UseRequestLocalization(options.Value);
#endregion
app.UseHttpsRedirection();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseSession();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
#region Routing
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
"areas",
"{area:exists}/{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Login}/{id?}");
endpoints.MapRazorPages();
//endpoints.MapBlazorHub();
//endpoints.MapFallbackToController("Index", "Home");
});
#endregion
}
options.Cookie.SameSite = SameSiteMode.None must be used to allow cross-site cookie use.
https://learn.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-5.0
The order of middleware is important. Call UseSession after UseRouting and before UseEndpoints. See Middleware Ordering.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-5.0
If your production environment consists of multiple servers, the easy way out (but not the recommended way), is to enable sticky sessions in your load balancer.
Using distributed cache is the recommended way when using multiple servers. More info: https://dzone.com/articles/aspnet-core-session-storage-strategies
You should consider not to use sessions at all, that will make things a lot easier for you.

Authorization happens before authentication after migrating to .net core 3.1

After migrating from .net core 2.2 to .net core 3.1, and after taking some time to fix all the bugs depending on documentations, after running the app, the authorization method get's hit before it requires the user to be authenticated.
I am using OpenId Connect with AD FS for authentication and a custom method called "PermissionGranted" for authorization. And this method get's hit before it requires for the user to be authenticated.
This is the code of my Startup.cs file:
public class Startup
{
private readonly ConfigSettings settings;
private readonly GetSettings configSettings;
private readonly MyMemoryCache myMemoryCache;
public Startup(IWebHostEnvironment env, IConfiguration configuration)
{
configSettings = new GetSettings();
settings = configSettings.getSettings();
Configuration = configuration;
Environment = env;
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
myMemoryCache = new MyMemoryCache();
Log.Logger = new LoggerConfiguration().Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File(settings.LogFile, rollingInterval: RollingInterval.Day)
.WriteTo.ApplicationInsights(new TelemetryConfiguration(settings.AppInsightsConnection), TelemetryConverter.Events)
.CreateLogger();
}
public IConfiguration Configuration { get; }
private IWebHostEnvironment Environment { get; }
public IServiceProvider ServiceProvider { get; set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(Log.Logger);
services.AddSingleton(settings);
services.AddHttpContextAccessor();
services.AddRouting(options => options.LowercaseUrls = true);
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies", options =>
{
options.AccessDeniedPath = "/users/denied";
})
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.MetadataAddress = settings.MetadataAddress;
options.ClientId = settings.ClientId;
options.GetClaimsFromUserInfoEndpoint = true;
options.ResponseType = OpenIdConnectResponseType.IdToken;
options.SignedOutRedirectUri = settings.PostLogoutUri;
options.SaveTokens = true;
options.UseTokenLifetime = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidAudience = settings.ClientId,
ValidateIssuer = true,
ValidIssuer = settings.Issuer,
ValidateLifetime = true,
RequireExpirationTime = true
};
options.Events = new OpenIdConnectEvents
{
OnRemoteFailure = context =>
{
context.HandleResponse();
context.Response.Redirect("/Login");
return Task.FromResult(0);
},
OnTicketReceived = context =>
{
// Configuration to make the Authentication cookie persistent for 8 hours (after 8 hours it expires and the user has to re-authenticate)
context.Properties.IsPersistent = true;
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddHours(8);
return Task.FromResult(0);
}
};
options.Events.OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.Prompt = "login";
return Task.CompletedTask;
};
});
Audit.Core.Configuration.Setup()
.UseSqlServer(config => config
.ConnectionString(settings.ConnectionString)
.TableName("AuditEvents")
.IdColumnName("EventId")
.JsonColumnName("AuditData")
.LastUpdatedColumnName("LastUpdatedDate"));
services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN");
services.AddApplicationInsightsTelemetry(settings.AppInsightsConnection);
services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
options.Filters.Add(new AuthorizeFilter(policy));
})
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
// Use NewtonsoftJson and custom date format
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
});
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(15);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true; // Make the session cookie essential
});
services.AddAuthorization(options =>
{
options.AddPolicy("Permissions", policy => policy.RequireAssertion(context => PermissionGranted(context)));
});
services.AddDbContext<PasswordManagerContext>(options =>
options.UseSqlServer(settings.ConnectionString));
services.AddSingleton<MyMemoryCache>();
services.AddPaging(options =>
{
options.ViewName = "PagerUI";
options.HtmlIndicatorDown = " <span>↓</span>";
options.HtmlIndicatorUp = " <span>↑</span>";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAntiforgery antiforgery)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSession();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Login}/{action=Index}/{id?}");
//endpoints.MapFallbackToController("Index", "Home");
});
ServiceProvider = app.ApplicationServices;
}
According to official docs, the order of these:
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints
matters but in my code they are ordered correctly.
What could be missing or what could possibly be wrong in the configuration?
Thanks in advance

Server cannot authorize it's own tokens

Here is the setup, I have an auth server that issues tokens to an angular website. I have a controller inside the AuthServer that needs to use the [Authorize] system to only allow JWT tokens that are valid. When I check the User variable in my controller it is always null but when I check the HttpRequestHeaders on the controller I see the token being sent.
I also have an Api server that I implemented using the JWT tokens and [Authorize] system very easily.
Another layer, I am running both Api and Auth servers in docker containers.
My entire Startup.cs file from AuthServer:
var connectionString = Configuration.GetConnectionString("Default");
if (_env.IsDevelopment())
{
try
{
using (AppIdentityDbContext identityDb =
new AppIdentityDbContextFactory(connectionString).Create())
{
int Pendings = identityDb.Database.GetPendingMigrations().Count();
identityDb.Database.Migrate();
}
using (PersistedGrantDbContext persistGrantDb =
new PersistedGrantDbContextFactory(connectionString).Create())
{
int Pendings = persistGrantDb.Database.GetPendingMigrations().Count();
persistGrantDb.Database.Migrate();
}
}
catch (Exception)
{
}
}
services.AddControllersWithViews();
services.AddDbContextPool<AppIdentityDbContext>(options => options.UseSqlServer(connectionString));
services
.AddIdentity<AppUser, IdentityRole>(config=> {
config.User.RequireUniqueEmail = true;
config.SignIn.RequireConfirmedEmail = true;
})
.AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders();
services.AddIdentityServer().AddDeveloperSigningCredential()
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(Configuration.GetConnectionString("Default"));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = (int)TimeSpan.FromDays(1).TotalSeconds; // interval in seconds
})
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<AppUser>()
.AddProfileService<AppUserProfileService>()
.AddJwtBearerClientAuthentication();
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme,
jwtOptions =>
{
// jwt bearer options
jwtOptions.Authority = _env.IsDevelopment() ? "https://localhost:5001" : "";
jwtOptions.RequireHttpsMetadata = _env.IsDevelopment() ? false : true;
jwtOptions.Audience = "resourceapi";
jwtOptions.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateAudience = false,
ValidateIssuer = _env.IsDevelopment() ? false : true,
ValidateActor = false,
ValidateIssuerSigningKey = false
};
},
referenceOptions =>
{
// oauth2 introspection options
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()));
services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
services.AddSingleton<IEmailSender, SmtpSender>();
Configure Section:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
//app.UseHsts();
app.UseHttpsRedirection();
var forwardOptions = new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
RequireHeaderSymmetry = false
};
forwardOptions.KnownNetworks.Clear();
forwardOptions.KnownProxies.Clear();
// ref: https://github.com/aspnet/Docs/issues/2384
app.UseForwardedHeaders(forwardOptions);
}
app.UseCors("AllowAll");
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Check for user inside of AccountController: Controller
var u = User;
var _user = await _userManager.GetUserAsync(u);
var e = this._httpContextAccessor;