ASP.NET Core - Session not getting saved - session

I'm trying to save my session in ASP.NET Core, but it is not getting saved.
I have looked at other answers, suggesting to change CookiePolicyOptions and nothing has worked so far. I have another project with the exact same code (presumably), and it works there but not in this project.
In my controller I have:
[HttpPost]
public IActionResult AddToPlan(int mealId)
{
PlanCart planCart = GetPlanCart();
planCart.AddItem(mealId);
SavePlanCart(planCart);
// ALWAYS 1
var y = planCart.returnList();
foreach (var x in y)
{
var z = x; // For debug purposes
}
return RedirectToAction("Index");
}
private PlanCart GetPlanCart()
{
PlanCart planCart = HttpContext.Session.GetJson<PlanCart>("PlanCart") ?? new PlanCart();
return planCart;
}
private void SavePlanCart(PlanCart planCart)
{
HttpContext.Session.SetJson("PlanCart", planCart);
}
I have a class with extension methods:
public static class SessionsExtensions
{
public static void SetJson(this ISession session, string key, object value)
{
session.SetString(key, JsonConvert.SerializeObject(value));
}
public static T GetJson<T>(this ISession session, string key)
{
var sessionData = session.GetString(key);
return sessionData == null
? default(T) : JsonConvert.DeserializeObject<T>(sessionData);
}
}
Startup class:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.UseCookiePolicy();
}
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();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddMemoryCache();
I have checked my session. The session DOES exist but every time the count of the PlanCartList is 1 and previous items are lost.
If anybody could help me it would be very much appreciated because I'm losing my mind here!

Related

MassTransit/Quartz.NET schedule works for InMemory but not in persistent mode

When using InMemoryMessageScheduler with Quartz, the ScheduledMessage actually gets scheduled and the "Message" is published at the defined time.
The problem is when the Quartz with persisting option is used, the message is persisted into the database, the ScheduledMessage is consumed by the ScheduleMessageConsumer BUT the "Message" is never published at the defined time.
I've used Sample-GettingStarted and I've added the next changes:
QuartzConfig:
public class QuartzConfig : Dictionary<string, string>
{
public QuartzConfig(string connectionString)
{
this["quartz.scheduler.instanceName"] = "MassTransit-Scheduler";
this["quartz.scheduler.instanceId"] = "AUTO";
this["quartz.serializer.type"] = "json";
this["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz";
this["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz";
this["quartz.jobStore.tablePrefix"] = "QRTZ_";
this["quartz.jobStore.dataSource"] = "myDS";
this["quartz.dataSource.myDS.provider"] = "Npgsql";
this["quartz.dataSource.myDS.connectionString"] = connectionString;
this["quartz.jobStore.useProperties"] = "true";
}
public NameValueCollection ToNameValueCollection()
{
return this.Aggregate(new NameValueCollection(), (seed, current) =>
{
seed.Add(current.Key, current.Value);
return seed;
});
}
}
Configuration:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddMassTransit(x =>
{
x.AddConsumer<MessageConsumer>();
var scheduler = CreateScheduler();
x.UsingRabbitMq((context,cfg) =>
{
cfg.ReceiveEndpoint("quartz", endpoint =>
{
endpoint.Consumer(() => new ScheduleMessageConsumer(scheduler));
endpoint.Consumer(() => new CancelScheduledMessageConsumer(scheduler));
cfg.UseMessageScheduler(endpoint.InputAddress);
});
cfg.ConfigureEndpoints(context);
});
});
services.AddMassTransitHostedService();
services.AddHostedService<Worker>();
});
static IScheduler CreateScheduler()
{
var dbConnectionString = "Host=localhost;Database=scheduler;Port=5432;Password=pass;User ID=user;Pooling=true;MaxPoolSize=200;Enlist=true";
var quartzConfig = new QuartzConfig(dbConnectionString)
.ToNameValueCollection();
ISchedulerFactory schedulerFactory = new StdSchedulerFactory(quartzConfig);
return schedulerFactory.GetScheduler().GetAwaiter().GetResult();
}
Worker.cs:
public class Worker : BackgroundService
{
readonly IBus _bus;
public Worker(IBus bus)
{
_bus = bus;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await _bus.CreateMessageScheduler().SchedulePublish(DateTime.UtcNow + TimeSpan.FromSeconds(5), new Message { Text = $"I really hope this is scheduled {DateTime.Now}" });
await Task.Delay(10000, stoppingToken);
}
}
}
Any ideas?
The Quartz Integration package connects a bus observer to handle the start/stop of Quartz.NET, as shown in the source. Unfortunately the documentation isn't great on how to do it.

