Spring Security OAuth2: CORS preflight channel did not succeed - oauth

I was receiving this error while making a call to '/oauth/token' when I was making an HTTP call to the server instance running on my own system. I fixed this by creating a filter like this:
#Component
public class SimpleCORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
HttpServletRequest httpServletRequest = (HttpServletRequest) req;
if (httpServletRequest.getMethod() != "OPTIONS") {
chain.doFilter(req, res);
} else {
// In case of HTTP OPTIONS method, just return the response
}
}
I have added it as a filter in WebConfigurer:
private void initCORSFilter(ServletContext servletContext, EnumSet<DispatcherType> disps) {
log.debug("Registering CORS Filter");
FilterRegistration.Dynamic corsFilter = servletContext.addFilter("corsFilter", new SimpleCORSFilter());
Map<String, String> parameters = new HashMap<>();
corsFilter.setInitParameters(parameters);
corsFilter.addMappingForUrlPatterns(disps, true, "/*");
corsFilter.setAsyncSupported(true);
}
I am getting this error in FireFox:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://182.176.221.94:9091/ams/oauth/token. (Reason: CORS preflight channel did not succeed).
In short I was making sure the the preflight OPTIONS call always gets a response to proceed ahead. This was working on my own system, but now that the server instance is deployed on a Linux server, I am again getting this issue. And I am getting this only on calling 'oauth/token', everything other call is working fine.
What can I possibly do to get rid of this. Any help?

Your filter does not need to be annotated with #Component and it should be mapped with adequate url pattern in WebConfigurer class like other filters used in JHipster.
Also your filter should not break the filter chain as it does for OPTIONS. It's not consistent to allow OPTIONS method in header and then to not return the headers if you are processing an OPTIONS request.

