I am currently trying to set up my react application and connect it to my backend. I am using JWT to create web tokens, which I then save via cookieparser.
Server is running on: http://localhost:3000/
Client is running on: http://127.0.0.1:8080/client/public/
Now, I am trying to make a post request from the client (react) and try to store the token (cookie) in the browser but it is not working. It constantly saves the cookie onto the localhost:3000 domain and not on the domain where the client is running. Down below the relevant code for backend and frontend:
**// This is the cors setup for Express**
app.use(cors({ origin: 'http://127.0.0.1:8080', credentials: true }))
**// This is the request router API for creating the user and the cookie (and sending it to the browser)**
router.post('/api/users', async (req, res) => {
const user = new User(req.body)
try {
await user.save()
const token = await user.generateAuthToken()
res.cookie('auth_token', token, { httpOnly: false, sameSite: 'none' })
res.set('Content-Type', 'application/json')
res.send(token)
} catch (e) {
res.status(400).send('Did not work')
}
})
**// This is the request from the client with Axios:**
axios.post('http://localhost:3000/api/users', {
name: this.state.userName,
email: this.state.userEmail,
password: this.state.userPassword,
}, {
withCredentials: true,
credentials: 'include'
})
.then( (response) => {
const data = response
console.log(data)
})
.catch( () => {
console.log('Cant access backend')
})
this.setState({
userName: '',
userEmail: '',
userPassword: ''
})
}
Images:
Setting up cors
this is the api router:
API Router for creating user and sending it to DB
this is the client (frontend) request
Post request to register user with axios
Thank you very much in advance for your help :)
You're storing the cookie in the server. For you to store in the client. Return the cookie and store it using the static js file in the client.
//Returned cookie
document.cookie = "auth_token=token returned by server;
Related
Vite + React: http://localhost:5173
Express: http://localhost:3000
Here is the code in the express server's login route:
const mycookie = cookie.serialize("jwt", refreshToken, {
httpOnly: true, // Set the HTTP-only flag
secure: true, // Set the secure flag
sameSite: "none",
path: "/", // Set the path of the cookie to '/'
maxAge: 3600, // Set the maximum age of the cookie to 1 hour
});
// Set the cookie in the response headers
res.setHeader("Set-Cookie", mycookie);
res.json({
accessToken,
refreshToken,
});
Here is my cors config:
const allowedOrigins = require("./allowedOrigins");
const corsOptions = {
crendials: true,
origin: function (origin, callback) {
console.log(allowedOrigins.indexOf(origin));
if (allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
};
Here is my handleSubmit function in the frontend (gets called when the user clicks submit):
const handleSubmit = () => {
console.log("Login");
// axios.defaults.withCredentials = true;
axios.post("http://localhost:3000/api/v1/auth/login", { email, password });
};
Whenever the request is made to the server. The response header does contain the set-Header to set the jwt token but I am not able to see it in my applications tab under cookies in devtools. A pre-flight request also comes in which probably clears the cookie.
My networks tab:
The xhr request:
The OPTIONS request:
The Applications tab:
However, when I disable CORS in the browser, the cookie is getting set.
Networks tab (NOTICE: No PreFlight request)
Applications tab:
I tried working out different answers from stackoverflow answers like this, this, this, and many more along with a reddit post on a similar issue but nothing has worked.
P.S. : I already tried using credentials: true
In my local environment, I'm trying to set up user tokens after login in my NextJS app (localhost:3005), using the response from my express backend (localhost:3020). I can see set-cookie in the response on the server, but the cookies in the getServerSideProps is empty always.
My code is pretty basic:
Express backend
// Cors set up
cors({
methods: "GET,HEAD,OPTIONS,POST",
preflightContinue: false,
credentials: true,
origin: [
"http://localhost:3005",
...
],
optionsSuccessStatus: 204,
})
// Response - can see this cookie in set-cookie
return res.cookie("test", "test1", {
httpOnly: true,
sameSite: "none",
expires,
domain: "localhost:3005", // tried without this also
}).redirect("http://localhost:3005/login");
My NextJS app has the following:
//Login component : on login submit
const resp = await fetch(
`${BACKEND_URL}login?originalUrl=/login`, // Redirecting back
{
headers: new Headers({
Authorization: "Bearer " + someToken,
}),
credentials: "include",
method: "POST",
redirect: "follow", // Tried this but did not work
}
);
// resp.redirected is true
// Login component - triggers correctly but no cookies
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const { req } = ctx;
const { cookies } = req;
console.log("cookies", cookies ); // Always {}
return { props: {} };
};
I have been stuck with this for a while now guys, it seems like I'm missing something very trivial here. I'm not super familiar with NextJs, so any help here would be greatly appreciated. Thanks!
This was happening because of the Chrome SameSite=None + Secure requirement. Setting secure to true in the cookie resolved this issue. I did not have to set up SSL certificates for my local environment, just setting secure worked.
We have a NodeJS express server with express-session and express-mysql-session enabled. We've connected the session database and can verify there are sessions being created. Our app redirects users without a token cookie to Auth0 for authentication, and they are redirected back to our app at /callback. It does successfully set our ID token that allows our user to be logged in. However, the passport auth0 strategy complains that it cannot verify the authentication state.
'Unable to verify authorization request state.'
The server code is below, with some other parts commented out. Connecting to the session database is successful since we can see our sessions get created with each request.
const options = {
database: process.env.SESSION_DB_NAME || "sessions",
host: process.env.SESSION_DB_HOST! as string,
password: process.env.SESSION_DB_PASSWORD! as string,
port: parseInt(process.env.SESSION_DB_PASSWORD || "3306", 10),
user: process.env.SESSION_DB_USER! as string,
};
const sessionStore = new MySQLStore(options);
app.use(
session({
cookie: {
httpOnly: true,
maxAge: 24 * 3600 * 1000, // 1 day
secure: true,
},
resave: false,
saveUninitialized: true,
secret: process.env.SESSION_SECRET!,
store: sessionStore,
})
);
app.get("/login", (request, response, next) => {
// if there is no session, it's a server error
if (!request.session) {
console.error(new Error("No session in /login"));
return response.status(500).send("Server error: No Session");
}
return passport.authenticate("auth0", {
scope: "openid email profile",
})(request, response, next);
});
console.log("router: Auth0 Callback");
app.get("/callback", (request, response, next) => {
const domain = request.hostname;
console.log(request.session);
passport.authenticate("auth0", (auth0Error, token, info) => {
// if there was a problem authenticating
if (auth0Error) {
console.error(auth0Error);
return response.status(500).send();
}
// ensure we have a session. The middleware should be giving us one.
if (!request.session) {
console.error("No session in /callback.");
return response.status(500).send();
}
// if we couldn't log in
if (!token) {
if (info) {
console.log(info);
return response.status(500).send(info.message);
}
// redirect to the login page
return response.status(401).send("Cannot log you in.");
}
// parse the token we get
const user = jwt.decode(token);
// if there is no valid user token, we've got bigger problems
if (!user) {
console.error("Token is falsey after authenticating.");
return response.status(500).send();
} else if (typeof user === "string") {
console.error("Cannot decode JWT after authenticating");
return response.status(500).send();
}
request.logIn(user, (logInError) => {
// redirect to desired route
// ...
});
})(request, response, next);
});
router.get("/*", (request, response, next) => {
passport.authenticate("jwt", (authenticationError, isAuthenticated, info) => {
// Log in successful via existing JWT
if (isAuthenticated) {
return next();
}
// handle unauthenticated situations
// ...
})(request, response, next);
});
I've tried the following suggestions at https://github.com/auth0/passport-auth0/issues/70:
app.set("trust proxy", 1")
session cookie already set to secure
serving only on https
using mysql session store and default in-memory store
We are running the server in a kubernetes cluster using ClusterIP for our ingress. Env variables are being set correctly, I'm pretty sure. When I run this locally I do not have any issues.
What could cause the session to not be restored but instead recreated with each request?
Your question was posted over a year ago so I figure you've already fixed it, but I thought I'd post my solution to help others. I very recently ran into the same issue, although I was using an Apache reverse proxy instead of a Kubernetes cluster.
I've posted my solution in another SO question: https://stackoverflow.com/a/67891167/8221175
In short, my Apache configuration was missing these lines:
RequestHeader set X-Forwarded-Proto expr=%{REQUEST_SCHEME}
RequestHeader set X-Forwarded-SSL expr=%{HTTPS}
ProxyPass / http://localhost:8001/
ProxyPassReverse / http://localhost:8001/
This, along with other session/cookie configuration for express (see the SO link) fixed it. My sessions now persist and I no longer have the issue.
For those interested in why the 'Unable to verify authorization request state.' message is issued, check out my answer in this GitHub issue: https://github.com/auth0/passport-auth0/issues/89#issuecomment-856971807
I work with app, that already has its own infrastructure. The task is to prevent user login in several browser. Our application has single app architecture, so ideally user should work only in one browser tab. And I have a problem. I can’t remove cookie from client.
I. Briefly.
App settings:
Server: NodeJS
Port: 8083
Client: VueJS
Port: 8088
I use module express-session to initialize session mechanism on server side and send cookies to client. Client hasn’t set cookies.
II. Details:
Server’s root file is index.js
I do the following in it:
Plug in express module:
const express = require('express')
Plug in cors module:
const cors = require('cors')
Add cors settings:
app.use(cors({
origin: 'http://localhost:8088',
credentials: true
}))
Then I initialize session in user.js file and receive client’s connects:
Plug in express-session module:
const session = require('express-session')
Plug in routing by express.Router():
const router = express.Router()
Add session settings:
const EIGHT_HOURS = 1000 * 60 * 60 * 8
const {
SESS_NAME = 'sid',
SESS_LIFETIME = EIGHT_HOURS,
SESS_SECRET = 'test',
NODE_ENV = 'development'
} = process.env
const IN_PROD = NODE_ENV === 'production'
Initialize session:
router.use(session({
name: SESS_NAME,
resave: false,
saveUninitialized: false,
secret: SESS_SECRET,
cookie: {
maxAge: SESS_LIFETIME,
sameSite: false,
// Must have HTTPS to work 'secret:true'
secure: IN_PROD
}
}))
Receive client queries by router.post()
So what I did:
I use req.session.destroy to remove session data and expect the browser logout user from certain browser and cookies clear.
req.session.destroy(err => {
if (err) {
return res.send({ error: 'Logout error' })
}
res.clearCookie(SESS_NAME, {path: '/'})
return res.send({ 'clearSession': 'success' })
})
Unfortunately nothing magic happens
I read different topics. For example, here (GitHub) offer the conclusion: use explicit cookie’s path indication in res.clearCookie method as shown above.
That didn’t work.
Wrote this setting {path: '/'} in cookies settings. Didn’t work too.
router.use(session({
name: SESS_NAME,
resave: false,
saveUninitialized: false,
secret: SESS_SECRET,
cookie: {
path: '/',
maxAge: SESS_LIFETIME,
sameSite: false,
// Must have HTTPS to work 'secret:true'
secure: IN_PROD
}
}))
And as wrote in express-session documentation (NPM:express-session) this path is the default path for cookie storage.
Add req.session = null in req.session.destroy:
req.session.destroy(err => {
if (err) {
return res.send({ error: 'Logout error' })
}
req.session = null
res.clearCookie(SESS_NAME, {path: '/'})
return res.send({ 'clearSession': 'success' })
})
That didn’t work
delete req.session doesn’t work too.
So, how can I resolve this problem? What should I do?
adding .send('cleared cookie') made my browser clear its cache of the named cookie.
const logOutRequest = (req, res) => {
req.session.destroy((err) => {
res.clearCookie("notcookie").send('cleared cookie');
});
};
Have you tried removing the exact cookie by setting it to null that is lets say that you are dealing with a cookie named Views you could remove the cookie using req.session.Views = null
Instead of doing this
req.session.destroy(err => {
if (err) {
return res.send({ error: 'Logout error' })
}
req.session = null
res.clearCookie(SESS_NAME, {path: '/'})
return res.send({ 'clearSession': 'success' })
})
you could do the name of your session cookie and set that to null that is
req.session.sid= null
This removes the cookie from client browser
req.session.destroy(err => {
res.clearCookie("session-cookie-name", { path: "/" });
});
The most important key is setting 'domain' in the clearCookie method to solve your issue. Expressjs will return the following in the http response header. However, it seems that on the browser or some browsers that I tested, it doesn't know which cookie belonging to which domain to clear, hence, the cookie remains there. You do not need to include the path when calling clearCookie
Set-Cookie: mycookie=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT
You have to set domain like below:
req.session.destroy(err => {
res.clearCookie("session-cookie-name", { domain: 'your-domain' });
});
Then response header will become
Set-Cookie: mycookie=; Domain=your-domain; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT
The browser will clear the cookie nicely!
this works for me
#Post('signout')
async signout(#Req() req: Request, #Res({ passthrough: true }) res: Response) {
const user = req.user
if (!user) return {}
await new Promise<void>((resolve, reject) => {
req.session.destroy((err) => {
if (err) {
reject(err)
} else {
res.clearCookie('ACCESS_TOKEN', {
domain: '.xxx.com'
})
res.clearCookie('REFRESH_TOKEN', {
domain: '.xxx.com'
})
res.clearCookie('connect.sid', {
domain: '.xxx.com'
})
resolve()
}
})
})
return {}
}
I use React as a client to send request to Express with proxy and express-session set up. But every time React makes a request to Express server, a new session is created. So I checked Express alone by manually accessing to the same api url and it keep using the same session each time I refresh the page.
Project structure:
project-folder
- client // React client with proxy set up
+ src
+ package.json
+ ...
- server.js
- package.json
Inside server.js:
const session = require('express-session');
let sessionConf = {
name: 'aoid',
secret: 'stackoverflow',
resave: true,
saveUninitialized: true,
rolling: true,
cookie: {
httpOnly: false,
secure: false,
maxAge: 2000000
}
};
app.use(session(sessionConf));
app.get('/api/prod', (req, res, next) => {
let sessionId = req.sessionID; // is generated each time React client send request, works fine with api alone!
console.log(sessionId);
if (!sessionId) return res.status(401).send('Unauthorized Error');
res.status(200).send({ data });
});
Here is how React client send its request to Express:
let loadItems = async () => {
const response = await fetch('/api/prod');
const body = await response.json();
if (response.status !== 200) throw Error(body.message);
return body;
}
I think the problem comes from the misconfiguration between React and Express. Did anyone have this problem before?
fetch does not send cookie by default, you need to set it explicitly:
fetch(url, {
method: 'GET',
credentials: 'include',
// ...
})