Why cookie is not set? - javascript

Do you have any idea why cookie is not set in client? It is sent from backend:
func loginEmail(_ req: Request) throws -> Response
{
let response = Response(status: .ok)
let cookie = HTTPCookies.Value(string: "abcdef")
response.cookies["userId2"] = cookie
return response
}
it is visible in browser in Network tab
set-cookie: userId2=abcdef; Path=/; SameSite=Lax
but not on Application
GET is sent to backend. Backend runs on 8080 port, frontend on 3000.
I use axios in a React / Next.js app for calling endpoint:
const login = () => {
axios
.get(`http://localhost:8080/loginEmail`)
.then((res) => {})
.catch((err) => console.error(err));
};
I am using Vapor as backend, and has the following configurations, maybe they matter:
app.middleware.use(CORSMiddleware(configuration: .init(
allowedOrigin: .originBased,
allowedMethods: [.GET, .POST, .PUT, .OPTIONS, .DELETE, .PATCH],
allowedHeaders: [.accept, .authorization, .contentType, .origin, .xRequestedWith, .userAgent, .accessControlAllowOrigin, .init("crossDomain"), .accessControlAllowCredentials, .xRequestedWith]
)))
app.sessions.configuration.cookieName = "userId2"
// Configures cookie value creation.
app.sessions.configuration.cookieFactory = { sessionID in
print("sessionID.string: \(sessionID.string)")
return .init(string: sessionID.string, isSecure: false)
}
app.middleware.use(app.sessions.middleware)

Related

socketio cookies are always empty; header "Set-cookie" is not being set

Im building a webserver with express, socketio and react. I have barely any experience when it comes to server and client communication, so Im trying to figure everything out.
Goal
Everytime a client connects to the webserver, it should be tested wether a cookie with the name "uid" exists. If it does, I want to print the value to the console, if there is no cookie with the name "uid" I want to create one.
Problem
Whenever I access http://localhost:3000 (on which adress the react client is hosted) and inspect the localhost request, there is no header with the name Set-header, same happens when I try to curl it. Also, in the Application tab the Cookies for http://localhost:3000 are completely empty. Accessing the cookie that should be set when a socket connects results in undefined or an empty object.
What I tried
The docs suggest setting cookie: true inside the options when creating the server, and prove that the cookie is set with a curl. I also tried setting the options to true on server creation (see server source code below), however when I try to curl the line Set-Cookie: io=G4J3Ci0cNDWd_Fz-AAAC; Path=/; HttpOnly; SameSite=Lax doesnt show up (see curl output below).
Following the docs again, I tried setting a cookie via headers["set-cookie"] = cookie.serialize("uid", "1234", { sameSite: "strict" }); inside io.engine.on("initial_headers"), however the cookie property on socket.request.headers.cookie remains undefined when trying to parse the cookie via let cookies = socket.request.headers.cookie; let parsed = cookie.parse(cookies ?? ""); //does not translate console.log(parsed); and thus the console output is only an empty {}.
I dont know what the difference between cookie based sticky session and application-cookies is, and I also dont know much about settings cookies and headers and all that stuff, maybe I am missing something obvious about the server/client side. Thanks in advance
Curl output:
C:\Users\stadl>curl "http://localhost:3000" -v
* Trying 127.0.0.1:3000...
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.83.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: *
< Access-Control-Allow-Headers: *
< Content-Type: text/html; charset=utf-8
< Accept-Ranges: bytes
< Content-Length: 1711
< ETag: W/"6af-+M4OSPFNZpwKBdFEydrj+1+V5xo"
< Vary: Accept-Encoding
< Date: Sun, 25 Sep 2022 13:29:44 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
Server code:
const express = require("express");
const app = express();
const http = require("http");
const server = http.createServer(app);
const { Server } = require("socket.io");
const ports = require("../ports");
const cookie = require("cookie");
const SERVER_PORT = ports.get("server");
const CLIENT_PORT = ports.get("client");
const io = new Server(server, {
cors: {
origin: "http://localhost:" + CLIENT_PORT,
},
cookie: true,
});
io.engine.on("initial_headers", (headers, request) => {
headers["set-cookie"] = cookie.serialize("uid", "1234", { sameSite: "strict" });
});
io.engine.on("headers", (headers, request) => {
if (!request.headers.cookie) {
return;
}
const cookies = cookie.parse(request.headers.cookies);
if (!cookies.uid) {
headers["set-cookie"] = cookie.serialize("uid", "abc", { maxAge: 86400 });
}
});
io.on("connection", socket => {
console.log(`socket ${socket.id} connected`);
let cookies = socket.request.headers.cookie;
let parsed = cookie.parse(cookies ?? "");
console.log(parsed);
socket.on("disconnect", () => {
console.log(`socket ${socket.id} disconnected`);
});
});
server.listen(SERVER_PORT, () => {
console.log("Server is listening on http://localhost:" + SERVER_PORT);
});
Client code:
import React from "react";
import io from "socket.io-client";
import Navigation from "./components";
import { reload } from "./reload";
const ports = require("ports");
const SERVER_PORT = ports.get("server");
const socket = io.connect("http://localhost:4000");
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
DARKMODE: false,
};
}
componentDidMount() {
setInterval(reload, 1000);
}
toggleDarkMode() {
this.setState({
DARKMODE: !this.state.DARKMODE,
});
}
render() {
return (
<div className="app" data-theme={this.state.DARKMODE ? "dark" : "light"}>
<Navigation onDarkModeToggle={this.toggleDarkMode.bind(this)}></Navigation>
</div>
);
}
}
export default App;
----------------------------- EDIT --------------------------------
When activating the `show filtered out request cookies` in chrome on inspecting the localhost request, the cookie "uid" with the value "abc" gets displayed. But why is it getting filtered out?

