Angular dev-server with Rails on different ports and CORS policy with passing credentials

Eugene Nikolaev
3 min readFeb 8, 2021

--

Frontend development with modern JS framework is usually done using dev-server. With it, we don’t have to compile JS each time and after saving code changes, it happens automatically and page is refreshed with changes reflected.

But to be able to use it, we have to run a dev-server on some port which might be different from the backend app’s port. But when request, done by page’s code is trying to access different origin (domain, scheme, or port), browser will block the request, because of CORS policy. To fix this issue we will need to add some headers on backend app.

First, lets start Angular dev-server with:

ng serve** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **

it will run on port localhost:4200 by default.

Let’s also run Rails app:

rails server
Puma starting in single mode...
* Version 3.12.0 (ruby 2.5.3-p105), codename: Llamas in Pajamas
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop

which will start Puma on localhost:3000.

Now the problem: if we try to make request with JS to backend API, it will fail with error in browser console:

this.http
.get('http://localhost:3000/my_api/')
.subscribe((response: any) => {
console.log(response);
resolve(true);
}, () => {})
Access to XMLHttpRequest at ‘http://localhost:3000/my_api/' from origin ‘http://localhost:4200' has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

It happens, because of the browser’s CORS (Cross-Origin Resource Sharing) policy. For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts and app can only request resources from the same origin the application was loaded from unless the response from other origins includes the right CORS headers.

To fix that, we need our server to add some special response headers. Before the actual request to the server, the browser is doing a ‘preflight’ request to check those headers and continues to actual request if correct headers are set.

In Rails, usually ‘rack-cors’ gem is used for that. Basically it’s just adding headers like:

Access-Control-Allow-Methods: *
Access-Control-Allow-Origin: *

to server response. Adding gem:

gem ‘rack-cors’

Also, we need to place this config to config/enviroments/development.rb, because we will probably need this only for development environment:

config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '*', headers: :any, methods: [:get, :post, :patch, :put]
end
end

After restarting Rails server, requests will start working.

But the next problem starts when we need to pass credentials data within request if the backend requires some authentication stored in page’s cookies.

Adding credentials to a query can be done with ‘credentials: ‘include’ if native JS fetch is used, or by using ‘withCredentials: true’ in Angular.

After we change JS to:

this.http.get('http://localhost:3000/my_api/',
{
withCredentials: true })
.subscribe((response: any) => {
console.log(response);
resolve(true);
}, () => {})

following error will occur:

Access to XMLHttpRequest at ‘http://localhost:3000/my_api/' from origin ‘http://localhost:4200' has been blocked by CORS policy: The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’ when the request’s credentials mode is ‘include’. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

That happens because browser requires to set the list of allowed origins and refuses to send credentials to just ‘any (*)’ origin.

To fix it we need to change Rails side as follows:

config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'http://localhost:4200'
resource '*', headers: :any, methods: [:get, :post, :patch, :put], credentials: true
end
end

Adding ‘credentials: true’ will make Rails set ‘Access-Control-Allow-Credentials’ header in response to true and with allowing sending credentials from specific origin (localhost:4200) request will start working again.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Eugene Nikolaev
Eugene Nikolaev

Written by Eugene Nikolaev

Senior Software Engineer at GMO GlobalSign

No responses yet

Write a response