CORS Preflight request - Custom auth token in header - Spring and Angular 4 - spring

I'm having trouble requesting a simple GET on my local server with a token as a header.
My browser keep sending preflight request on simple GET request.
I tried with postman / curl and i don't have any issue.
Here's the actual code :
Server (Spring) :
#Override
public void doFilter(final ServletRequest req, final ServletResponse res,
final FilterChain chain) throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods",
"POST, GET, PUT, OPTIONS, DELETE, PATCH");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, x-auth-token, Content-Type");
response.setHeader("Access-Control-Expose-Headers", "x-auth-token");
response.setHeader("Access-Control-Allow-Credentials", "true");
final HttpServletRequest request = (HttpServletRequest) req;
if (request.getMethod().equals("OPTIONS")) {
try {
response.getWriter().print("OK");
response.getWriter().flush();
} catch (IOException e) {
e.printStackTrace();
}
} else {
chain.doFilter(request, response);
}
}
So my x-auth-token here is a jwt-like token.
In my Angular code, I simply add my token as a x-auth-token like this :
getAll() {
return this.http.get('http://127.0.0.1:8080/module', this.jwt()).map((response: Response) => response.json());
}
private jwt() {
// create authorization header with jwt token
let token = localStorage.getItem('user');
let obj = JSON.parse(token)
console.log(obj.token.token)
if (obj.token) {
let headers = new Headers({
'x-auth-token': obj.token.token,
'Content-Type': "application/json"
});
return new RequestOptions({ headers: headers });
}
}
Note that my authentication / signup routes work fine, i'm only having trouble communicating my x-auth-token header to my Spring server.
Both my webserver are running locally : Spring on 8080 and Angular on 8000.
Any help would be greatly appreciated,
Thank you,

First, postman and curl are not good ways to test for CORS, since they don't enforce the same origin policies as standard browsers do. They will work even in cases that will fail in the browser.
Second, the reason your request is pre-flighted by the browser is that it is not a simple request, because of the x-auth-token header and the application/json Content-Type. Only application/x-www-form-urlencoded
multipart/form-data and text/plain content-types are allowed in simple requests.
I would assume that the problem is in the OPTION response. Make sure that it includes all the relevant headers. It might be possible it is failing or that you are loosing the added headers because you are handling it differently than other responses.

Related

How to call Rest API Post method in Angular 8. Getting CORS error

I am trying to call Rest API post method in Angular 8.
'Access-Control-Allow-Origin' :'*','Access-Control-Allow-Methods' :'POST, GET, PUT, OPTIONS, DELETE, PATCH','Access-Control-Max-Age' :'3600','Access-Control-Allow-Headers' :'Origin, X-Requested-With, Content-Type, Accept',
'Content-Type': 'application/json',
'username':'dexadmin','password':'dexcenter'}
this.http.post('http://localhost:8080/DEXRESTAPI/queues/getMaxJob',{},{​​'headers':headers}).
subscribe((data:any) => {​​
console.log('Data', +data);
console.log('Was my name');
}​​);
}
Rest API is as below:
#RequestMapping(value = "/getMaxJob", method = RequestMethod.POST)
public Integer getMaxJob() throws JmsException {
try {
return queueDelegate.getMaxJob();
}
catch (Exception cause) {
logger.error("Error getting MaxJob" + cause.getMessage(), cause);
throw new JmsException(cause.getLocalizedMessage());
}
}
I am getting 401 unauthorized error. I am getting on consol below error -
Access to XMLHttpRequest at 'http://localhost:8080/DEXRESTAPI/queues/getMaxJob/' 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.
I have set in SecurityConfig.java file
http
.httpBasic().disable()
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers(HttpMethod.GET).permitAll()
.antMatchers(HttpMethod.POST).permitAll()
.anyRequest().fullyAuthenticated();
}

NullPointerException in filter

