I have a login system that supposed to save user login into a session.
my back-end is Nodejs(express) and my front-end is Reactjs(with redux).
I'm using axios to send username and password to the back-end. my data get evaluated and user ID get saved into a session. so far so good but if I send a request to another route the user session will be gone.
I looked at some similar problems but got nothing. Here is my code:
This is my express-session
const session = require('express-session');
app.use(session({
secret: process.env.SESSION_SECRET,
resave: true,
saveUninitialized: true,
cookie: {maxAge: 259200000, httpOnly: true}
}));
This is my axios request
Note: I already set base url for url: "signin", the request is going
to http://localhost:5000/signin
axios({
method: "post",
url: "signin",
withCredentials: false,
headers: {
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json",
Accept: "application/json"
},
data:{
"username": /*username*/`,
"password": `/*password*/``
}
}).then(res => {
if(res.status === 200){
if(res.data.status){
dispatch({type: "LOGGED_IN"}) // just changes the state{authenticated : true}
}else{
dispatch({type: "NOT_LOGGED_IN"}) // just changes the state{authenticated : false}
}
}
})
}
In signin route, after my username and password get evaluated I save user ID into session:
req.session.user = user._id
next()
In the next middleware I still have req.session.user.
I have an API for checking the user session
if(req.session.user){
res.send(true);
}else{
res.send(false);
}
If I make a request to that after login, I get false
When I login and make the request with postman it returns true but with axios I get false
my axios request to that API is :
axios({
method: "post",
url: "checklogin",
withCredentials: false,
headers: {
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json",
Accept: "application/json"
}
}).then(res => {
if(res.status === 200){
if(res.data){
dispatch({type: "LOGGED_IN"})
}else{
dispatch({type: "NOT_LOGGED_IN"})
}
}
})
OK, problem solved. I set withCredentials to false because when it was true I was keep getting CORS error. If my origins (in cors module setting ) be equal to "*" credentials is not allowed so I changed my origin to my front-end domain and set credentials to true (in cors module setting) and now I'm getting true response.
Related
So i use actix web on the backend and svelte on the frontend
This sends the cookie to the frontend
let cookie = auth_authority.create_signed_cookie(UserClaims {
id: o.id,
role: Role::User,
})?;
info!("Logged in user");
info!("{}", cookie);
Ok(HttpResponse::Ok()
.cookie(cookie)
.json("You are now logged in"))
And this is the CORS config:
let cors = Cors::default()
.allowed_origin("http://localhost:3000")
.allowed_methods(vec!["POST", "GET"])
.allowed_headers(vec![
header::AUTHORIZATION,
header::CONTENT_TYPE,
])
.expose_any_header()
.supports_credentials()
.max_age(3600);
And this is the function that fetches the data for svelte:
const resp = await fetch("http://0.0.0.0:4000/login", {
method: "POST",
mode: 'cors',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
username,
password,
}
)
})
I do not recieve the Set-Cookie header when using a web browser but when i use curl i do(Most likely caused by CORS). I can send other headers that i create but i cannot send Set-Cookie no matter what. I used the .supports_credentials() method to allow cookies but that did nothing.
Can someone guide me into a direction to solve this or does someone know how to solve it?
I am unable to retrieve a cookie that I sent earlier.
As part of login, I sent back a refresh token as an httpOnly cookie.
const payload = {name, email};
console.log("payload: ", payload);
const accessToken = jsonwebtoken.sign(payload, process.env.ACCESS_TOKEN_KEY, { expiresIn: '15m' });
const refreshToken = jsonwebtoken.sign(payload, process.env.REFRESH_TOKEN_KEY, { expiresIn: '1d' });
console.log("Access Token:", accessToken); // access token is generated
console.log("Refresh Token:", refreshToken); // refresh token is generated
res.cookie('refreshToken', refreshToken, { httpOnly: true, secure: false, sameSite: 'Lax', maxAge: 24*60*60*1000 }); // call succeeded. what is the name of cookie?
res.json({ accessToken });
Later on a refresh endpoint I look for a cookie and can't find it:
export const handleRefreshToken = async (req, res) => {
console.log("Request Cookies", req.cookies);
const cookies = req.cookies;
if (!cookies?.refreshToken) return res.sendStatus(401);
I see the following cookies:
_ga: 'xxxxxxxxxxxxxxxxx',
_gid: 'xxxxxxxxxxxxxxxx',
_gat_gtag_UA_xxxxxx: 'x',
_ga_QPY49S2WC4: 'xxxxxxxxxxxxxxxxxxx'
This is on my dev environment with nodejs running on localhost:5000.
Update: Using devtools (Network) I see the cookie in the response on the client side. The name of the cookie is 'refreshToken'. However, the cookie doesn't show up on the browser when I look at the cookies on the browser. Perhaps, the cookie isn't being retained on the browser. Any suggestions on why that could be?
Update2: The link provided by #Konrad Linkowski worked. When the axios request is made from the client, I needed the option "{ withCredentials: true }".
The error was on the client end. The express code functioned correctly. This link explains it: Does Axios support Set-Cookie? Is it possible to authenticate through Axios HTTP request?
My original call on the client side (using axios) was:
const res = await axios.post('/login', { ident: email, password });
Instead it should have been:
const res = await axios.post('/login', { ident: email, password }, { withCredentials: true });
I have a node.js application served over https. I would like to call an API from that application. The API is also served over https and it has been generated using the express-generator.
Unfortunately the call never works. There is no error message. The call never reaches the API.
Strangely enough if I try to call another public API (e.g. https://api.publicapis.org/entries') that is working perfectly.
Here is my call:
const requestBody = {
'querystring': searchQuery,
};
const options = {
rejectUnauthorized: false,
keepAlive: false, // switch to true if you're making a lot of calls from this client
};
return new Promise(function (resolve, reject) {
const sslConfiguredAgent = new https.Agent(options);
const requestOptions = {
method: 'POST',
body: JSON.stringify(requestBody),
agent: sslConfiguredAgent,
redirect: 'follow',
};
fetch('https://192.168.112.34:3003/search', requestOptions)
.then(response => response.text())
.then(result => resolve(result))
.catch(error => console.log('error', error));
});
};
And here is the API which I would like to call:
router.post('/', cors(), async function(req, res, next) {
req.body;
queryString = req.body.querystring;
let data = JSON.stringify({
"query": {
"match": {
"phonetic": {
"query": queryString,
"fuzziness": "AUTO",
"operator": "and"
}
}
}
});
const { body } = await client.search({
index: 'phoneticindex',
body: data
});
res.send(body.hits.hits)
});
What is wrong with my API and/or the way I am trying to communicate with it?
UPDATE: I receive the following error in the fetch catch block: 'TypeError: Failed to fetch'
When I create a request in Postman I receive the expected response.
UPDATE 2: This is most probably an SSL related issue. The webapp is expecting an API with a valid certificate. Obviously my API can only have a self signed cert which is not enough here. How can I generate a valid cert for an API which is running on the local network and not publicly available?
UPDATE 3: I managed to make it work by changing the fetch parameters like this:
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
mode: 'cors',
body: raw,
agent: httpsAgent,
redirect: 'follow',
})
and on the API side I added the following headers:
'Content-Type': 'application/json',
'Access-Control-Allow-Origin' : 'https://localhost:2200',
'Access-Control-Allow-Methods' : 'POST',
'Access-Control-Allow-Headers' : 'Content-Type, Authorization'
I also added app.use(cors()) and regenerated the self-signed certificates.
I am building a Nuxt app that uses fastify for the backend API. I am using httpOnly session-cookies for authentication. So far everything works fine but the issue i have is that Nuxt just has a hardcoded cookie timeout of 30 minutes. I have updated this to 24hrs but what i really want to do is have Nuxt refresh the timeout after each request.
I see in my database that the fastify session cookie get updated after each request and the session expiration gets updated after ANY user API request. So, this looks like the backend is functioning correctly but the frontend need to update the auth._token_expiration.local value.
You can see in the config that i manually set the maxAge to 24hrs, cant i have the frontend update the auth._token_expiration.local value automatically when making successful API requests? my token is stored as sessionId
nuxt.config.js
auth: {
redirect: false,
strategies: {
local: {
token: {
required: false,
type: false,
maxAge: 86400 // seconds - 1 day
},
user: { property: false },
endpoints: {
login: {
url: '/user/login',
method: 'post',
withCredentials: true
},
logout: {
url: '/user/logout',
method: 'post',
withCredentials: true
},
user: {
url: '/user/profile',
method: 'get',
withCredentials: true
}
}
},
localStorage: false,
cookie: true
}
},
So i discovered that the sessionId Expires data DOES get updated after each api request, so that is good. To make this function i also had to set the nuxt.config token maxAge to 0. This solved my issues.
I am running my React js web app in one port 3000.
For node server I am using 4000.
While calling fetch method it returns `400 Bad request'.
Error
POST http://localhost:4006/auth/admin 400 (Bad Request)
react code npm started in 3000 port
fetch('http://localhost:4000/auth/admin',
{ mode: 'no-cors',
method: "POST",
body: JSON.stringify({
username:"admin",
password:"1234"
}),
headers: {
"Content-Type": "application/json",
'Accept': 'application/json, text/plain, */*',
credentials: "omit", //
// "Content-Type": "application/x-www-form-urlencoded",
},
})
.then((response) => console.log(response));
node code running in 4000 port
const passport = require("passport");
const route = require("../constants/routeStrings");
const keys = require("../config/keys");
const processStatus = require("../constants/processStatus");
const success = {
status: processStatus.SUCCESS
};
const failute = {
status: processStatus.FAILURE
};
module.exports = app => {
app.post('/auth/admin', passport.authenticate("local"), (req, res) => {
res.send(success);
});
};
Do not stringify the body. Change from
body: JSON.stringify({
username:"admin",
password:"1234"
}),
to
body: {
username:"admin",
password:"1234"
},
The 400 response is raised by passport since it is unable to read your params. You need to tell your "node" app to parse them before your actual routes.
// Import body parser, you should read about this on their git to understand it fully
const parser = require('body-parser');
const urlencodedParser = parser.urlencoded({extended : false});
// before your routes
app.use(parser .json());
app.use(urlencodedParser) // This will parse your body and make it available for your routes to use
Then do your other calls.
Also, make sure that you are sending username and password keys, otherwise read the documentation on how to change these key names to something else
I suffered long hours, but I overcame it throw writing those lines of code blocks. I successfully send the request to the server's controller, hopefully yours: make it try.
First define a async function to make POST request:
async function _postData(url = '', data = {}) {
const response = await fetch(url, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
credentials: 'same-origin',
redirect: 'follow',
referrerPolicy: 'no-referrer',
headers: {
"Content-type": "application/json; charset=UTF-8"
},
body: JSON.stringify(data)
});
return response.json();
}
Now create a request JSON payload:
let requestPayload = {
propertyName1: 'property value1',
propertyName2: 'property value23',
propertyName3: 'property value',
So on
}
Note: Request model will be your desired model, what request payload you actually send.
Now make a request using this payload including your end point URL:
_postData('http://servername/example', requestPayload )
.then(json => {
console.log(json) // Handle success
})
.catch(err => {
console.log(err) // Handle errors
});
100% worked on my project.