Can I use HTTP instead of HTTPS to host MVC client to work with IdentityServer?

I had my quickstart programs working on my local test environment: A MVC client accessing an API through IdentityServer. And this is all setup using HTTPS and self-host (Kestrel). What I am wondering is - can I host MVC in HTTP instead of HTTPS when working with IdentityServer? It doesn't seem matter if I change all parties to use HTTP or just MVC and leave the rest with HTTPS, as soon as I changed my MVC from https://localhost:5009 to http://localhost:5008 (in several places), the app failed with an error "invalid redirect uri".
Am I missing something, or is this simply something not allowed?
You can run over HTTP just fine during development, but in production you should always try to use HTTPS.
The error you get is because the RedirectURLs in the client definition in IdentityServer does not match the url of your client:
RedirectUris =
{
"https://localhost:5001/...."
},
It is also recommended in the client to set this to false if you want to use HTTP when you define your OpenIDConnect options.
/// <summary>
/// Gets or sets if HTTPS is required for the metadata address or authority.
/// The default is true. This should be disabled only in development environments.
/// </summary>
public bool RequireHttpsMetadata { get; set; } = true;
If you mange to get to the IdentityServer login page, then in the URL you will find the the redirectURL that is actually sent to IdentityServer. Or capture the request using fiddler to see what the requestUrl is that is passed to identityserver.
When I look at the screenshot, you have port 5009 in the allowedRedirectURis and 5008 in the request.
Here is my MVC Startup
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
// The default is true. This should be disabled only in development environments!!!
options.RequireHttpsMetadata = false;
options.Authority = "https://localhost:5005";
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.ResponseType = "code";
options.SaveTokens = true;
options.Scope.Add("profile");
options.Scope.Add("email");
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("MyAPI");
options.Scope.Add("offline_access");
});
}
// 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");
// 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();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}")
.RequireAuthorization();
});
}
}
Here is Startup of IdentityServer
public class Startup
{
public IWebHostEnvironment Environment { get; }
public IConfiguration Configuration { get; }
public Startup(IWebHostEnvironment environment, IConfiguration configuration)
{
Environment = environment;
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
var connstr = Configuration.GetConnectionString("MyIDSConnection");
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connstr));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.EmitStaticAudienceClaim = true;
})
.AddAspNetIdentity<ApplicationUser>()
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(connstr,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(connstr,
sql => sql.MigrationsAssembly(migrationsAssembly));
});
// not recommended for production - you need to store your key material somewhere secure
builder.AddDeveloperSigningCredential();
//services.AddIdentityServer().AddSigningCredential(
// new X509Certificate2(Path.Combine(_environment.ContentRootPath, "certs", "IdentityServer4Auth.pfx")));
services.AddAuthentication()
.AddGoogle(options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
// register your IdentityServer with Google at https://console.developers.google.com
// enable the Google+ API
// set the redirect URI to https://localhost:5001/signin-google
options.ClientId = "";
options.ClientSecret = "";
});
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
// uncomment if use MVC
app.UseStaticFiles();
app.UseRouting();
// Add IdentityServer to the pipeline
// UseIdentityServer includes a call to UseAuthentication, so it’s not necessary to have both
app.UseIdentityServer();
// uncomment if use MVC
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
private void InitializeDatabase(IApplicationBuilder app)
{
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
context.Database.Migrate();
serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>().Database.Migrate();
if (!context.Clients.Any())
{
foreach (var client in Config.Clients)
{
context.Clients.Add(client.ToEntity());
}
context.SaveChanges();
}
if (!context.IdentityResources.Any())
{
foreach (var resource in Config.IdentityResources)
{
context.IdentityResources.Add(resource.ToEntity());
}
context.SaveChanges();
}
if (!context.ApiScopes.Any())
{
foreach (var resource in Config.ApiScopes)
{
context.ApiScopes.Add(resource.ToEntity());
}
context.SaveChanges();
}
}
}
}
Here is the IdentityServer console window when starts
And here is the MVC console window when start

Integration Test IocManager is Null

