MassTransit SignalR Bindings - .net-core

I'm having some issues setting up my MassTransit SignalR solution. I have posted a question before regarding bindings which Chris answered and helped me out but after updating the packages (i was still using 6.x.x which had other issues like restarting RabbitMQ service would origin in an error when sending messages) my bindings don't seem to work properly anymore.
This was the first question i asked: Exchange binding not working in MassTransit with RabbitMQ and SignalR
Now i have updated to the 7.1.7 and also updated the methods that were deprecated. This is how my Startup looks like:
public void ConfigureServices(IServiceCollection services)
{
Utilities utilities = new Utilities(Configuration);
RabbitMQIdentity rabbitMQIdentity = utilities.GetRabbitMQIdentity();
var username = rabbitMQIdentity.UserName;
var password = rabbitMQIdentity.Password;
var hostName = rabbitMQIdentity.HostName;
var portNumber = rabbitMQIdentity.Port;
services.AddHttpClient();
services.AddControllers();
services.AddSignalR(e => {
e.EnableDetailedErrors = true;
e.MaximumReceiveMessageSize = 102400000;
});
services.AddMassTransit(x =>
{
x.AddSignalRHub<NotificationHub>();
x.UsingRabbitMq((context, cfg) =>
{
cfg.Host($"amqp://{username}:{password}#{hostName}:{portNumber}");
cfg.AutoDelete = true;
cfg.Durable = false;
cfg.QueueExpiration = TimeSpan.FromMinutes(10);
cfg.ConfigureEndpoints(context);
});
});
services.AddMassTransitHostedService();
services.AddSingleton<IHostEnvironment>(hostEnvironment);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<LogConfigurationUtility, WebLogConfigurationUtility>();
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.SetIsOriginAllowed((x) => Configuration["CorsWhiteList"].Split(';').Any(x.Contains))
.WithMethods("GET", "POST")
.AllowAnyHeader()
.AllowCredentials();
});
});
}
// 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.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseMiddleware<RequestMiddleware>();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<NotificationHub>("/notificationhub");
});
}
And here is my publish:
logger.LogInformation($"MassTransit publishing group message. GroupName:{paymentCallback.OrderId} ; Message:{paymentCallback.Event}");
IReadOnlyList <IHubProtocol> protocols = new IHubProtocol[] { new JsonHubProtocol() };
publishEndpoint.Publish<Group<NotificationHub>>(
new GroupHub<NotificationHub>()
{
GroupName = paymentCallback.OrderId,
Messages = protocols.ToProtocolDictionary("Notify", new object[] { paymentCallback.Event })
},
context => context.TimeToLive = TimeSpan.FromMinutes(10)
);
And the client:
useEffect(() => {
$(function() {
const connection = new HubConnectionBuilder()
.withUrl(hubUrl)
.configureLogging(LogLevel.Trace)
.build();
// Create a function that the hub can call to broadcast messages.
connection.on("Notify", (status) => {
console.log("entrouuuuuu");
setNotification(status);
});
// Start the connection.
async function start() {
try {
await connection.start();
connection.invoke("InitializeClient", orderId);
console.log("SignalR Connected.");
} catch (err) {
setTimeout(start, 5000);
}
}
start();
});
So now, in RabbitMQ management tools i can see the exchange being created but again it has no bindings and it has also no queues. Does the same issue still persist Chris? Or am i doing something wrong?
Thanks for the help!

Related

I set any CORS setting but doesn't work in my ASP.NET Core 5 Web API

I enable all of the enable CORS settings, but some of these methods of request don't still work like Delete, Put.
This is my code
public void ConfigureServices(IServiceCollection services)
{
var appSettings = Configuration.GetSection("ApplicationSettings");
services.Configure<ApplicationSettingsModel>(Configuration.GetSection("ApplicationSettings"));
services.AddOptions();
services.AddControllersWithViews(options => options.UseGeneralRoutePrefix(appSettings.GetValue<string>("apiRoutePrefix") ?? ""));
services.AddMvc(o => { o.UseGeneralRoutePrefix("api/v{version:apiVersion}"); });
services.AddApiVersioning(config =>
{
config.DefaultApiVersion = new ApiVersion(1, 0);
config.AssumeDefaultVersionWhenUnspecified = true;
config.ReportApiVersions = true;
});
services.AddOurAuthentication(Configuration);
services.AddControllers(c => c.Conventions.Add(new ApiExplorerGroupPerVersionConvention()))
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
};
});
services.AddCors(app =>
{
app.AddPolicy("allowAll", a => a.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().SetIsOriginAllowedToAllowWildcardSubdomains());
});
if (_enableSwagger)
services.AddOurSwagger();
}
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
SwaggerMiddleware(app);
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors("allowAll");
app.UseApiResponseAndExceptionWrapper();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(builder => builder.MapControllers());
}
How can I solve this problem?
You have to change the place of Cors code in your startup. Put this code to the top of ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy("AllowAnyOrigins", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
...........
}
app.UseCors should be placed between app.UseRouting() and app.UseAuthorization()
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
.....
app.UseRouting();
app.UseCors("AllowAnyOrigins");
app.UseAuthentication();
app.UseAuthorization();
....
}
I resolve this.
I change my code to below:
public void ConfigureServices(IServiceCollection services)
{
var appSettings = Configuration.GetSection("ApplicationSettings");
services.Configure<ApplicationSettingsModel>(Configuration.GetSection("ApplicationSettings"));
services.AddOptions();
services.AddControllersWithViews(options => options.UseGeneralRoutePrefix(appSettings.GetValue<string>("apiRoutePrefix") ?? ""));
services.AddMvc(o => { o.UseGeneralRoutePrefix("api/v{version:apiVersion}"); });
services.AddApiVersioning(config =>
{
config.DefaultApiVersion = new ApiVersion(1, 0);
config.AssumeDefaultVersionWhenUnspecified = true;
config.ReportApiVersions = true;
});
services.AddOurAuthentication(Configuration);
services.AddControllers(c => c.Conventions.Add(new ApiExplorerGroupPerVersionConvention()))
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
};
});
services.AddCors(options =>
{
options.AddPolicy("allowAll",
builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed(s => true)
.SetPreflightMaxAge(TimeSpan.FromMinutes(9))
.AllowCredentials()
.Build());
});
if (_enableSwagger)
services.AddOurSwagger();
}
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
SwaggerMiddleware(app);
// app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors("allowAll");
app.UseCorsMiddleware();
// app.UseApiResponseAndExceptionWrapper();
app.UseEndpoints(builder => builder.MapControllers());
}
and this code:
public class CorsMiddleware
{
private readonly RequestDelegate Next;
public CorsMiddleware(RequestDelegate next)
{
Next = next;
}
public Task Invoke(HttpContext context)
{
return BeginInvoke(context);
}
private Task BeginInvoke(HttpContext context)
{
context.Response.Headers.Add("Access-Control-Allow-Credentials", new[] { "true" });
context.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "Origin, X-Requested-With, Content-Type, Accept, Athorization, ActualUserOrImpersonatedUserSamAccount, IsImpersonatedUser" });
context.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "GET, POST, PUT, DELETE, OPTIONS" });
if (context.Request.Method == HttpMethod.Options.Method)
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
return context.Response.WriteAsync("OK");
}
return Next.Invoke(context);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class CorsMiddlewareExtensions
{
public static IApplicationBuilder UseCorsMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CorsMiddleware>();
}
}
Edited from comment suggestion, removed AllowCredentials I noticed OP didn't have that in his code.
Try this, instead of creating a policy, what if you did this:
app.UseCors(x => x
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed(origin => true)); // allow any origin
If this works, it's because you were creating a policy and maybe the policy is not applying. But the above should just work without a policy.
You also mention that only some verbs don't work - it's possible that a cors error is masking a 500 exception, the way to test that is, with a 500 (or other) exception, your code will actually get hit, so you can test that with a breakpoint and if your breakpoint gets hit, it's not CORS even though the browser is saying that.
But most likely you were creating a policy, and not applying the policy to the things that aren't working. THe code above is global.