The problem was that I was using != comparison for if (httpServletRequest.getMethod() != "OPTIONS". I changed it to if (!httpServletRequest.getMethod().equalsIgnoreCase("OPTIONS")) instead and it worked. This could have something to do with the fact that I tested on local machine while running the codebase but created a WAR file out of it and deployed on the server where it didn't work. I am not sure what exactly is the reason, but this fixed the issue.

Related

Is there a way to change http request method in netflix zuul routing filter?

I'm trying to trasform http GET method call from legacy api server built with MVC1 pattern to new restful api server without any change of front-end source code using netflix zuul and eureka.
I added zuul pre filter transforming legacy url to restful convention url working after PreDecorationFilter and it works fine.
But now I'm facing problem converting the GET method to proper method like POST, PUT, DELETE by distinguising url so that the requests are properly mapped in spring controller via #GetMapping/#PostMapping/#PutMapping/#DeleteMapping.
I looked into SimpleRoutingFilter that handles HttpClient but
Because of environmental constraint, I have to use eureka service id to route to the new api server and that means I should use RibbonRoutingFilter which is quite complicated to find out a right place to this operation in.
So, is this possible to change http method or make new http request before RibbonRoutingFilter?
If possible can you please suggest where is the right place to do that or some reference?
Many thanks!
======================================================================
Milenko Jevremovic,
Would you please tell me more detail about using Feign?
I defiend #FeignClient like below
#PostMapping(value = "{url"}, consumes = "application/json")
ResponseEntity<?> postMethod(#PathVariable("url") String url);
and to get query parameters to request body for POST In zuul pre filter,
after transform logic from GET request url to POST new restful url ...
byte[] bytes = objectMapper.writeValueAsBytes(ctx.get("requestQueryParams"));
ctx.setRequests(new HttpServletRequestWrapper(request) {
#Override ..getMethod
#Override ..getContentLength
#Override ..getConentLengthLong
#Override
public ServletInputStream getInputStream() {
return new ServletInputStreamWrapper(bytes);
}
}
ResponseEntity<?> response feignClient.post(transformedNewApiUri);
and set RequestContext code that you suggested ....
and controller of new api server is like,
#PostMapping
ResponseEntity<model> post(#RequestBody req..)
It comes to controller fine but when I see the http request in post method of controller,
There is no request body for parameters.
(HttpServleterRequest getInputStream shows empty)
The request data set in zuul pre filter by HttpServletRequestWrapper is
not used in Feign maybe...?
Would you please get me more idea setting request body when changing GET query
to POST constructor for using Feign?
It is not possible to change method of HttpServletRequest, but it's possible to replace request in RequestContext. HttpServletRequestWrapper appears to be very helpful:
static class PostHttpServletRequest extends HttpServletRequestWrapper {
public PostHttpServletRequest(HttpServletRequest request) {
super(request);
}
#Override
public String getMethod() {
return "POST";
}
}
So method run can be rewritten as following:
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
HttpServletRequest requestWrapper = new PostHttpServletRequest(request);
ctx.setRequest(requestWrapper);
return null;
}
After doing some research did not find any built in solution.
But what comes in my mind you can use Feign client in your Pre filter, get the response, set the response and return it immediately to client from your Pre filter.
You can set Feign client url or your service id, like it is explained in the docs, it uses ribbon as well .
Change response in your run method like:
...
RequestContext ctx = RequestContext.getCurrentContext();
ctx.setResponseStatusCode(your_code);
ctx.setResponseBody(new_body);
ctx.setSendZuulResponse(false);
return null

Preflight request fails on Chrome, Safary, Opera

I´m struggling with CORS requests on an Springsecurity backen project + Angulajs frontend.
CORS requests are working fine on IE (also with curl, wget and python requests) but miserably fail on Chrome and Safary because of Preflight bad request.
I know that those Browsers are blocking CORS POSTs, making the request empty as soon as the reach the backend, in fact I don’t see any data when I log out the request from backend. I tried every possible combination of:
Frontend side:
1) $http(method: POST)
2) $http.post(
3) Adding flags: Access-Control-Allow-Origin, Access-Control-Expose, etc.
4) Adding all possible header combination: ‘Content–Type’:’application/
Browser side:
1) Start chrome with flag: --disable-web-security
2) Installing Chrome extension CORS
Backend side:
1) Spring Security Disable csfr
2) Spring Security Permit all
3) Spring Security HttpMethod.OPTION
4) Set up a CORS Filter that accept all origins: “*”
5) Activated CORS framework for spring extending WebMvcConfigurerAdapter class.
Nothing, NHOTING worked for me!
I discussed this issue in another post: CORS POST request fails on Chrome, Safari and Firefox
I´m still unable to perform CORS requests, this is now I major issue and I suspect the problem is in LoginFilter:
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
private TokenAuthenticationService tokenAuthenticationService;
public JWTLoginFilter(String url, AuthenticationManager
authenticationManager) {
super(new AntPathRequestMatcher(url));
setAuthenticationManager(authenticationManager);
tokenAuthenticationService = new TokenAuthenticationService();
}
#Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws AuthenticationException, IOException, ServletException {
try {
ServletInputStream inputStream = httpServletRequest.getInputStream();
httpServletRequest.getCharacterEncoding();
AccountCredentials credentials = null;
ObjectMapper mapper = new ObjectMapper();
credentials = mapper.readValue(inputStream, AccountCredentials.class);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword());
return getAuthenticationManager().authenticate(token);
} catch (JsonMappingException e) {
e.printStackTrace();
throw e;
} catch (JsonParseException e) {
e.printStackTrace();
throw e;
} catch (AuthenticationException e) {
e.printStackTrace();
throw e;
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication)
throws IOException, ServletException {
AccountCredentials cred = (AccountCredentials) authentication.getPrincipal();
tokenAuthenticationService.addAuthentication(response, cred);
}
}
EDIT
the exact error on Google Chrome is:
:8000/#!/login:1 XMLHttpRequest cannot load http://localhost:8080/myApp/login. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. The response had HTTP status code 403.
So I found that it does not has ANYTHING to do with the request headers, but the problems are the response headers.
To make the preflight passing through, all response headers have to be mapped, as example:
response.addHeader("Access-Control-Expose-Headers", "xsrf-token, Authorization, Barer, Token");
The preflight request is sent AUTOMATICALLY with verb option by browser itself BEFORE the real request is sent.
You must configure your server to send response with some headers when this preflight request is sent. With spring security you can use :
#Provider
#Component
public class CrossDomainContainerResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext containerRequestContext,
ContainerResponseContext containerResponseContext) throws IOException {
containerResponseContext.getHeaders().add("Access-Control-Allow-Origin", "YOUR FRONTEND URI");
containerResponseContext.getHeaders().add("Access-Control-Allow-Headers",
"Access-Control-Allow-Origin");
containerResponseContext.getHeaders().add("Access-Control-Allow-Credentials", "true");
containerResponseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
}
If you are using XML config, you can also use the <cors /> tag.
The --disable-web-security never worked for me on Chrome. But it worked on Vivaldi Browser.