Express doesn't set cookies with httpOnly flag

I have my back-end Express.js server that has sign in function. After user sign in, he gets 2 tokens - access token and refresh token. What I want to do, is to make return from server refresh token as httpOnly cookie.
Here is a peace of code of this function:
const { refreshToken, accessToken } = await jwtService.updateTokens({
userId: client.id, username: client.username
}, { transaction })
logger.info(`Client ${client.email} has been successfully signed in!`)
await transaction.commit()
return res
.status(200)
.cookie("refreshToken", JSON.stringify(refreshToken), { httpOnly: true, secure: false })
.json({ accessToken, reopening: reopening ? client.username : null })
Basically, browser just doesn't set this cookie as httpOnly and doesn't set it at all, actually. So, I was trying to ping this endpoint with postman, and it works:
In reponse body I have access token and in httpOnly cookie I have this refresh token.
So, the problem is, why browser doesn't save it? I have found a couple of solutions and all of them were about cors and axios credentials. I use 2 express servers - 1 is for normal back-end and 1 is for front-end.
Here is how "path" from front-end to back-end looks like:
Sign in function that send request to front-end express server:
const api = axios.create({
baseURL: apiUrl,
headers: {
'Content-Type': 'application/json'
}
})
export const signIn = async payload => {
try {
const { data } = await api.post('s-i', payload)
return data
} catch (e) {
return e.response.data
}
}
Front-end express server sends request to actual back-end:
const api = axios.create({
baseURL: process.env.NODE_ENV === "development" ? process.env.PNB_API_DEV : process.env.PNB_API_PROD,
})
const router = Router()
router.post('/s-i', async (req, res) => {
try {
const { data } = await api.post('/sign-in', req.body)
res.json(data)
} catch (e) {
return res.status(e.response.status).json(e.response.data)
}
});
And then that function that was at the very begging.
So - the question is - how to make browser save those httpOnly cookies? If it's really about credentials or cors where should I put those settings?
PS
Back-end port - 3001 and front-end port - 8010.

POST request not uploading file to the server