Middleware Ordering

I have a new .NET Core 3.1 app and am struggling with the concept of Middleware. From reading around, it seems the order of including different middlewares is important. I currently have several problems which I can't seem to solve:
I never see the developer error page, and have to check the event log to see what's happened if there's an error. I just get the blank "error 500" etc pages from Chrome. The custom error pages also never display when there's a 500/400.
The app always tries to redirect me to /Account/Login despite changing this in the cookie settings.
User.IsAuthenticated returns false when the CheckPermissionsAction call is made in Elmah, so I can't access Elmah. The User.IsInRole call works for from controllers though.
This is how I'm bootstrapping the app. It feels like something is overriding the settings:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<DataProtectionTokenProviderOptions>(options =>
options.TokenLifespan = TimeSpan.FromDays(2));
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(30);
});
services.AddControllersWithViews();
services.AddTransient<IUserStore<User>, UserStore>();
services.AddTransient<IRoleStore<IdentityRole>, RoleStore>();
services.AddRazorPages();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = new PathString("/login");
options.AccessDeniedPath = new PathString("/error/denied");
options.LogoutPath = new PathString("/log-off");
options.ExpireTimeSpan = TimeSpan.FromDays(60);
options.SlidingExpiration = true;
options.Cookie.HttpOnly = true;
options.Cookie.Name = "MyCookie";
options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
options.Cookie.SameSite = SameSiteMode.Lax;
});
services.AddIdentity<User, IdentityRole>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequiredLength = 6;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
})
.AddUserStore<UserStore>()
.AddRoleStore<RoleStore>()
.AddDefaultTokenProviders();
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.AddElmah<SqlErrorLog>(options =>
{
options.ConnectionString = Configuration.GetConnectionString("MyApp");
options.CheckPermissionAction = (context)=>{
return context.User.Identity.IsAuthenticated && context.User.IsInRole(RoleHelper.SuperAdmin);
};
options.Path = "/elmah";
});
services.AddSingleton<IAppConfiguration, AppConfiguration>(e => Configuration.GetSection("AppConfig")
.Get<AppConfiguration>());
OptionsConfigurationServiceCollectionExtensions.Configure<DbHelper>(services, Configuration.GetSection("ConnectionStrings"));
services.AddHttpContextAccessor();
}
public void ConfigureContainer(ContainerBuilder builder)
{
// wire up using autofac specific APIs here
builder.Register(context => new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, MyDetailsViewModel>();
})).AsSelf().SingleInstance();
builder.RegisterModule(new RegistrationModule()); // separate assembly, wires up autofac registrations
builder.Register(c =>
{
//This resolves a new context that can be used later.
var context = c.Resolve<IComponentContext>();
var config = context.Resolve<MapperConfiguration>();
return config.CreateMapper(context.Resolve);
})
.As<IMapper>()
.InstancePerLifetimeScope();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
// debugger shows this section is called, but I never see the error page.
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseRouteDebugger();
}
else
{
app.UseExceptionHandler("/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.UseSession();
app.UseElmah();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
var cookiePolicyOptions = new CookiePolicyOptions
{
Secure = CookieSecurePolicy.SameAsRequest,
MinimumSameSitePolicy = SameSiteMode.None
};
app.UseCookiePolicy(cookiePolicyOptions);
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Guest}/{action=Index}/{id?}");
endpoints.MapRazorPages();
endpoints.MapControllers();
});
app.UseStatusCodePages(async ctx =>
{
//Re-execute the request so the user gets the error page
string originalPath = ctx.HttpContext.Request.Path.Value;
switch (ctx.HttpContext.Response.StatusCode)
{
case 401:
//Re-execute the request so the user gets the error page
ctx.HttpContext.Items["originalPath"] = originalPath;
ctx.HttpContext.Request.Path = "/error/denied";
break;
case 412:
ctx.HttpContext.Items["originalPath"] = originalPath;
ctx.HttpContext.Request.Path = "/error/expired-account";
break;
case 404:
ctx.HttpContext.Items["originalPath"] = originalPath;
ctx.HttpContext.Request.Path = "/error/not-found";
break;
case 500:
ctx.HttpContext.Items["originalPath"] = originalPath;
ctx.HttpContext.Request.Path = "/error/not-found";
break;
}
});
DapperExtensions.DapperExtensions.SetMappingAssemblies(new[]
{
Assembly.GetAssembly(typeof(MyApp.Domain.Model.Note)),
Assembly.GetExecutingAssembly()
});
}
In regards to the order of your middleware, there is a problem with it.
There is a section in the Microsoft docs dedicated to the order of middleware, I suggest reading it.
As for your middleware, the correct order would be:
app.UseHttpsRedirection();
app.UseStatusCodePages(async ctx =>
{
// Omitted for brevity.
});
app.UseStaticFiles();
var cookiePolicyOptions = new CookiePolicyOptions
{
// Omitted for brevity.
};
app.UseCookiePolicy(cookiePolicyOptions);
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
// If the app uses session state, call Session Middleware after Cookie
// Policy Middleware and before MVC Middleware.
app.UseSession();
app.UseElmah(); // Not sure about this one. I don't know what it's supposed to do?
app.UseEndpoints(endpoints =>
{
// Omitted for brevity.
});
DapperExtensions.DapperExtensions.SetMappingAssemblies(new[]
{
// Omitted for brevity.
});