CORS: Rest API with ASP.NET 4.5.1 MVC

I have a problem allowing cross site scripting for my application. GET requests work fine when i try to do a POST i get:
XMLHttpRequest cannot load http://localhost:49187/api/CampaignRegistration. The request was redirected to 'http://localhost:49187/Authentication/UnAuthorized?ReturnUrl=%2Fapi%2FCampaignRegistration', which is disallowed for cross-origin requests that require preflight.
My preflight request returns 200 OK (without the authentication header) but my actual request returns 302 Not Found (which contains my authentication header).
My preflight request looks like this:
Access-Control-Request-Headers:accept, content-type
Access-Control-Request-Method:POST
and the response is:
Access-Control-Allow-Headers:content-type
Access-Control-Allow-Origin:*
And my request payload headers are:
{Cache-Control: "no-cache", Authorization: "Basic XXXXX"}
I have enabled CORS in my WebApiConfig.cs like this (i will change the origin: * when it actually works). I removed everything related to CORS in my web.config.
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
My WebApi Controllers all extends a BasicApiController which includes a custom authenticate attribute which looks like this:
protected override bool IsAuthorized(HttpActionContext actionContext)
{
try
{
if (HttpContext.Current.Request.Headers.AllKeys.Contains("Authorization"))
{
string authHeader = HttpContext.Current.Request.Headers["Authorization"];
string cred =
Encoding.UTF8.GetString(Convert.FromBase64String(authHeader.Substring("Basic ".Length)));
string[] parts = cred.Split(':');
string userName = parts[0];
string password = parts[1];
if (userName == _configRepository.WebApiUsername && password == _configRepository.WebApiPassword)
{
return true;
}
}
return false;
}
catch (Exception ex)
{
return false;
}
}
My frontend was made using backbone.js but i really don't think the issue is on the front end side. I have tried changing the web.config to no avail, from what i gathered nothing is needed in it after enabling CORS like i did. I tried bypassing the authentication for OPTIONS methods since my application does not use OPTIONS (outside of preflight requests) but, as expected, that did nothing as the preflight request is ok even without the authentication header. If anyone has any ideas it would be greatly appreciated !
Seems the issue was client side. The authorization parameter was in the payload not in the headers.... /facepalm.

CORS issue on Swagger UI