I am setting up Integration tests of my Web UI using abp 3.9.0.
I have followed the model at https://github.com/aspnetboilerplate/aspnet-core-template/tree/master/test/AbpCompanyName.AbpProjectName.Web.Tests
Everything compiles and executes, but I get a null reference exception at the following in my Test base class:
protected void UsingDbContext(Action<CentralPortalDbContext> action)
{
using (var context = IocManager.Resolve<CentralPortalDbContext>())
{
action(context);
context.SaveChanges();
}
}
Inspecting the function during debugging, it shows that IocManager is null.
I've tried various permutations in all of the classes with no luck.
Startup.cs:
using System;
using Abp.AspNetCore;
using Abp.AspNetCore.TestBase;
using Abp.Dependency;
using *****.CentralPortal.EntityFrameworkCore;
using *****.CentralPortal.Web.Controllers;
using Castle.MicroKernel.Registration;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.AspNetCore.Authentication.Cookies;
using Abp.Reflection.Extensions;
namespace *****.CentralPortal.Web.Tests
{
public class Startup
{
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddEntityFrameworkInMemoryDatabase();
services.AddMvc()
.PartManager.ApplicationParts.Add(new AssemblyPart(typeof(Web.Startup.CentralPortalWebModule).GetAssembly()));
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie("Cookies")
.AddOpenIdConnect(options => SetOpenIdConnectOptions(options));
services = SetAuthorizations(services);
//Configure Abp and Dependency Injection
return services.AddAbp<CentralPortalWebTestModule>(options =>
{
options.SetupTest();
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
UseInMemoryDb(app.ApplicationServices);
app.UseAbp(); //Initializes ABP framework.
app.UseExceptionHandler("/Error");
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}"
);
});
app.UseAuthentication();
}
private void UseInMemoryDb(IServiceProvider serviceProvider)
{
var builder = new DbContextOptionsBuilder<CentralPortalDbContext>();
builder.UseInMemoryDatabase(Guid.NewGuid().ToString()).UseInternalServiceProvider(serviceProvider);
var options = builder.Options;
var iocManager = serviceProvider.GetRequiredService<IIocManager>();
iocManager.IocContainer
.Register(
Component.For<DbContextOptions<CentralPortalDbContext>>()
.Instance(options)
.LifestyleSingleton()
);
}
private void SetOpenIdConnectOptions(OpenIdConnectOptions options)
{
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:50052";
options.RequireHttpsMetadata = false;
options.ClientId = "centralportal";
options.ClientSecret = "*************";
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.MetadataAddress = $"http://localhost:50052/.well-known/openid-configuration";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("auditingApi");
options.Scope.Add("ordersApi");
options.Scope.Add("identityApi");
options.Scope.Add("offline_access");
options.Scope.Add("role");
}
//Helper method to add all authorization policies
//Keeps the ConfigureServices method cleaner.
private IServiceCollection SetAuthorizations(IServiceCollection services)
{
services.AddAuthorization(options =>
options
.AddPolicy("TestResults", builder =>
{
builder.RequireClaim("role", new List<string> { "TestResults" });
})
);
services.AddAuthorization(options =>
options
.AddPolicy("Orders", builder =>
{
builder.RequireRole(new[] { "Orders" });
})
);
services.AddAuthorization(options =>
options
.AddPolicy("HomePage", builder =>
{
builder.RequireRole(new[] { "HomePage" });
})
);
services.AddAuthorization(options =>
options
.AddPolicy("Dashboard", builder =>
{
builder.RequireRole(new[] { "Dashboard" });
})
);
services.AddAuthorization(options =>
options
.AddPolicy("UserAdmin", builder =>
{
builder.RequireRole(new[] { "UserAdmin" });
})
);
services.AddAuthorization(options =>
options
.AddPolicy("CustomerAdmin", builder =>
{
builder.RequireRole(new[] { "CustomerAdmin" });
})
);
return services;
}
}
}
WebTestBase:
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Abp.AspNetCore.TestBase;
using *****.CentralPortal.EntityFrameworkCore;
using *****.CentralPortal.Tests.TestDatas;
using AngleSharp.Dom.Html;
using AngleSharp.Parser.Html;
using Microsoft.AspNetCore.Hosting;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Shouldly;
using Abp.Dependency;
namespace *****.CentralPortal.Web.Tests
{
public abstract class CentralPortalWebTestBase : AbpAspNetCoreIntegratedTestBase<Startup>
{
protected static readonly Lazy<string> ContentRootFolder;
//IIocManager _IocManager;
static CentralPortalWebTestBase()
{
ContentRootFolder = new Lazy<string>(WebContentDirectoryFinder.CalculateContentRootFolder, true);
}
protected CentralPortalWebTestBase() : base()
{
UsingDbContext(context => new TestDataBuilder(context).Build());
}
protected override IWebHostBuilder CreateWebHostBuilder()
{
var _ContentRootFolder = new Lazy<string>(WebContentDirectoryFinder.CalculateContentRootFolder, true);
UsingDbContext(context => new TestDataBuilder(context).Build());
return base
.CreateWebHostBuilder()
.UseStartup<Startup>()
.UseContentRoot(_ContentRootFolder.Value);
}
#region Get response
protected async Task<T> GetResponseAsObjectAsync<T>(string url,
HttpStatusCode expectedStatusCode = HttpStatusCode.OK)
{
var strResponse = await GetResponseAsStringAsync(url, expectedStatusCode);
return JsonConvert.DeserializeObject<T>(strResponse, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
}
protected async Task<string> GetResponseAsStringAsync(string url,
HttpStatusCode expectedStatusCode = HttpStatusCode.OK)
{
var response = await GetResponseAsync(url, expectedStatusCode);
return await response.Content.ReadAsStringAsync();
}
protected async Task<HttpResponseMessage> GetResponseAsync(string url,
HttpStatusCode expectedStatusCode = HttpStatusCode.OK)
{
var response = await Client.GetAsync(url);
response.StatusCode.ShouldBe(expectedStatusCode);
return response;
}
#endregion
#region UsingDbContext
protected void UsingDbContext(Action<CentralPortalDbContext> action)
{
using (var context = IocManager.Resolve<CentralPortalDbContext>())
{
action(context);
context.SaveChanges();
}
}
protected T UsingDbContext<T>(Func<CentralPortalDbContext, T> func)
{
T result;
using (var context = IocManager.Resolve<CentralPortalDbContext>())
{
result = func(context);
context.SaveChanges();
}
return result;
}
protected async Task UsingDbContextAsync(Func<CentralPortalDbContext, Task> action)
{
using (var context = IocManager.Resolve<CentralPortalDbContext>())
{
await action(context);
await context.SaveChangesAsync(true);
}
}
protected async Task<T> UsingDbContextAsync<T>(Func<CentralPortalDbContext, Task<T>> func)
{
T result;
using (var context = IocManager.Resolve<CentralPortalDbContext>())
{
result = await func(context);
context.SaveChanges();
}
return result;
}
#endregion
#region ParseHtml
protected IHtmlDocument ParseHtml(string htmlString)
{
return new HtmlParser().Parse(htmlString);
}
#endregion
}
}
Web Test Module
using Abp.AspNetCore.TestBase;
using Abp.Modules;
using Abp.Reflection.Extensions;
using Castle.MicroKernel.Registration;
using Castle.Windsor.MsDependencyInjection;
using *****.CentralPortal.EntityFrameworkCore;
using *****.CentralPortal.Web.Startup;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace *****.CentralPortal.Web.Tests
{
[DependsOn(
typeof(CentralPortalWebModule),
typeof(CentralPortalEntityFrameworkCoreModule),
typeof(AbpAspNetCoreTestBaseModule)
)]
public class CentralPortalWebTestModule : AbpModule
{
public override void PreInitialize()
{
Configuration.UnitOfWork.IsTransactional = false; //EF Core InMemory DB does not support transactions.
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(CentralPortalWebTestModule).GetAssembly());
SetupInMemoryDb();
}
private void SetupInMemoryDb()
{
var services = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase();
var serviceProvider = WindsorRegistrationHelper.CreateServiceProvider(
IocManager.IocContainer,
services
);
var builder = new DbContextOptionsBuilder<CentralPortalDbContext>();
builder.UseInMemoryDatabase().UseInternalServiceProvider(serviceProvider);
IocManager.IocContainer.Register(
Component
.For<DbContextOptions<CentralPortalDbContext>>()
.Instance(builder.Options)
.LifestyleSingleton()
);
}
}
}
Web Test Class:
using System.Threading.Tasks;
using *****.CentralPortal.Web.Controllers;
using Shouldly;
using Xunit;
namespace *****.CentralPortal.Web.Tests.Controllers
{
public class HomeController_Tests: CentralPortalWebTestBase
{
[Fact]
public async Task Index_Test()
{
string url = GetUrl<HomeController>(nameof(HomeController.Index));
//Act
var response = await GetResponseAsStringAsync(
url
);
//Assert
response.ShouldNotBeNullOrEmpty();
}
[Fact]
public async void ShouldOnlyShowHomeMenuWhenNotAuthorized()
{
var response = await GetResponseAsStringAsync(
GetUrl<HomeController>(nameof(HomeController.Index))
);
//Parse the response for menuitems, count them and assert
int menuitemcount = 0;
string statuscode = "";//TODO: Gotta parse the response string to get the code
Assert.Equal(menuitemcount, 2);
}
}
}
I'm not sure why IocManager is null at this point. I'm guessing it's a simple config item I'm missing, but I can't seem to find it or any examples that deviate from what I've got.
Any help would be appreciated.
As Requested: the Full Exception.
[1/29/2019 8:51:46 AM Informational] [xUnit.net 00:00:02.72] System.NullReferenceException : Object reference not set to an instance of an object.
[1/29/2019 8:51:46 AM Informational] [xUnit.net 00:00:02.72] Stack Trace:
[1/29/2019 8:51:46 AM Informational] [xUnit.net 00:00:02.72] C:\ProjectCode\*****\*****_CentralPortal\test\*****.CentralPortal.Web.Tests\CentralPortalWebTestBase.cs(78,0): at *****.CentralPortal.Web.Tests.CentralPortalWebTestBase.UsingDbContext(Action`1 action)
[1/29/2019 8:51:46 AM Informational] [xUnit.net 00:00:02.72] C:\ProjectCode\*****\*****_CentralPortal\test\*****.CentralPortal.Web.Tests\CentralPortalWebTestBase.cs(37,0): at *****.CentralPortal.Web.Tests.CentralPortalWebTestBase.CreateWebHostBuilder()
[1/29/2019 8:51:46 AM Informational] [xUnit.net 00:00:02.72] D:\Github\aspnetboilerplate\src\Abp.AspNetCore.TestBase\AbpAspNetCoreIntegratedTestBase.cs(30,0): at Abp.AspNetCore.TestBase.AbpAspNetCoreIntegratedTestBase`1..ctor()
[1/29/2019 8:51:46 AM Informational] [xUnit.net 00:00:02.72] C:\ProjectCode\*****\*****_CentralPortal\test\*****.CentralPortal.Web.Tests\CentralPortalWebTestBase.cs(28,0): at *****.CentralPortal.Web.Tests.CentralPortalWebTestBase..ctor()
[1/29/2019 8:51:46 AM Informational] [xUnit.net 00:00:02.72] at *****.CentralPortal.Web.Tests.Controllers.HomeController_Tests..ctor()
So it was the following in the web test module. I'm not sure where I got that from, I think it was in an example I pulled from. Commenting out SetupInMemoryDb(); resolved the issue.
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(CentralPortalWebTestModule).GetAssembly());
SetupInMemoryDb();
}
private void SetupInMemoryDb()
{
var services = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase();
var serviceProvider = WindsorRegistrationHelper.CreateServiceProvider(
IocManager.IocContainer,
services
);
var builder = new DbContextOptionsBuilder<CentralPortalDbContext>();
builder.UseInMemoryDatabase().UseInternalServiceProvider(serviceProvider);
IocManager.IocContainer.Register(
Component
.For<DbContextOptions<CentralPortalDbContext>>()
.Instance(builder.Options)
.LifestyleSingleton()
);
}