Ignore authorization at development environment

I'm recently migrated from ASP .NET Core 2.2 to 3.1, everything works fine but I have an issue with ignoring authorization on Dev environment.
The code that I used in CORE 2.2:
if (env.IsDevelopment())
{
//On Development - ignore authorization
services.AddMvc(opts => { opts.Filters.Add(new AllowAnonymousFilter()); })
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
else
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
This obviously doesn't work on CORE 3.1 but I can't find any equivalent code that works.
Among other things, I tried to use this code (below) with no result.
services.AddControllers(opts =>
{
if (env.IsDevelopment())
{
opts.Filters.Add(new AllowAnonymousFilter());
}
else
{
}
});
Please help me with this.
My relevant code (CORE 3.1):
public void InstallServices(IServiceCollection services, IConfiguration configuration, IWebHostEnvironment env, ILogger logger)
{
services.AddControllers(opts =>
{
if (env.IsDevelopment())
{
opts.Filters.Add(new AllowAnonymousFilter());
}
else
{
}
});
services.AddAutoMapper(typeof(Startup));
var jwtSettings = new JwtSettings();
configuration.Bind(nameof(JwtSettings), jwtSettings);
services.AddSingleton(jwtSettings);
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = jwtSettings.PrivateSigningSecretKey,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services.AddSingleton(tokenValidationParameters);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.TokenValidationParameters = tokenValidationParameters;
});
services.AddAuthorization(options =>
{
options.AddPolicy(Authorizations.RequireAdminOrManagerRole,
policy => policy.RequireRole(Authorizations.Admin, Authorizations.Manager));
});
//deleted Swagger setup
}
public void InstallConfiguration(IApplicationBuilder app, IWebHostEnvironment env, IConfiguration configuration, ILogger logger)
{
//deleted Swagger setup
app.UseHttpsRedirection();
app.UseMiddleware(typeof(ErrorHandlingMiddleware));
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
I found something that works for me.
Let me know if you have a better solution
My Configuration:
app.UseHttpsRedirection();
app.UseMiddleware(typeof(ErrorHandlingMiddleware));
app.UseRouting();
if (env.IsStaging() || env.IsDevelopment())
{
//on staging/development dont require authentication
app.Use(async (context, next) =>
{
// Set claims for the test user.
var claims = new[] { new Claim("role", "Admin"), new Claim("sub", "some guid") };
var id = new ClaimsIdentity(claims, "DebugAuthorizationMiddleware", "name", "role");
// Add the test user as Identity.
context.User.AddIdentity(id);
// User is now authenticated.
await next.Invoke();
});
}
else
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
Credit:
Programmatically add [AllowAnonymous] attribute to all my controller methods

Migration from .net core 2.2 to 3.1 - api 404 error

I develop a SPA with .net core at server side (and Angular for the clients).
After migration from .net core 2.2 to 3.1 the api systematically responds with error 404. I have no clue where to get into the problem.
Here is the content of the Startup.cs. Thanks a lot for any hint!
namespace HostSystems.API
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).SetCompatibilityVersion(CompatibilityVersion.Latest)
.AddNewtonsoftJson(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore;
opt.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
opt.SerializerSettings.SerializationBinder =
new CustomJsonSerializationBinder();
opt.SerializerSettings.MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead;
})
;
services.AddCors();
services.Configure<CloudinarySettings>(Configuration.GetSection("CloudinarySettings"));
services.AddScoped<IHsRepository, HsRepository>();
services.AddScoped<PermissionService>();
services.AddTransient<Seed>();
services.AddTransient<GeneralMap>();
services.AddTransient<EventMap>();
services.AddTransient<LegalEntityMap>();
services.AddTransient<UserMap>();
services.AddTransient<ProviderMap>();
services.AddTransient<LicenseeMap>();
services.AddTransient<SItemMap>();
services.AddScoped<LogUserActivity>();
services.AddSignalR();
}
// 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.UseExceptionHandler(builder =>
{
builder.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
context.Response.AddApplicationError(error.Error.Message);
await context.Response.WriteAsync(error.Error.Message);
}
});
});
}
app.UseCors(x =>
x.WithOrigins("http://localhost:5000", "http://localhost:4200")
.AllowAnyHeader().AllowAnyMethod().AllowCredentials());
// app.UseForwardedHeaders(new ForwardedHeadersOptions
// {
// ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
// });
app.UseAuthentication();
app.UseDefaultFiles();
app.UseStaticFiles();
// This method is obsolete and will be removed in a future version
app.UseSignalR(routes =>
{
routes.MapHub<TestHub>("/loopy");
});
// This got depreciated with .net core v 3.1 -> for now there is no fallback, you have to find the alternative solution
// app.UseMvc(routes =>
// {
// routes.MapSpaFallbackRoute(
// name: "spa-fallback",
// defaults: new { controller = "Fallback", action = "Index" }
// );
// });
}
}
}
As I am not sure where to start, please let me know what else could be helpful.