Can someone tell me why am i getting these errors.
GET http://127.0.0.1:9000/api-docs/service.json
200 OK 4ms swagger-ui.js (line 30261)
Unable to Load SwaggerUI /api-docs/ (line 83)
Cross-Origin Request Blocked: The Same Origin Policy disallows
reading the remote resource at http://127.0.0.1:9000/api-
docs/service.json. This can be fixed by moving the resource to the
same domain or enabling CORS.
uncaught exception: Can't read from server. It may not have the
appropriate access-control-origin settings.
I am trying to run Swagger UI on port say 9090 and the Swagger API documentation at 9000 and trying to display the documentation in the UI.
I have added the CORS filter on API Documentation server (port 9000) as follows.
FilterHolder cors = swaggerUIContext.addFilter(CrossOriginFilter.class,"/*",EnumSet.of(DispatcherTyp‌ e.REQUEST));
cors.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
cors.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "");
cors.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD");
cors.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "Content-Type, api_key, Authorization");
The Request and Response headers in firefox V33.0 are
Response Headers
Content-Length 428
Content-Type application/json
Request Headers
Accept application/json;charset=utf-8,*/*
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Connection keep-alive
Host localhost:9000
Origin http://localhost:9090
Referer http://localhost:9090/api-docs/
User-Agent Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:33.0)
Gecko/20100101 Firefox/33.0
Here is how I am setting the CORS on server
final ResourceHandler swaggerUIResourceHandler = new ResourceHandler();
swaggerUIResourceHandler.setResourceBase("target/classes/api-docs");
final ServletContextHandler swaggerUIContext = new ServletContextHandler();
swaggerUIContext.setContextPath("/api-docs");
swaggerUIContext.setHandler(swaggerUIResourceHandler);
FilterHolder cors = swaggerUIContext.addFilter(CrossOriginFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
cors.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
cors.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
cors.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD");
cors.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "Content-Type, api_key, Authorization");
ServletHolder def = new ServletHolder("default", DefaultServlet.class);
def.setInitParameter("resourceBase","./http/");
def.setInitParameter("dirAllowed","false");
swaggerUIContext.addServlet(def,"/");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { swaggerUIContext, new DefaultHandler() });
server.setHandler(handlers);
Did you do something funky with the json file?
I faced the same error, while trying to modify my JSON file and seeing the changes on Chrome. Ensure that the json itself is not breaking somehow, an extra bracket, comma and so on. I started from scratch with one of the example json from the swagger live demo and I was fine. I know it's a task but worked for me, atleast the UI loaded!
You can also go through the swagger ui readme, CORS support section
If you are using Spring Security
please add this code.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().configurationSource(request ->
{
CorsConfiguration cors = new CorsConfiguration();
cors.setAllowedMethods(
Arrays.asList(HttpMethod.DELETE.name(),HttpMethod.GET.name(), HttpMethod.POST.name()));
cors.applyPermitDefaultValues();
return cors;
}).httpBasic();
}
Explanation:
In the above CorsConfiguration class I'm using two methods.
cors.applyPermitDefaultValues();
cors.setAllowedMethods(List of Request Type name);
This method cors.applyPermitDefaultValues(); will allow cross origin request for all hosts.
Usually this method support cross origin support for these 3 request type methods GET,HEAD and PUT.
If your API exposing PUT , DELETE or any other request methods. Then you need to override it by this cors.setAllowedMethods();
I was able to get it working by adding the following method to my Application. Note this also opens up the API so that you can accept CrossOrigin requests. The details of the addMapping() bit are up to you, but this example opens up everything from everywhere. Here is the full class.
#SpringBootApplication
#EnableSwagger2
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
}
};
}
}
I also had this issue and after inspecting the headers in the pet-store example, I found that "Access-Control-Allow-Headers" needs "Content-Type, api_key, Authorization".
Make sure that you have api_key as well as I had that missing.
I have just encounter what way be a similar problem: Swagger UI: HTTP Content-type "application/json" causes "Unable to Load SwaggerUI".
Try changing the HTTP Content-type header of your GET service.json response from "application/json" to "text/html", or even removing it. I don't know why, but it seems it makes a difference for Swagger UI.
For Springdoc OpenAPI the following fix the issue:
#OpenAPIDefinition(servers = {#Server(url = "/", description = "Default Server URL")})

Glassfish JSF 2.0 charset problem

I'm working on a project developed with JSF 2.0 (Mojarra 2.0.3) front end and deployed on Glassfish v.3.0.1 server. Application must accept ISO-8859-2 charset and write data to MySql database.
To problem is that data is not in right charset.
The request Http header has attribute value:
content-type: application/x-www-form-urlencoded; charset=UTF-8
The problem is not with response, since data can be displayed correctly when read from database. Also, MySql connection URL should be correct because it is set for latin2 collaction. I even tried with creating custom filter, but without any result.
Any ideas how can I accomplish to accept correct charset?
Thanks in advance.
You can always force ISO-8859-2 encoding by creating a Filter and defining it in your web.xml. At a bare minimum, the Filter should have:
public class CustomFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
if ((request instanceof HttpServletRequest)
&& (response instanceof HttpServletResponse)) {
request.setCharacterEncoding("ISO-8859-2");
response.setContentType("text/html; charset=ISO-8859-2")
}
chain.doFilter(request, response);
} catch (Exception e) {
// Do your logging here
}
}
}

Resources