How to make POST request to external API using Angular2? - angularjs

I'm trying to leverage CORS to make a post request. I came across several articles/answers related to CORS but somehow just couldn't get it working.
As I understand, the access-control-allow-origin: * is to be set on the server side to get this working but what I have here is a angular-cli project.
My project is purely Angular 2.1 based and no backend server is involved. Any suggestions as to how to set it up properly will be highly appreciated.
The exact error that I'm getting is this:
"NetworkError: 404 Not Found - https://flowxo.com/hooks/a/rbpja7r2/?usertype=User"
and this warning in console:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the
remote resource at https://flowxo.com/hooks/a/rbpja7r2/?usertype=User.
(Reason: CORS header 'Access-Control-Allow-Origin' missing).
Update:
Here's how I'm trying to make a POST request:
let headers = new Headers();
headers.append('Access-Control-Allow-Headers', 'Content-Type');
headers.append('Content-Type', 'application/json');
headers.append('Access-Control-Allow-Methods', 'POST, OPTIONS');
headers.append('Access-Control-Allow-Origin', '*');
return this.http.post(
this.flowxoUrl,
JSON.stringify(formData),
{headers: headers}
)
.map((res:Response) => res.json())
.catch((error:any) => Observable.throw(error.json().error || 'Server error')); //...errors if any

This is server side issue, not Angular.
Your server should respond with Access-Control-Allow-Origin header.
Java (Spring) example:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request = (HttpServletRequest) servletRequest;
response.setHeader("Access-Control-Allow-Origin", "*"); //you can specify domains here * - is a wildcard, it will allow all origins to request
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, content-type");
if("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
#Override
public void destroy() {}
}

Related

Authorization header not visible in react app though its marked allowed in exposed header in spring server

I am using spring-boot-starter-parent with version 2.3.4.RELEASE and spring-security.
I have defined cors configuration as below in my spring boot app. I am able to make calls to REST API from react app running on my local without cors issue.
As part of login am hitting /user API with user credentials. Server sends user details as response and I am generating JWT in a filter only for /user API and sending JWT in Authorization header.
I have tested with postman and curl I can see the Authorization header but my react app though it can authenticate it can't see or access Authorization header or the JWT in the response of /user API.
In cors configuration I have marked Authorization header as exposed config.setExposedHeaders(Arrays.asList("Authorization")); not sure why its not getting reflected.
complete code is as below.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().configurationSource(new CorsConfigurationSource() {
#Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
config.setAllowedMethods(Collections.singletonList("*"));
config.setAllowCredentials(true);
config.setAllowedHeaders(Collections.singletonList("*"));
config.setExposedHeaders(Arrays.asList("Authorization"));
config.setMaxAge(3600L);
return config;
}
}).and()
.csrf().disable()
.addFilterBefore(new JWTTokenValidatorFilter(), BasicAuthenticationFilter.class) //JWT validation before basic authentication
.addFilterAfter(new JWTTokenGeneratorFilter(), BasicAuthenticationFilter.class) //JWT validation after basic authentication
.authorizeRequests()
.anyRequest().authenticated()//authorize all requests
.and()
.httpBasic();
}
I tried explicitly defining a cors filter but that didnt work either
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers", "Authorization, x-requested-with, x-auth-token");
response.addHeader("Access-Control-Expose-Headers","Authorization");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
if (!(request.getMethod().equalsIgnoreCase("OPTIONS"))) {
try {
chain.doFilter(req, res);
} catch (Exception ex) {
ex.printStackTrace();
}
} else {
System.out.println("Pre-flight");
response.setHeader("Access-Control-Allowed-Methods", "POST, GET, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Authorization, content-type,x-auth-token, " +
"access-control-request-headers, access-control-request-method, accept, origin, authorization, x-requested-with");
response.addHeader("Access-Control-Expose-Headers","Authorization");
response.setStatus(HttpServletResponse.SC_OK);
}
}
}

HTTP POST turns into OPTIONS AngularJS