CORS request with GET works, but POST does not work - ASP .NET CORE and using JWT Auth

if I am doing a GET request, it works just fine, however, a POST request does not work. I've searched for a few hours and can't figure it out, basically every suggestion I've seen, I've already tried.
Here's my Configure function:
// 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()) //Remember to switch launchSettings.json to 'Production'
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
if (IsDebug) //Allows testing by using a frontend at localhost:8080 - Configure the port to your frontend port.
{
app.UseCors(builder => builder
.WithOrigins("http://localhost:8080")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
);
}
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute("Error", "{*url}", new { controller = "Home", action = "Index" });
});
}
I am registering the CORS middleware before any other.
For the methods I'm trying to call, I have [Authorize] placed on the class, and [HttpPost] placed on the methods. If I switch it to [HttpGet] it works.
Here's an example of how I'm calling my API:
var headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + jwtToken
}
// Don't burden the server with unnecessary calls to the API if we don't even have a token.
if (jwtToken) {
var result = await axios.post('https://localhost:44377/api/Playground/GetMenuItems', { headers: headers } );
if (result.data === -1) { throw -1 }
return result.data;
}
This is what my Network Tools looks like:
I believe the first one is the preflight as the request method is OPTIONS, and it returns 204:
Here is the failed POST request, failing with 401:
Notice how the bearer token is not sent in the request. However, it is sent in the request payload.
I'm absolutely scratching my head here, I would really appreciate it if anyone has some insight!
Cheers.
P.S. Here's my ConfigureServices function:
public void ConfigureServices(IServiceCollection services)
{
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.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(ConnectionString));
services.AddDefaultIdentity<IdentityUser>().AddEntityFrameworkStores<ApplicationDbContext>();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); // => remove default claims
services
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = Configuration["JwtIssuer"],
ValidAudience = Configuration["JwtIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtKey"])),
ClockSkew = TimeSpan.Zero // remove delay of token when expire
};
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
if (IsDevelopment)
{
IsDebug = Configuration.GetValue<bool>("DEBUGGING");
if (IsDebug)
{
services.AddCors(); //Dangerous in prod.
}
_connectionString = Configuration["PlaygroundConnectionString"];
}
else
{
// Get prod secrets
}
}
Axios.post requires 3 parameters - you are only providing two. And the second parameter is the data object to send as the body of the post command, which explains why your headers are being sent in the body.
From the axios documentation:
axios.post(url[, data[, config]])
Try changing your code to
var result = await axios.post('https://localhost:44377/api/Playground/GetMenuItems', null, { headers: headers } );
The Axios.get command only required 2 parameters, which is why that one is working.

Resources