I've been stuck with this problem for 3 days. I'm using React.js on the frontend with axios and want to upload the file to the server. There is an API endpoint which is a post endpoint something like this.
POST- https://88.66.123.122:20000/b1s/v1/Attachments2
This endpoint basically uploads the files to the server file system with the successful response that has 201 status. The response is successfully fine when I test the endpoint with the Desktop client Postman and the code snippet generated by this tool is this.
But I want to achieve this thing in browser UI. So I'm using React.js for this.
This endpoint also needs an authorization cookie in the request header to make sure the user is authenticated. So In the UI, I created a login button that basically sends a post request with a hardcoded credentials to the login endpoint and giving me a successful response with a session id.
I'm saving the session id in the browser as a cookie for the upload file but when I'm sending the cookie with the request, I'm getting the following response in the browser
Refused to set unsafe header "Cookie"
and the response I'm getting back with the following JSON.
POST https://88.66.123.122:20000/b1s/v1/Attachments2 [401 (Unauthorized)]
{
"error" : {
"code" : 301,
"message" : {
"lang" : "en-us",
"value" : "Invalid session."
}
}
}
I don't know How I can solve this problem? You can see the GIF.
Code:
import React from 'react';
import axios from 'axios';
const URL_LOGIN = `${process.env.REACT_APP_SERVER}Login`;
const COMPANY_DB = process.env.REACT_APP_DB;
const URL_ATTACHMENT = `${process.env.REACT_APP_SERVER}Attachments2`;
const CREDENTIALS = {
UserName: process.env.REACT_APP_USERNAME,
Password: process.env.REACT_APP_PASSWORD,
CompanyDB: COMPANY_DB
};
function App() {
const [isLogin, setIsLogin] = React.useState(false);
const [selected, setSelected] = React.useState(null);
function onClick() {
setIsLogin(true);
axios
.post(URL_LOGIN, CREDENTIALS)
.then(function (response) {
setIsLogin(false);
console.log(response.data);
})
.catch(function (err) {
setIsLogin(false);
console.log(err);
});
}
// onUpload
function handleUpload(event) {
console.log('File set', event.target.files[0]);
setSelected(event.target.files[0]);
}
function uploadSubmit() {
const formData = new FormData();
formData.append('files', selected, selected?.name);
axios
.post(URL_ATTACHMENT, formData)
.then(function (response) {
console.log('response', response);
})
.catch(function (err) {
console.log('err', err);
});
}
return (
<div>
<div>
<button type="button" onClick={onClick} disabled={isLogin}>
Login Create Cookie
</button>
</div>
<hr />
<div>
<div>
<input type="file" onChange={handleUpload} />
</div>
<button type="button" onClick={uploadSubmit}>
Upload File
</button>
</div>
</div>
);
}
export default App;
The cookies are managed by the server, not the client. In your case, you are using a cookie with HttpOnly flag. The client side scripting will not allow you to read the value of Cookies as it is a security threat.
In your nodejs application, the server must be sending a Cookie in response. The server must be doing something like this:
// nodejs (express)
res.cookie('cookieKey', "value", { maxAge: 900000, httpOnly: true });
notice the httpOnly flag, this flag prevents the cookie to be used by the client-side scripting.
Once you set the cookie in response to your NodeJs (Express) request, your browser should automatically start sending the Cookie with each of your requests.
If the request is cross-origin be sure to add withCredentials: true as a header in the axios request
After successfully getting the cookie/token from the server, pass the token in the header. (depends on how you are securing your API endpoint.)
axios
.post(URL_ATTACHMENT, formData, { headers : { header : token }})
.then(function (response) {
console.log('response', response);
})
.catch(function (err) {
console.log('err', err);
});

WebSockets via browser on React + NodeJS