I'm trying to submit a login form to a rest Api, translating my Jquery/Javascript code into AngularJS. I try to use $http service for send the request, but when I submit the form, the POST request turns into OPTIONS and no request params are passed to. This is my code:
controller.formData = {
username : $scope.formData.username,
password : $scope.formData.password,
};
$http({
method : 'POST',
url : 'http://localhost:8080/multe-web/signin',
data : controller.formData,
headers : { 'Content-Type': 'application/json' }
})
.success(function(data) {
console.log(data);
});
This is a screenshoot of browser's console
Why no parameters are passed to the HTTP POST request?
Can someone help me?
If you are trying to do a Cross Origin Request, this could be a 'preflight request':
See this post: How to disable OPTIONS request?
This request is called "preflight request". This appends when you try to access a resource that is from another domain. For more information you can search for Cross-origin resource sharing (CORS). During the preflight request, you should see headers starting with Access-Control-Request-* These request headers are asking the server for permissions to make the actual request. Your preflight response needs to acknowledge these headers in order for the actual request to work.
The OPTIONS that you see is a pre-flighted request that is initiated by the browser. This happens due to CORS that stands for "CROSS ORIGIN RESOURCE SHARING`.
Pre-flighted requests
In your case, your backend server needs to acknowledge the OPTIONS request and send a 200 back. Only then the browser fires the real POST request
Here are some links to get you started
Same Origin policy
AngularJS and CORS
Similar question on SO
You must define a CORS filter on your backend.
I don't know which language you are using but an example in spring framework (java) would be the following.
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, cache-control, content-type, Origin, key");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
Basically you are stating that response.setHeader("Access-Control-Allow-Origin", "*"); can come from any domain. ( in a production environment you would limit this offcourse to your known domains ).
When implementing this filter your options request will pass through without problems.
The options call is something the browser does automatically and you really don't want to disable this as some other answers are suggesting.
Kind regards

Spring-boot-oauth2-angularjs, error with CORS after session expiry and redirect to oauth2 server

I have a front-end application that uses the Spring-boot, Spring security oauth2 on server side and AngularJs on client side. I also use a third-party oauth2 server. My problem is that after the expiry of the application session, sping-security redirects all request to '/login' (and that's exactly how it should be) and I got 302 status code with location to redirect on auth-server page in response header. But the redirect does not happen because I get error:
XMLHttpRequest cannot load ****://bla-bla/oauth/authorize?client_id=...andSomeAuthStuff. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '****://myipadress:8084' is therefore not allowed access.
The question is why the first entry into an application or refresh a page or logout and new login does not involve such an error and all goes well but ONLY when I get a session timeout (For example I make ajax request from dead view), CORS error occurs :-(
I reproduce the steps:
On the "dead" page I make ajax request to my API (Spring-boot 1.3.3 WAR running on provided Tomcat 8)
Spring intercepts the request and responds:
General:
Request URL:***//myipadress:8084/appname/login
Request Method:GET
Status Code:302 Found
Remote Address:myipadress:8084
Response headers:
Access-Control-Allow-Headers:Authorization, Content-Type, Accept, x-requested-with, Cache-Control
Access-Control-Allow-Methods:POST, GET, OPTIONS, DELETE, PUT
Access-Control-Allow-Origin:*
Access-Control-Max-Age:3600
Cache-Control:no-cache, no-store, max-age=0, must-revalidate
Location:*//authserver/oauth/authorize?client_id=******&redirect_uri=*://myipadress:8084/appname/login&response_type=code&state=BIQ68y
Pragma:no-cache
Server:Apache-Coyote/1.1
Set-Cookie:JSESSIONID=05D39263EEF7EC9E24AEE8E1E6693748; Path=/appname/; HttpOnly
X-Content-Type-Options:nosniff
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block
CORS Filter:
public class SimpleCORSFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
res.setHeader("Access-Control-Max-Age", "3600");
res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, x-requested-with, Cache-Control");
chain.doFilter(request, res);
}
#Override
public void destroy() {
}
}
Security configuration:
#EnableOAuth2Sso
#Configuration
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.logout()
.and().antMatcher("/**")
.authorizeRequests()
.anyRequest().authenticated()
.and().csrf().disable()
.addFilterBefore(new SimpleCORSFilter(), ChannelProcessingFilter.class);
}
}
Log after ajax request:
2016-04-04 14:10:42.613 DEBUG 5428 --- [o-8084-exec-144] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login'; against '/login'
2016-04-04 14:10:42.613 DEBUG 5428 --- [o-8084-exec-144] uth2ClientAuthenticationProcessingFilter : Request is to process authentication
2016-04-04 14:10:42.615 DEBUG 5428 --- [o-8084-exec-144] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2016-04-04 14:10:42.657 DEBUG 5428 --- [o-8084-exec-144] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2016-04-04 14:10:42.658 DEBUG 5428 --- [o-8084-exec-144] o.s.s.web.DefaultRedirectStrategy : Redirecting to '****://authserver/oauth/authorize?client_id=*****&redirect_uri=***://myipadress:8084/appname/login&response_type=code&state=iNdnBk'
I believe you need to add the #EnableWebSecurity annotation to your CustomWebSecurityConfigurerAdapter.
Also, I tried to go the other way and put the filter in such a way:
#Override
public void configure(HttpSecurity http) throws Exception {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
source.registerCorsConfiguration("/**", config);
CorsFilter filter = new CorsFilter(source);
http
.logout()
.and().antMatcher("/**")
.authorizeRequests()
.anyRequest().authenticated()
.and().csrf().disable()
.addFilterBefore(filter, ChannelProcessingFilter.class);
}
But it also not helped and CORS headers in response all dissapeared!
I think the reason is not in the headers, but in the status code 302 in response. How do I come to 401 instead of 302?