How to debug JWT Bearer Error "invalid_token"

I'm trying to secure an existing AspNet Core 2.0 / angular 4 app using jwt. I'm using angular2-jwt for the client part and it works just fine. However when it comes to my WebApi, my token is always rejected(using AuthHttp from angular2-jwt to launch my requests or even with postman). The only response I get is 401 Bearer error="invalid_token". I've checked it with the jwt.io chrome extension and it seems just fine(signature, audience, issuer). I can't find anything in the IIS logs either as to why it is deemed invalid. So my question is how can I get more information on what is wrong with the token ?
Any help will be much appreciated.
For reference here's my startup.cs
public class Startup
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
IConfigurationSection jwtConf = this.Configuration.GetSection("jwt");
services.Configure<Controls.JWTConf>(Configuration.GetSection("jwt"));
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters =
new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtConf.GetValue<string>("issuer"),
ValidAudience = jwtConf.GetValue<string>("audience"),
IssuerSigningKey = Security.JwtSecurityKey.Create(jwtConf.GetValue<string>("keyBase"))
};
});
services.AddMvc(
config =>
{
var policy = new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireClaim(ClaimTypes.Name)
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
}
).AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
services.AddNodeServices();
string conn = this.Configuration.GetConnectionString("optimumDB");
services.AddDbContext<TracDbContext>(options =>
options.UseSqlServer(conn));
// Register the Swagger generator, defining one or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "Angular 4.0 Universal & ASP.NET Core advanced starter-kit web API", Version = "v1" });
});
}
// 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, TracDbContext context)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseStaticFiles();
app.UseAuthentication();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true,
HotModuleReplacementEndpoint = "/dist/__webpack_hmr"
});
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
// Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
app.MapWhen(x => !x.Request.Path.Value.StartsWith("/swagger", StringComparison.OrdinalIgnoreCase), builder =>
{
builder.UseMvc(routes =>
{
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
});
}
else
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
"Sitemap",
"sitemap.xml",
new { controller = "Home", action = "SitemapXml" });
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
app.UseExceptionHandler("/Home/Error");
}
}
}
My token generating controller
[Route("api/token")]
[AllowAnonymous]
public class TokenController : Controller
{
private IOptions<JWTConf> jwt;
public TokenController(IOptions<JWTConf> jwtConf)
{
this.jwt = jwtConf;
}
[HttpPost]
public IActionResult Create([FromBody]string userCode)
{
Model.Entities.Utilisateur user = new Model.Entities.Utilisateur { ID_UTILISATEUR = 6 };
JwtToken token = new JwtTokenBuilder()
.AddSecurityKey(JwtSecurityKey.Create(this.jwt.Value.keyBase))
.AddSubject("User")
.AddIssuer(this.jwt.Value.issuer)
.AddAudience(this.jwt.Value.audience)
.AddClaim(ClaimTypes.Name,user.ID_UTILISATEUR.ToString())
.AddExpiry(1440)
.Build();
var tok = new { token = token.Value };
//return Ok(token);
return Ok(JsonConvert.SerializeObject(tok));
}
}
And finally the controller that rejects the token :
[Produces("application/json")]
public class JobsController : BaseController
{
public JobsController(IConfiguration config, TracDbContext db) : base(config, db)
{
}
// GET: api/Jobs
[HttpGet]
[Route("api/Jobs")]
public IEnumerable<Departement> Get()
{
return new GroupedJobs(Db.GetJobs().ToList());
}
[HttpGet]
[Route("api/Jobs/{id}")]
public JOB_CLIENT Get(int id)
{
return Db.GetDetailsJob(id);
}
}
Found the problem ... turns out I was storing my token with quotes around it. So The authorization header that was being sent looked like this
Bearer "TOKEN"
instead of
Bearer TOKEN
Being new to the whole thing I tought the quotes were being added by the AuthHtpp and were part of the protocol.