I'm building a websocket chat service, running on NodeJS. It's already working on terminal, if you want to try, check the terminal client package. https://www.npmjs.com/package/#redstone-solutions/hackerchat-client
It works fine on the terminal, now i'm developing a package to integrate web apps (javascript, react, etc), but i can't connect to the websocket via browser.
Basically, that's the backend on the websocket creation:
(I'm not using socket.io, the only dependency is uuid)
async initialize(eventEmitter: NodeJS.EventEmitter): Promise<http.Server> {
const server = http.createServer((request: http.IncomingMessage, response: http.ServerResponse) => {
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-Request-Method', '*');
response.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
response.setHeader('Access-Control-Allow-Headers', '*');
response.writeHead(200, { 'Content-Type': 'text/plain' })
response.end('Hacker chat server is running!\n\nPlease connect with websocket protocol.')
})
server.on('upgrade', (request, socket) => {
socket.id = uuid()
const headers = [
'HTTP/1.1 101 Web Socket Protocol Handshake',
'Upgrade: WebSocket',
'Connection: Upgrade',
''
]
.map(line => line.concat('\r\n'))
.join('')
socket.write(headers)
eventEmitter.emit(EventTypes.event.NEW_USER_CONNECTED, socket)
})
return new Promise((resolve, reject) => {
server.on('error', reject)
server.listen(this.port, () => resolve(server))
})
}
Here's my try on a new react app, i'm using the same ways that i use on the terminal client.
function App() {
useEffect(() => {
async function run() {
const componentEmitter = new Events()
const connection = await createConnection()
console.log(connection)
}
run()
}, [])
async function createConnection() {
const options = {
port: '9898',
host: 'localhost',
headers: {
Connection: 'Upgrade',
Upgrade: 'websocket'
}
}
const request = http.request(options)
request.end()
return new Promise(resolve => {
request.once('upgrade', (response, socket) => resolve(socket))
})
}
return <div />
}
The request never upgrades on the react app! The console.log(connection) is never called.
I'm not sure if i'm using a resource that's not compatible with browsers (http package, for example)...
The applications are very simple/small, if you need the full code:
https://github.com/redstone-solutions/hackerchat-server
https://github.com/redstone-solutions/hackerchat-terminal-client
Any idea?
I found a way to connect with the socket, but I will need to restructure my application.
const server = new WebSocket('ws://localhost:9898')
Using the native WebSocket API from JavaScript, i can stablish a connection.
With the http library, the request never upgrades, i believe that this lib doesn't fully work on the browser.

How to retrieve web-token from cookie using express, passport

Can't access token saved to cookies using passport, express and jsonwebtokens.
I'm using passport for authorization and passport-jwt to authentication of web-tokens. I have verified that my server is issuing web-tokens and setting cookies on the browser, but when I attempt to use secure routes it gives me an unauthorized message.
...
// fetching from server
const response = fetch("http://localhost:5000/user/profile");
...
...
app.use(cors({ credentials: true, origin: "http://localhost:3000" }));
app.use(cookieParser("password"));
app.use("/",require("./routes/routes"));
app.use("/user",passport.authenticate("jwt",
{session:false},require("./routes/secure-routes"));
...
...
router.post("/login",async(req,res)=>{
passport.authenticate("login",{session:false},async (err,user)=>{
...
req.login(payload,{session:false},async error=>{
...
const token = jwt.sign(JSON.stringify(payload),"password");
res.cookie("jwt",token,{httpOnly:true});
res.status(200).send({msg:"cookie set!});
}}
})
...
...
const JWTstrategy = require("passport-jwt").Strategy;
passport.use(
new JWTstrategy(
{
jwtFromeRequest: req=>req.cookies.jwt,
secretOrKey: "password"
},
(jwtPayload, done) => {
return done(null,jwtPayload);
}
)
...
The server is definitely setting the cookies on the browser to the webtoken, but for some reason I can't retrieve the token from the GET route. Any help would be greatly appreciated.
You need to include the cookies.
const response = fetch("http://localhost:5000/user/profile", {
credentials: "include"
});

Categories

Resources