CSRF & CORS issue with angularjs and spring security

Struggling for quite some time. I am having Angular.js at front with Java/J2EE (RESTFul) at the backend with Spring security (Enabled Cors-filter). Spring security works perfectly fine except when I try to enable csrf protection. Below is my code I tried for csrf:
My CSRF filter
public class CsrfHeaderFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
}
Security configuration has below code:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.formLogin()
.successHandler(authSuccess)
.failureHandler(authFailure)
.and()
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.antMatchers("/admin/**").access("hasRole('ADMIN')")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.and()
.addFilterBefore(corsFilter, HeaderWriterFilter.class)
.addFilterAfter(csrfHeaderFilter, CsrfFilter.class)
.csrf().csrfTokenRepository(csrfTokenRepository());
//.csrf().disable();
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
Had to modify CORS filter to add X-XSRF-TOKEN header name inside Access-Control-Allow-Headers as initially it was giving this error.
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, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "X-XSRF-TOKEN, x-requested-with, Content-Type");
chain.doFilter(req, res);
}
My angular js code:
$scope.login = function () {
$http.post("http://localhost:9090/cynosureserver/login", "username=" + $scope.user.name +
"&password=" + $scope.user.password, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-XSRF-TOKEN': $cookies.get('XSRF-TOKEN')
}
}).then(function (data) {
alert("login successful" + JSON.stringify(data));
}, function (err) {
alert("error logging in" + JSON.stringify(err));
});
};
I have debug points set up. After login form it goes to Csrf filter. But never hit my "CustomUserDetailsService". The problem is I always get 403 response saying:
HTTP Status 403 - Expected CSRF token not found. Has your session expired?
If I make a simple Get request it is successful and also "XSRF-TOKEN" cookie is received inside Response Header.
But for my post method I am unable to proceed.
I am seeing below:
Status Code:403 Forbidden
Remote Address:[::1]:9090
Response Headers
Access-Control-Allow-Headers:X-XSRF-TOKEN, x-requested-with, Content-Type
Access-Control-Allow-Methods:POST, GET, PUT, OPTIONS, DELETE
Access-Control-Allow-Origin:*
....
Set-Cookie:JSESSIONID=5DE6DB8B5E31DC77DC4ED28600445615; Path=/cynosureserver/; HttpOnly
X-Content-Type-Options:nosniff
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block
Request Headers
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate
...
Content-Type:application/x-www-form-urlencoded
Host:localhost:9090
Origin:http://localhost:9000
Referer:http://localhost:9000/
X-XSRF-TOKEN:7c2057b5-0f1a-4dc1-bab4-c8f000a2ae2d
Form Data
username:manisha
password:123456
I tried many different combinations but nothing seems to work, Please help me - what am I missing ? I am totally clueless here. Sorry for lengthy post.

