[Fixed] Set HttpOnly Cookies while developing Web App locally

Issue

I am developing a web app with angular + nebular auth. Nebular auth is working and I get a JWT token from our auth server. The auth server is made with Node and sets also an HTTPOnly cookie for the refresh token. I want this token to be send along every request.
The login response has indeed the Set-Cookie header, but the cookie is never set. I have read a lot of answers in Stack Overflow but everything I tried did not work.

The auth server is in a Cloud server, while I am developing the app locally. This maybe can be a problem already.

Anyway, here’s what I have done till now:

Node.js

I am using an HTTP server, and setting the cookie with cookie-parser with:

res.cookie("refresh_token", token, {httpOnly: true, maxAge: ....});

I set the core options in app.js like this:

app.use(cors({
 credentials: true,
 origin: ["http://localhost:4200", "http://127.0.0.1:4200"]
 exposedHeaders = ["Content-Length", .....],
 allowedHeaders = ["Content-Type", "Authorization", "Set-Cookie", ....],
}));

When I get the response of the Login, I do get the Set-Cookie header but I cannot see the cookie in the Cookies tab of my browser console.

I tried to send a request from Angular anyway, with { headers: headers, withCredentials: true } but obviously when I check the cookie in Node there’s nothing.

So I am going crazy… it’s probably a problem with CORS, because I am developing from localhost and the server is up on the cloud?

How can I make this work?

Solution

So the initial solution I have found was just simply running the Node server on localhost. The server was pretty light so no problem but it wasn’t the best solution.

I solved the problem with a simple local proxy in Angular:

I created proxy.json on the root of the workdir

{
  "/api": {
    "target": "http://<server_ip>:3000",
    "secure": false
  }
}

Then added this line on angular.json

"serve": {
  "builder": "@angular-devkit/build-angular:dev-server",
  "options": {
    "proxyConfig": "proxy.json", // Added this
    "ssl": false
  },
  ...

I also needed to create an HttpInterceptor to intercept http requests and add the Access-Control-Allow-Credentials header to every one of them. Usually I use an Interceptor also to add custom headers I need for my project, handle authorization headers and also call the refresh token API when auth tokens are expired. Anyway the simplest Interceptor is this:

import { Injectable } from '@angular/core';
import {
  HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http';

import { Observable } from 'rxjs';

/** Inject With Credentials into the request */
@Injectable()
export class HttpRequestInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler):
    Observable<HttpEvent<any>> {
    
      req = req.clone({
        withCredentials: true
      });
      
      return next.handle(req);
  }
}

Finally on my environment.ts

export const environment = {
  app_name: "<app_name_dev>",
  production: false,
  api_path: 'http://localhost:4200/api',
};

So now using environment.api_path as base path to call my authentication APIs works correctly with CORS and I get my HTTP Only Cookie.

Leave a Reply

(*) Required, Your email will not be published