Can I display contents of Application or Cache objects using Glimpse in an MVC project?

The ASP.NET WebForms trace output has a section for Application State. Is it possible to see the same using Glimpse?
In my home controller's Index() method, I tried adding some test values, but I don't see the output in any of the Glimpse tabs.
ControllerContext.HttpContext.Application.Add("TEST1", "VALUE1");
ControllerContext.HttpContext.Cache.Insert("TEST2", "VALUE2");
I didn't see anything in the documentation either.
I don't think that there is an out-of-the-box support for this, but it would be trivial to write a plugin that will show this information.
For example to show everything that's stored in the ApplicationState you could write the following plugin:
[Glimpse.Core.Extensibility.GlimpsePluginAttribute]
public class ApplicationStateGlimpsePlugin : IGlimpsePlugin
{
public object GetData(HttpContextBase context)
{
var data = new List<object[]> { new[] { "Key", "Value" } };
foreach (string key in context.Application.Keys)
{
data.Add(new object[] { key, context.Application[key] });
}
return data;
}
public void SetupInit()
{
}
public string Name
{
get { return "ApplicationState"; }
}
}
and then you get the desired result:
and to list everything that's stored into the cache:
[Glimpse.Core.Extensibility.GlimpsePluginAttribute]
public class ApplicationCacheGlimpsePlugin : IGlimpsePlugin
{
public object GetData(HttpContextBase context)
{
var data = new List<object[]> { new[] { "Key", "Value" } };
foreach (DictionaryEntry item in context.Cache)
{
data.Add(new object[] { item.Key, item.Value });
}
return data;
}
public void SetupInit()
{
}
public string Name
{
get { return "ApplicationCache"; }
}
}

Resources