How to debug "No 'Access-Control-Allow-Origin' header is present on the requested resource"

I am getting this error shown on browser console:
XMLHttpRequest cannot load http://localhost:8080/api/login. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9009' is therefore not allowed access.
The environment I am using are:
Backend- Spring Boot
Front End- Angularjs
Web Server- Grunt
On server, I am already defining headers in request and response as:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE,PUT");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
httpResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization, Content-Type");
if (httpRequest.getMethod().equals("OPTIONS")) {
httpResponse.setStatus(HttpServletResponse.SC_ACCEPTED);
return;
}
}
I already found this question on this link No 'Access-Control-Allow-Origin' header is present on the requested resource but couldn't find the appropriate solution.
Here is the browser network image:
Your server should add the proper CORS headers for preflight/actual CORS requests. I wouldn't recommend implementing your own Filter, since this is a rather complex specification.
As of 4.2, Spring Framework supports CORS with a global configuration or with a Filter - see this blog post for more information.
If you're using Spring Boot, version 1.3.0 (to be released soon) will integrate this support.
You need to enable CORS(Cross Origin Resource Sharing) on your web server. Please refer to this resource.
You need to set your response header to:
Access-Control-Allow-Origin: *
This link has all the info needed to set CORS
Enabling CORS for all domain is not a good practice, so you should restrict it when everything is up and running.
Another workaround is to have a separate API for curl request
Call a URL in your own API and access the data using your server side with cURL or something. I have a current working project with same situation (Laravel + AngularJs).
My sample code for the remote auth check with cURL request.
Code is in Laravel-PHP format, I hope you can convert to the language you are working on.
Requester function:
public function curlRequester($url,$fields)
{
// Open connection
$ch = curl_init();
// Set the url, number of POST vars, POST data
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
// Execute post
$result = curl_exec($ch);
// Close connection
curl_close($ch);
$result_json_decoded = json_decode($result, true);
return $result_json_decoded;
}
Controller function
public function login()
{
// set POST params
$fields = array(
'username' => Input::get('username'),
'password' => Input::get('password')
);
$url = 'http://www.this-is-api-url.com/login';
$result = $this->curl->curlRequester($url,$fields);
return response()->json($result);
}
Angular request function
$scope.authCheck = function(){
$http({
url:'http://www.our-project-url/login',
method:"post",
data:{"username": "rameez", "password":"rameezrami"}
})
.success(function(response) {
if(response.status==1){
$location.path("/homepage");
}else if(response.status==0){
$scope.login_error_message_box = true;
$scope.login_error_message_text =response.message;
}
});
}
Your Filter is right, You should include your filter as a component and put it where it can be scanned by spring
#Component
public class CORSFilter implements Filter{
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#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");
chain.doFilter(req, res);
}
#Override
public void destroy() {
}
}
You need to also make an update in your angular code to allow CORS via AJAX.
myApp.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
]);
If the above doesn't work, you may need to also add the following:
myApp.all('/*', function (request, response, next) {
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Headers", "X-Requested-With");
response.header("Access-Control-Allow-Methods", "GET, POST", "PUT", "DELETE");
next();
});
As noted by #Marged you want to be cautious about the "*" and I would highly recommend replacing that with your domain(s).

Resources