I have a token endpoint that is passed a username and password grant type to authenticate users. This token endpoint is called from an AngularJS service that is part of my MVC web front end. When I call the service I get the following error
XMLHttpRequest: Network Error 0x80070005, Access is denied.
This seems to be a CORS problem. I did the following to resolve this problem with no luck thus far
I added the app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); to my Startup.cs file for my token web service.
public class Startup
{
public void Configuration
(
IAppBuilder app
)
{
ConfigureOAuth(app);
var config = new HttpConfiguration();
WebApiConfig.Register(config);
config.Filters.Add(new AuthorizeAttribute());
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
private void ConfigureOAuth
(
IAppBuilder app
)
{
app.UseOAuthAuthorizationServer(new OAuthServerOptionsProvider().Provide());
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
I also have a class that is overriding the OAuthAuthorizationServerProvider, within the GrantResourceOwnerCredentials method I have the following code to add all origins to the response headers
public override async Task GrantResourceOwnerCredentials
(
OAuthGrantResourceOwnerCredentialsContext context
)
{
System.Web.HttpContext.Current.Response.Headers.Add("Access-Control-Allow-Origin", "*");
// Other code left out to keep this short
}
In fiddler I can see that the response header was successfully added
Is there something I'm missing here?
Update
Here is my request headers
First of all I have to point out that the comments made by #maurycy helped me find the solution in the comments of this stackoverflow post.
This post explains that the app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); in the Startup.cs file should be moved to the top of the method and that the System.Web.HttpContext.Current.Response.Headers.Add("Access-Control-Allow-Origin", "*"); should be removed from the GrantResourceOwnerCredentials class. So I changed the code in my question to look something like this,
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
var config = new HttpConfiguration();
WebApiConfig.Register(config);
config.Filters.Add(new AuthorizeAttribute());
app.UseWebApi(config);
}
private void ConfigureOAuth(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseOAuthAuthorizationServer(new OAuthServerOptionsProvider().Provide());
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
I also have changed the class that is overriding the OAuthAuthorizationServerProvider
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
// Remove the response header that was added
// Other code left out to keep this short
}
Related
I can't continue the flow due to the error in the Startup.cs file on the ASP.NET Core side in the service post operations I made using axios on the React.JS side.
When I have only one Singleton object in Startup.cs it doesn't get any errors and post/get operations work correctly. For example like this,
services.AddSingleton();
However, when I wanted to post/get multiple different pages, I couldn't update the Startup.cs file and did it right. As an example, when I write as follows, none of my requests are working.
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
I want all requests made by the client (React.JS) to work. How can I edit my Startup.cs file to receive all my service requests?
When I make a request on the coupon page, I get the following error.
https://localhost:5001/api/coupons/test 404 (Not Found)
ASP.NET Core
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMvc();
services.AddSingleton<VendorRegistrationService>();
services.AddSingleton<CalculateShippingService>();
services.AddSingleton<CouponsService>();
services.AddCors(o => o.AddPolicy("ReactPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
//.AllowCredentials();
}));
}
VendorRegistrationController.cs
Produces("application/json")]
[Route("api/[controller]")]
[ApiController]
[EnableCors("ReactPolicy")]
public class VendorRegistrationController : ControllerBase
{
private readonly ILogger<VendorRegistrationController> _logger;
public readonly VendorRegistrationService vendorRegistrationService;
private static List<VendorRegistration> vendorRegistrationList = new List<VendorRegistration>();
public VendorRegistrationController(VendorRegistrationService vendorRegistrationService, ILogger<VendorRegistrationController> logger)
{
this.vendorRegistrationService = vendorRegistrationService;
_logger = logger;
}
[Route("test")]
[HttpPost]
public VendorRegistration Index([FromBody] VendorRegistration vendorRegistration)
{
vendorRegistrationService.Create(vendorRegistration);
return vendorRegistration;
}
ReactJS - RegistrationForm.tsx
const handleFormSubmit = async (values: any): Promise<any> => {
const response = axios.post('https://localhost:5001/api/vendorregistration/test',
values).then(res=>{
console.log('res=>',res);
}).catch(function(error) {
console.log(error);
})
};
UPDATED
#Panagiotis Kanavos I updated the development after your comment. The service call for VendorRegistration is currently running. However, when I make a service request for the Coupon, I keep getting the 404 (Not Found) error.
POST https://localhost:5001/api/coupons/test 404 (Not Found)
CouponsController.cs
[Route("test")]
[HttpPost]
public Coupons PostTest([FromBody] Coupons coupons)
{
couponsService.Create(coupons);
return coupons;
}
VendorRegistrationController.cs
[Route("test")]
[HttpPost]
public VendorRegistration PostTest([FromBody] VendorRegistration vendorRegistration)
{
vendorRegistrationService.Create(vendorRegistration);
return vendorRegistration;
}
Folks I'm developing a fullstack React.js - ASP.NET Core 5 application. The backend is done (fully tested). Of course it includes a CORS policy to allow request from the client side, but when I'm trying to send a request from react using axios, axios throws a network error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://localhost:5001/api/customers. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200.
I see the server sends correct responses (I can even debugged the server) but axios stills failing. I only tried to solved it by including a proxy in package.json:
"proxy": "https://localhost:5001"
I'm going to include my app.js request code and startup.cs code, since it contains the CORS Policy:
Client
const fetchCustomers = async () => {
const customers = await axios.get(customersApiUrl);
console.log(customers);
setCustomers(customers);
setIsLoading(false);
};
Server
public class Startup
{
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
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.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("http://localhost:3000/");
builder.AllowAnyHeader();
builder.AllowAnyMethod();
});
});
services.AddControllers();
services.AddDbContextPool<TwinEnginesDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Standard")));
services.AddScoped<ICustomerTypeRepository, CustomerTypeRepository>();
services.AddScoped<ICustomerTypeService, CustomerTypeService>();
services.AddScoped<ICustomerRepository, CustomerRepository>();
services.AddScoped<ICustomerService, CustomerService>();
services.AddAutoMapper(Assembly.GetExecutingAssembly());
}
// 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(MyAllowSpecificOrigins);
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Edited: I'm including the CustomersController.cs code plus the details from the HTTP request.
CustomersController.cs
[Route("api/[controller]")]
[ApiController]
public class CustomersController : ControllerBase
{
private readonly ICustomerService _customerService;
private readonly ICustomerTypeService _typeService;
private readonly IMapper _mapper;
public CustomersController(ICustomerService customerService, ICustomerTypeService typeService, IMapper mapper)
{
this._customerService = customerService ?? throw new ArgumentNullException(nameof(customerService));
this._typeService = typeService ?? throw new ArgumentNullException(nameof(typeService));
this._mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
}
// [EnableCors("MyAllowSpecificOrigins")]
[HttpGet("")]
public async Task<ActionResult<IEnumerable<CustomerDTO>>> GetCustomers()
{
var customers = await _customerService.GetAllAsync();
var response = _mapper.Map<IEnumerable<CustomerDTO>>(customers);
return Ok(response);
}
}
Request image:
Any ideas, thoughts? I really need your help folks, this is a technical assignment for a dev job.
Try to use the setting without the
slash at the end: builder.WithOrigins("http://localhost:3000");
After the change please do a clean and rebuild the project, as it might be a thing.
Also, you don't need a proxy setting on the JS side.
P.S. A mode for the request might not be set properly on the Axios side. In case the solution above doesn't work try to use:
axios(requestURL, { mode: 'cors' })
Try to add this attribute to your controllers
[EnableCors(MyAllowSpecificOrigins)]
I am implementing a simple authentication program using AngularJS frontend and Spring Boot backend. I am facing an issue while sending the login request. When the relavent request sent, following error prints in the console
Error:-
Access to XMLHttpRequest at 'http://localhost:8080/rest/users/user' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
POST http://localhost:8080/rest/users/user net::ERR_FAILED
Same error occurred when sending the request for registration function. Then I found a solution of adding #CrossOrigin(origins = "http://localhost:4200") to the controller. It fixed the error occured during the registration.
Now the problem is even though I have written the login method in the same controller, it gives me the error while trying to log in and will not gives any error if I try to register new user.
Below is the implementation of the backend
Repository :-
import com.app.cashier.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.web.bind.annotation.CrossOrigin;
import java.util.List;
public interface UsersRepository extends JpaRepository<User, Integer> {
List<User> findByUserName(String userName);
}
Resource :-
package com.app.cashier.resource;
import com.app.cashier.model.User;
import com.app.cashier.repository.UsersRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#CrossOrigin(origins = "http://localhost:4200") //<------------ I addded #CrossOrigin
#RestController
#RequestMapping(value = "/rest/users")
public class UserResource {
#Autowired
UsersRepository usersRepository;
#GetMapping(value = "/all")
public List<User> getAll(){
return usersRepository.findAll();
}
#GetMapping(value = "/user") //<----- Login function. This still gives the above error
public List<User> getUser(#RequestBody final User user){
return usersRepository.findByUserName(user.getUserName());
}
#PostMapping(value = "/load") //<----- Registration function. This gives no error after adding #CrossOrigin
public List<User> persist(#RequestBody final User user){
usersRepository.save(user);
return usersRepository.findAll();
}
}
AngularJS frontend request
login(userName) {
console.log(userName)
return this.http.post<any>(`http://localhost:8080/rest/users/user`, { userName })
.pipe(map(user => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('currentUser', JSON.stringify(user));
this.currentUserSubject.next(user);
console.log(user);
return user;
}));
}
How can I overcome this issue. Massive thanks!
Providing the #CrossOrigin annotation at the controller level should enable cross origin for all the methods under that controller.lar request so it might be because of some additional headers that you are adding for that particular request so try like :
#CrossOrigin(origins = "http://localhost:4200", allowedHeaders = "*")
#RestController
#RequestMapping(value = "/rest/users")
public class UserResource {
//Your code
}
If still having issues then Could you share the url and the headers that you are using to login the new user ?. Also , try having a global cors configuration instead of a controller level one and provide fine grained properties like the methods that you want to expose. Provide the following in a configuration class :
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:4200");
}
};
}
Similar : CORS policy conflict in Spring boot
The default Blazor approach to 404 is to create a soft 404 in App.razor, but I would like to adhere to search engine best practices to actually return the 404 status code while displaying a 404 page on Azure.
I tried to remove the element in App.razor to see if I could force a 404, however, that did not compile.
Any suggestions?
I use this code. It works well.
I created Error404Layout. I use this layout for NotFound part.
<Router AppAssembly="#typeof(Program).Assembly" PreferExactMatches="#true">
<Found Context="routeData">
<RouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="#typeof(Error404Layout)">
<h2>Oops! That page can't be found.</h2>
</LayoutView>
</NotFound>
Error404Layout content below
#inherits LayoutComponentBase
#using Microsoft.AspNetCore.Http
#inject IHttpContextAccessor httpContextAccessor
#Body
#code {
protected override void OnInitialized()
{
httpContextAccessor.HttpContext.Response.StatusCode = 404;
}
}
You have to add this code in startup.cs / services method
public void ConfigureServices(IServiceCollection services)
{
....
services.AddHttpContextAccessor();
}
I can see 404 status code
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-3.1 does state:
Additionally, again for security reasons, you must not use
IHttpContextAccessor within Blazor apps. Blazor apps run outside of
the context of the ASP.NET Core pipeline and the HttpContext isn't
guaranteed to be available within the IHttpContextAccessor, nor it is
guaranteed to be holding the context that started the Blazor app.
So you need to create a wrapper around IHttpContextAccessor to limit this when Blazor is being server side prerendered.
I was able to return 404 http status codes when using server side prerendering in the Blazor WebAssembly App (ASP.Net Core Hosted) Template
When I pointed the browser to http://localhost/fetchdata it returned a page. I wanted this to return a 404 status code as an example. This was possible using dependency injection and a stub class.
In BlazorApp1.Client I added a IResponse.cs file:
namespace BlazorApp1.Client {
public interface IResponse {
void SetNotFound();
}
}
In BlazorApp1.Client I added a ResponseStub.cs file:
namespace BlazorApp1.Client {
public class ResponseStub : IResponse {
public void SetNotFound() {
// Do nothing if we are browser side
}
}
}
In FetchData.razor in BlazorApp1.Client I added:
#inject BlazorApp1.Client.IResponse Response
and in the code section:
protected override void OnInitialized() {
Response.SetNotFound();
}
In Program.cs in BlazorApp1.Client I added:
builder.Services.AddScoped<IResponse, ResponseStub>();
Then in BlazorApp1.Server, in Startup.cs I added under ConfigureServices:
services.AddHttpContextAccessor();
services.AddScoped<IResponse, Response>();
and under Configure I replaced:
endpoints.MapFallbackToFile("index.html");
with:
endpoints.MapFallbackToPage("/_Host");
Then create the Server implementation of IResponse in Response.cs:
using BlazorApp1.Client;
using Microsoft.AspNetCore.Http;
using System.Net;
namespace BlazorApp1.Server {
public class Response : IResponse {
private readonly IHttpContextAccessor _httpContextAccessor;
public Response(IHttpContextAccessor accessor) {
_httpContextAccessor = accessor;
}
public void SetNotFound() {
_httpContextAccessor.HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
}
}
}
And finally I create a _Host.cshtml file in BlazorApp1.Server/Pages:
#page "/fallback"
#namespace BlazorPrerendering.Server.Pages
#using BlazorApp1.Client
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#{
Layout = "_Layout";
}
<app>
<component type="typeof(App)" render-mode="ServerPrerendered" />
</app>
<div id="blazor-error-ui">
An unhandled error has occurred.
Reload
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
#ergin-Çelik Your solution works very well. I only had to add the following check because I got an error that the response had already been sent.
I have add this as answer because code formatting is better here.
protected override void OnInitialized()
{
if (_httpContextAccessor.HttpContext != null &&
!_httpContextAccessor.HttpContext.Response.HasStarted)
{
_httpContextAccessor.HttpContext.Response.StatusCode = (int) HttpStatusCode.NotFound;
}
}
I've got an app which is hosting simultaneously Identity Server 4 and a client app (Vue) which uses a couple of rest services defined in an area for managing the site. The idea is that users associated with a specific role can access the client app and call the rest services for performing the actions.
Currently, my problem is that when the api return 302 when the user doesn't belong to the admin role. I'd like to change this to a 401, but I'm having some problems with it.
If this was a simple aspnet core app, then I'd simply pass a lambda to the OnRedirectToLogin property of the cookie handler that takes care of the request. Unfortunately, IS4 will only allow me to set a couple of basic settings of the cookie (expiration and sliding). The same docs say that I can override the cookie handler. So, I've tried doing the following:
services.AddIdentityServer()
... // other configurations
services.AddAuthentication(sharedOptions => {
sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;//IdentityServerConstants.ExternalCookieAuthenticationScheme;
sharedOptions.DefaultChallengeScheme = IdentityServerConstants.SignoutScheme;
})
... //other external providers...
.AddCookie( CookieAuthenticationDefaults.AuthenticationScheme, options => {
options.Events = new CookieAuthenticationEvents {
OnRedirectToLogin = ctx => {
if (ctx.Request.Path.StartsWithSegments("/Admin", StringComparison.OrdinalIgnoreCase)) {
ctx.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
}
return Task.CompletedTask;
};
});
I expected to seem my handler being called whenever a request is redirected to the login page, but it never happens. Can anyone help?
Thanks
EDIT: just to add that I'm also using aspnet identity for managing the user accounts...
Posting the answer here in case anyone is interested...
After some digging, I've found that using identity means that you can't customize the cookie handler by doing what I was doing. Fortunately, the ConfigureAuthenticationEvent that can be configured by the ConfigureApplicationCookie extension method already does the right thing: if it detects that the current request is an AJAX call, it will return 401; if not, it will return 302. And here was the problem: the request made from the vue client wasn't being considered an AJAX request because it wasn't setting the X-Request-With header to XMLHttpRequest.
So, all it was required was to configure axios to set the header in all the calls:
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
I wrote a middleware sometime ago for this exact purpose and never looked back so if you don't find better solution, perhaps the solution can help you as well:
public class RedirectHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RedirectHandlingMiddleware> _logger;
public RedirectHandlingMiddleware(RequestDelegate next, ILogger<RedirectHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
await HandleRedirect(context, ex);
await _next(context);
}
private Task HandleRedirect(HttpContext context)
{
if (context.Request.Path.StartsWithSegments("/Admin", StringComparison.OrdinalIgnoreCase) && context.Response.StatusCode == 302)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
return Task.CompletedTask;
}
}
Just need to register in Startup.cs:
app.UseAuthentication();
app.UseMiddleware<RedirectHandlingMiddleware>();