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.