Angular: Sending/Receiving cookies with a cross-site application server

AngularEnthusiast
6 min readMay 29, 2024

If you are interested to know how cookies can be sent/received between cross-origin but same-site resources, you can check the below story, which explores a similar concept.

In order to create a scenario where cross-site resources are interacting with one another locally, I had to modify a section of the C:\Windows\System32\Drivers\etc\hosts file.

# localhost name resolution is handled within DNS itself.
127.0.0.1 localhost
127.0.0.1 csrfexample
127.0.0.1 nodecsrfexample
# ::1 localhost

I have added the below 2 additional lines in the file. The angular app will run on csrfexample:4200 and the node application server will run on nodecsrfexample:3000.

127.0.0.1 csrfexample
127.0.0.1 nodecsrfexample

Since the 2 resources have different domains, they are cross-site resources.

Angular App Configuration

I have updated the “start” script of the package.json as below:

“start”: “ng serve --host csrfexample --port 4200”,

The “ng serve” command by default uses development configuration.

The environment.development.ts looks like below. Since CORS errors will be handled at the Node server and we are not using proxy in the angular app, we have specified the URL, where we can reach the Node server in the environment.development.ts.

export const environment = {
baseUrl:”http://nodecsrfexample:3000/",
production:false
};

We have created a SecurityService, where 2 http requests to the Node server are triggered.

login() will enable the user to authenticate with the Node server. The Node server will set a cookie by name “session-token”, once the user is successfully authenticated.

Once the user is authenticated, he can update his own password. The updatePassword() handles this request.

updateAuthenticationStatus() and getAuthenticationStatus() simply sets and gets the authenticated state of the user.

AppComponent Template

Clicking on the “Login” button calls the login() in class, which in turn calls the login() in the SecurityService. Once the user is successfully authenticated, we are showing a textbox and a “Update” button to update the password.

Clicking on the “Update” button, calls the updatePassword() in the class, which in turn calls the updatePassword() in the SecurityService.

<button (click)=”login()”>Login</button>
<ng-container *ngIf=”isAuthenticated”>
<textarea [(ngModel)]=”password”></textarea>
<button (click)=”updatePassword()”>Update</button>
</ng-container>

AppComponent Class : Nothing much in this class. I have shown it so that you can see the continuity between the template and the SecurityService.

Finally, below is the TestInterceptorService, where we are cloning the http request to include an additional property: withCredentials and setting it to true. This property ensures that cookies are sent along with the request and server sent cookies are received and set in the browser.

Node Server

This is the structure of the Node project.

Below is the index.js to handle the requests from the angular app.

I have not done any validation of username/password for “/login” route because its not within the scope of this story. The purpose of the “/login” route is simply to set a cookie named “session-token” and send back a status code 200 response.

In the “/update/password” route, we have checked for the presence of the “session-token” cookie in the request to verify whether the angular app has/hasn’t successfully sent the cookie along with the request to the cross-origin server. It also confirms that the user is authenticated.

Below is the app.js, where we are using the cors npm package to handle CORS errors.

app.use(cors( {
credentials:true,
origin:[‘http://csrfexample:4200']
}
))

Lets now start the angular app using “npm run start”.

Hitting http://csrfexample:4200 in the browser.

Clicking on the “Login” button, the http request has completed successfully. The node server has sent the “session-token” cookie, as evident from the Set-Cookie response header below.

Please observe the Authorization Request header where we are sending the username and password in base-64 encoded form.

Checking the Application tab, to verify if the “session-token” cookie has actually been set. Observe the warning message in the screenshot below. Since we had set the sameSite attribute of the “session-token” cookie to “lax”, its blocked by the browser, when a cross-site Node server sends it.

Another way to verify this, would be try the update the password. If the cookie has actually been set, it should be sent along with the request. But as you can see below, the request has failed.

There is no Cookie request header. This implies no cookies have been sent along with this request. No cookies have been sent, because no cookie is available to be sent.

Solution

For receiving and sending cookies in cross-site resources, we need to set sameSite attribute to “none” but establish a secure connection for the requests.

The process behind setting up a https connection, is itself a separate story. You can check the below links to set up a https connection for angular app and node express server.

I assume you have set up a secure https connection for both the angular application and Node server.

Instead of running the angular app using web dev server, I will containerize and deploy the angular app to nginx web server. You can also run the angular app using web dev server.

The secure Node server listens to requests on https://nodecsrfexample:443 and the angular application runs on https://csrfexample:8081. The nginx webserver listens on port 443.

Since we are building the angular project using production configuration by default, the environment.ts file will be used. The environment.ts file has been updated as below, to point to the secure Node server.

export const environment = {
baseUrl:”https://nodecsrfexample:443/",
production:true
};

In the app.js in the node project, the npm cors configuration needs to be updated

FROM

app.use(cors( {
credentials:true,
origin:[‘http://csrfexample:4200']
}
))

TO

app.use(cors( {
credentials:true,
origin:[‘https://csrfexample:8081']
}
))

In the index.js, the attributes of the “session-token” cookie needs to be modified.

FROM

res.cookie(“session-token”,”mysession”,{httpOnly:true,secure:false,sameSite:’lax’});

TO

res.cookie(“session-token”,”mysession”,{httpOnly:true,secure:true,sameSite:’none’});

Hitting https://csrfexample:8081 in the browser,

Clicking on the login button, the request has completed successfully. The node server has sent the secure httpOnly “session-token” cookie with the sameSite attribute set to “none”.

Below are the request headers.

Finally, lets check if the “session-token” cookie has been actually set or not. Observe in the Application tab, that the cookie has been set without no warning messages.

Lets now try to update the password of the user. The request has successfully completed now.

Observe the Cookie header in the list of request headers. This implies that the “session-token” has been sent along with the request, validated at the Node server, which in turn has sent back a status code 200 response.

--

--

AngularEnthusiast

Loves Angular and Node. I wish to target issues that front end developers struggle with the most.