I'm running a filter with every request where I check for "X-Company-ID" header. The header is only sent on login reqest and afterwards the CompanyContext currentCompany is set from the current user data.
Here is my filter, I added some print lines to understand what's going on:
#Component
public class CORSFilter extends GenericFilterBean {
private CompanyService companyService;
#Autowired
public CORSFilter(CompanyService companyService) {
this.companyService = companyService;
}
public CORSFilter() {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, PATCH, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "origin, authorization, accept, content-type, content-disposition," +
"x-requested-with, remember-me, Pragma, Cache-Control, If-Modified-Since, X-Auth-Token, X-Company-ID, X-Client-Certificate");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Expose-Headers", "X-Auth-Token, X-Company-ID, Content-Disposition");
String companyHeader = request.getHeader("X-Company-ID");
System.out.println("Company header: " + companyHeader);
if (companyHeader != null && !ObjectUtils.isEmpty(companyHeader)) {
System.out.println("Company header 2: " + companyHeader);
Company company = companyService.getCompanyByDomainIdentifier(companyHeader);
if (!ObjectUtils.isEmpty(company) || company != null) {
System.out.println(company.getCompanyName());
CompanyContext.setCurrentCompany(company.getId());
}
}
if (!request.getMethod().equalsIgnoreCase("OPTIONS")) {
chain.doFilter(req, res);
}
}
}
The output goes as follows:
Company header: 2
Company header 2: 2
Avantirent OÜ
Company header: 2
Company header 2: 2
2020-02-05 16:17:29.930 ERROR 6124 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exception
java.lang.NullPointerException: null
at com.bitweb.syda.config.filter.CORSFilter.doFilter(CORSFilter.java:52)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
If I understand correctly- the first time the filter runs it gets the header and the right company. The second time it gets the header but getting the company gives me a NullPointerException.
The exception comes from the line Company company = companyService.getCompanyByDomainIdentifier(companyHeader); which is either a correct Company or null. I don't do anything with it if it's null, but it still gives me a NullPointerException.
I'm really stuck here, any help?

Angular2 JWT Authorization header missing in http response (CORS support added)

I'm trying to do an authorization using JWT (Json Web Token). On front-end I'm using Angular2, on back-end Spring REST api. I've added CORS filter on back-end side.
In http request I'm sending username and password and expecting token in 'Authorization' header of response. Everything works fine when I use Postman, I receive all headers including 'Authorization'.
Also, when I record the traffic in Chrome Console (while doing user login through the form) 'Authorization' header is present in response, so obviously it returns back to the browser. But when I list headers in my angular app, there is just few headers in array:
// auth.service.ts
login(username, password): Observable<boolean> {
// call remote service to authenticate user
return this.http.post(this.authUrl, JSON.stringify({ username: username, password: password }))
.map((response: Response) => {
console.log("Authorization header: " + response.headers.get('Authorization'));
console.log("all headers: " + response.headers.keys());
// TODO - login successful if there's a jwt token in the response
});
}
Result of those 2 console output is:
Authorization header: null
all headers: Pragma,Cache-Control,Expires
Here is the screenshot from Google Chrome console where you can see that all needed headers are present on client side:
Server-side token generation (Spring Boot):
public void addAuthentication(HttpServletResponse response, String username) throws UnsupportedEncodingException {
// Token generation
String JWT = Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, secret.getBytes("UTF-8"))
.compact();
response.addHeader(headerString, tokenPrefix + " " + JWT);
}
Does anyone has some useful advice?
Thanks
SOLUTION:
public class CORSFilter implements Filter{
#Override
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", "x-requested-with");
response.setHeader("Access-Control-Expose-Headers", "Authorization");
chain.doFilter(req, response);
}
#Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
}
Authorization header is present but since it's a custom header the browser will not allow you to access it directly even though chrome will see it.
To solve that you need one more important header in you cors config.
Access-Control-Expose-Headers: Authorization, CustomHeader2, e.tc
Make sure to expose any other custom headers that you want your app to have access to.

ClientAbortException when executing angular 2 post in tomcat

