1

I am using Angular 4 , JAX-RS for rest services. Angular is deployed in tomcat server and JAX-RS rest services were deployed in Websphere 8.5 App server. I am trying to secure the rest services using Basic Auth. Here is web.xml part used for resolving CORS issue and Basic Auth

<filter>
        <filter-name>CORS</filter-name>
        <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
        <init-param>
            <param-name>cors.allowGenericHttpRequests</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>cors.allowOrigin</param-name>
            <param-value>*</param-value>
        </init-param>
        <init-param>
            <param-name>cors.allowSubdomains</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>cors.supportedMethods</param-name>
            <param-value>GET, POST, PUT, DELETE, OPTIONS, HEAD </param-value>
        </init-param>
        <init-param>
            <param-name>cors.supportedHeaders</param-name>
            <param-value>*</param-value>
        </init-param>
        <init-param>
            <param-name>cors.supportsCredentials</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>cors.maxAge</param-name>
            <param-value>-1</param-value>
        </init-param>
    </filter>
    <filter-mapping>
            <filter-name>CORS</filter-name>
            <url-pattern>/*</url-pattern>
    </filter-mapping> 

<security-role>
        <description>default</description>
        <role-name>default</role-name>
    </security-role>

     <security-constraint>
        <display-name>DefaultConstraints</display-name>
        <web-resource-collection>
            <web-resource-name>all resources</web-resource-name>
            <url-pattern>/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
            <http-method>PUT</http-method>
            <http-method>HEAD</http-method>
            <http-method>TRACE</http-method>
            <http-method>DELETE</http-method>
            <http-method>OPTIONS</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>default</role-name>
        </auth-constraint>
    </security-constraint>  

     <login-config>
        <auth-method>BASIC</auth-method>
    </login-config>

When Angular app invokes Rest services from chrome browser with the above configuration in Java web it is throwing the error and below is seen in the browser console.

Failed to load resource: the server responded with a status of 401 (Unauthorized)

Failed to load http://localhost:9091/xxxxxxx/rest/v1/technologies: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 401.

If the same angular app invokes the rest services from IE , the browser is throwing a pop-up for credentials and after succesful login, angular is able to fetch and display the records.

It is working for IE, not working for chrome.

If I remove the security constraint, basic auth, roles and only place CORS filter, Angular is able to fetch records from rest services in both chrome and IE. If fails only in chrome when security constraint.

Please help me in resolving. Let me know if there are any mistakes.

Thanks, Hari.

hari
  • 21
  • 5

4 Answers4

2

This is because of your server (API) is deployed on localhost:9091, and your frontend app (Angular APP) is live on localhost:8080.

To make an api call, or better say http call, Angular has Http/HttpClient module in it, which is then implemented on native browser level, the problem here is, browser detects that the request from the app (:8080) is calling another resource (:9091) MIGHT BE A CORS request.

Definition from Wikipedia

Cross-origin resource sharing. Cross-origin resource sharing (CORS) is a mechanism that allows restricted resources (e.g. fonts) on a web page to be requested from another domain outside the domain from which the first resource was served.

In order to make sure the above mechanism work, browser prevents the calls, by making an extra call before each request. These request are of OPTIONS type request, if the server responds "yes.. CORS allowed." then browser will make your call proceed else it will throw an error (Exactly what you are getting).

Solution to this,

Use proxy while deploying your angular app.

ng serve --aot --port 3300 --proxy-config proxy-conf.json

here, I'm deploying my angular app on port:3300, and saying it, to use proxy-configuration stored in proxy-conf.json file.

Where, proxy-conf.json file has..

    [
        {
            "context": [
                "/api",
                "/auth"
            ],
            "target": "http://localhost:8001",
            "secure": false
        }
    ]

In the context array of above config, I said cli, to apply proxy only on requests with url /api,/auth only. This is something you need to customize as per your API endpoints.

The target key is to point out what is the location of your API. (I deployed my API on port :8001)

Hope it helps.!!

miiiii
  • 1,580
  • 1
  • 16
  • 29
  • Hi Manoj, The above solution works for me if I run the angular app using angular cli like the ng serve...... But if I use ng build and deploy the dist folder to the web server, it is not working. please suggest – hari Feb 01 '18 at 10:11
  • What else you can do is, use `ng build --base-href http://localhost:9091/`, and make an `httpInterceptor` in your angular app (I assume you already having it), and there, create a simple variable, `baseUrl = /xxxxxxx/rest/v1/technologies/` and then, to every http calls, like `http.get(urlOfGet)` replace it with `http.get(baseUrl+urlOfGet)`, This should definitely work, as all calls will be listened to localhost:9091 as the hosting url, so no cros issue and the baseUrl in interceptor will differentiate path for api calls only. Do let me know if it works or not, it's interesting issue. :P – miiiii Feb 01 '18 at 11:10
  • Hi Manoj, I have doubt in the solution, since I will be deploying the angular app in webserver like tomcat and it runs on localhost:8080, I have to access the application initially like localhost:8080/dist. When I hit this it turn request the localhost:9091 and causing the same issue – hari Feb 02 '18 at 05:45
  • http://localhost:9091/xxxxxxx/rest/v1/technologies 401 (Unauthorized) :8080/dist/login:1 Failed to load http://localhost:9091/xxxxxxxx/rest/v1/technologies: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 401. – hari Feb 02 '18 at 05:46
  • Is ur rest API running on 9091? If yes, then deploy app to same port. – miiiii Feb 02 '18 at 07:57
  • Manoj, rest app is running on Websphere App server on port 9091, and we want Angular to be deployed only in web server like tomcat and it is running on 8080. We dont want to angular to run on App server. – hari Feb 02 '18 at 11:13
  • Then it might need to have some server side proxy configs, or other simple solution could be, allow 'Access-Control-Allow-Origin' from your server, so the browser will always make one extra call (with http method OPTIONS) before actual api call. Then you can have two different domain/ports for app and api. – miiiii Feb 02 '18 at 11:17
  • If you see in my post, I have used CORS filter and allowed origins. The problem arises when accessed in chrome browser only when security constraint is placed in web.xml. If I remove security constraint, even in chrome it works fine. No CORS issue since I am using CORS filter as posted. – hari Feb 02 '18 at 12:07
  • I've never done this practically (sply with xml, I've used annotations based config in java), so asking you like a naive, u said issue is in chrome only, so in other browsers you must be getting response with `Access-Control-Allow-Origin:` header?? If not, then that means your filters are not working as expected. – miiiii Feb 02 '18 at 12:27
1

Try installing CORS extension:

https://chrome.google.com/webstore/detail/cors-toggle/jioikioepegflmdnbocfhgmpmopmjkim?hl=en

Works fine in my case.

Sikandar Sahab
  • 638
  • 3
  • 10
  • 27
  • after installing the plugin, I see now only GET http://localhost:9091/xxxxxxxxx/rest/v1/technologies 401 (Unauthorized), now no CORS. but still browser popup is not shown for asking credntials. please suggest. – hari Feb 01 '18 at 09:21
0

You are getting this error from your browser. You are getting the error because your applications are not on the same domain. I suggest using a proxy. There is an option for that in the CLI. This way it will be a one time configuration.

An other option would be to install a CORS plugin in your browser. This however would require every developer to install it on every browser they use.

Robin Dijkhof
  • 18,665
  • 11
  • 65
  • 116
  • yes. Angular proxy it works. But this can help only during development and when angular running Angular CLI/server. But when it is deployed in webserver like tomcat I think It is not working. Please suggest . – hari Feb 01 '18 at 09:08
  • before deploying your app to tomcat container, your app must be build by applying proxy configs, once compiled/built using this, you can deploy to any container,it wud work. – miiiii Feb 01 '18 at 09:30
0

In Chrome/Firefox, the WAS LTPA2 token cookie is not set for XMLHttprequest. After adding withCredentials:true, the http get requests were working in chrome/firefox also. Chrome is now adding the cookie after adding this in options.

return this.http.post(environment.frameworkUrl,framework,
  {withCredentials: true} );
}

But the same is not working for post requests. Chrome is not adding the cookie for post requests.

hari
  • 21
  • 5