After completing the quickstart of angular 2 app i tried to execute a post to a rest web service deployed in another server, which is tomcat.
I have added an OPTIONS method to allow all origins. After returning from the getOptions() method it enters the getTestResponse method and a
ClientAbortException occurs.
Sample code :
return this._http.post(url, body, options)
.map(res => res.json());
Web service code :
#OPTIONS
#Path("samplePath")
public Response getOptions() {
return Response.ok()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT")
.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding")
.build();
}
#POST
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public TestResponse getTestResponse(TestRequest testRequest) {
//somehow get response from database
//
}
Do you have any idea why this exception could occur?
It's because here you add headers just to your option request.
But actually POST is failing.
To fix issue, best is to add a CORS filter to your application which will be applied to all requests.
CorsFilterApi
import org.jboss.resteasy.plugins.interceptors.CorsFilter;
#ApplicationPath(RestApplication.ROOT_PATH)
public class RestApplication extends Application {
public static final String ROOT_PATH = "/resources";
private Set<Object> singletons = new HashSet<>();
...
#Override
public Set<Object> getSingletons() {
CorsFilter corsFilter = new CorsFilter();
corsFilter.getAllowedOrigins().add("*");
corsFilter.setAllowCredentials(true);
corsFilter.setAllowedHeaders("origin, content-type, accept, authorization");
corsFilter.setAllowedMethods("GET, POST, DELETE, PUT, OPTIONS, HEAD");
singletons.add(corsFilter);
return singletons;
}
}
EDIT:
Solution 2 apply headers just to your post request and
make sure that your #OPTION REQUEST IS NOT CONTAINING #PATH
because then it's not same request in your case:
#OPTIONS
public Response getOptions() {
return Response.ok()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT")
.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding")
.build();
}
#POST
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Response getTestResponse(TestRequest testRequest) {
return Response.ok()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS").build();
}

Spring REST api not working while hosted remotely

I have written a spring rest service. When i was running it on localhost it was running well
$.ajax({
url:"http://localhost:8080/api/v1.0/basicSignup",
type:"POST",
contentType:"application/json",
but when i tried & hosted on some remote server
$.ajax({
url:"http://X.X.X.X/api/v1.0/basicSignup",
type:"POST",
contentType:"application/json",
it throwing error
In chrome
XMLHttpRequest cannot load http://X.X.X.X/api/v1.0/basicSignup.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:8084' is therefore not allowed access.
In console i see that in Method tab it show options
In mozilla also it shows OPTIONS.
Response Headersview source
Allow GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
Content-Length 0
Date Sat, 15 Aug 2015 15:15:07 GMT
Server Apache-Coyote/1.1
Request Headersview source
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding gzip, deflate
Accept-Language en-us,en;q=0.5
Access-Control-Request-He... content-type
Access-Control-Request-Me... POST
Cache-Control no-cache
Connection keep-alive
DNT 1
Host X.X.X.X
Origin null
Pragma no-cache
User-Agent Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:13.0) Gecko/20100101 Firefox/13.0.1
As #JB's comment says, you might need to implement CORS. Basically, the single origin policy wouldn't allow JavaScript from one domain, say http://localhost:8084 to make AJAX calls to another domain, say http://X.X.X.X by default. There are some mechanisms for handling this, but people seem to prefer CORS, because generally it looks most convenient and powerful.
Spring Lemon exhibits how to use CORS in details. Below is an example from another project of mine:
In the client code, I would set up these ajax options initially (or along with each call)
$.ajaxSetup({ // Initialize options for AJAX calls
crossDomain: true,
xhrFields: {
withCredentials: true
}
...
});
At the server side, have a filter which will set the CORS headers. The latest version of Spring (which would come along with Spring Boot 1.3) has an easier way to configure CORS at the server side. But, in one project using Spring Boot 1.2, I would have it like this:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter {
#Value("${applicationUrl:http://localhost:8084}")
String applicationUrl;
public void doFilter(ServletRequest req,
ServletResponse res,
FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin",
applicationUrl); // "*" does not work when withCredentials = true;
response.setHeader("Access-Control-Allow-Methods",
"GET, POST, PUT, PATCH, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age",
"3600");
response.setHeader("Access-Control-Allow-Headers",
"x-requested-with,origin,content-type,accept,X-XSRF-TOKEN");
response.setHeader("Access-Control-Allow-Credentials", "true"); // needed when withCredentials = true;
HttpServletRequest request = (HttpServletRequest) req;
if (!request.getMethod().equals("OPTIONS"))
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
Set applicationUrl in application.properties, like this
applicationUrl: http://X.X.X.X

Resources