How do I fetch cookie data with React? - javascript

I have a MERN + Passport.js application that is using fetch to make a call to my express API in order to retrieve data stored in a cookie. I have my React frontend routed to localhost:3000, and my backend routed to localhost:3001. My cookies are not being saved, and thus my session is not persisting within my browser. It is not an issue with the express-sessions or passport middleware, as when I use POSTMAN to execute the necessary calls to my API, the cookie is stored and the session persists.
It is only when I attempt to pass this information through/to my front end that things go wrong. I have been stumped for a while and can't seem to find an answer anywhere.
This is the line that I am using to save the cookie:
handleLogin(event) {
event.preventDefault();
fetch("http://localhost:3001/users/login", {
// credentials: 'include',
credentials: 'same-origin',
method: "post",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: this.state.username,
password: this.state.password
})
})
// .then( (response) => response.json())
.then( (response )=> {
if(response.message){
alert(response.message);
}
})
Which correctly calls my API, which logs the current session, user data, and cookie.
Upon refreshing and making another request, I lose the cookie (it was never properly stored in the first place I think), and all session data.
This is the get request that I make whenever I navigate to a new page:
componentDidMount(){
var current_user = "";
fetch("http://localhost:3001/users/", {
// credentials: 'include',
credentials: 'same-origin',
method: "get",
headers: {
'Accept':'application/json',
'Content-Type': 'application/json'
}
})
// .then( (response)=> response.json())
.then( (response)=> {
if(response.user){
current_user = response.user;
this.setState({
user: current_user
}), ()=> console.log(response);
}
})
}
In response, I get an undefined user and no other response, and the cookie is never stored in my browser. But again, if I do this in POSTMAN, strictly doing the POST request followed by that GET request, the proper user data is returned and the cookie is shown in POSTMAN as well.
Any idea as to why fetch is not passing the cookie information back to my front end? I have tried both credentials: 'include' and credentials: same-origin.
Thank you!

It seems like the problem, or at least part of it, is your use of same-origin. From Mozilla docs (italics my own):
omit: Never send cookies.
same-origin: Send user credentials (cookies, basic http auth, etc..) if the URL is on the same origin as the calling script. This is the default value.
include: Always send user credentials (cookies, basic http auth, etc..), even for cross-origin calls.
The definition of "same origin" does not include different ports.
If you change same-origin to include, fetch should start managing your cookies correctly.
If not - do you know for sure that the cookie is "never stored in the browser"? With Chrome, you can check chrome://settings/siteData.

Related

redirect to certain route after a response using fetch?

here is my problem, when I post from using the classic form post the
post goes to server if certain conditions are met the server then redirect to the specific page
but when I use fetch request for posting due to the need to model data a certain way a yet with similar request the server doesn't redirect me to the wanted page and it just stays where it is , any solution my bet is I need to add something to my fetch so the fetch will respond to the redirection sent by the server.
// Front-End Code Fetch
fetch('/pin', {
credentials: "same-origin",
mode: "same-origin",
method: 'post',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(inputJSON)
})
// Back-End Code
exports.pin = async (req, res, next) => {
res.redirect(`/app`)
}
// Routing model
app.route('/pin')
.post(authentication.pin)
app.route('/app')
.get(toAppControler.getApp)
See this answer: you can't redirect a POST to a GET.
Can you add this to your code? This way you can either GET or POST /app?
app.route('/app').post(...)

API call (fetch) with user token through Javascript (React.js)

how are you guys doing??
I'm trying to fetch data (in React with JS) from a website X (that has cors-enabled) but need first to get a user token from a website Y and I don't know how to figure that out. Somehow I think that I have to get the user token from Y, save it in a variable and add it as a header on the other call but I'm kinda lost with the GET/POST methods.
By now what I have is this but somehow doesn't work, I think I'm not sending properly the head or making the actual calls correctly:
getToken(){
const url = 'websiteOfTheToken';
const response = await fetch(url);
const data = await response.json();
this.setState({
userToken: data.image
});
}
And:
getData(){
const response = await fetch('websiteOfTheData', {
method: 'POST',
mode: 'no-cors',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'accept': 'application/json',
'Content-Type': 'application/json'
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
body: JSON.stringify({
title: '' // I don't know what is this doing
})
}).catch( error => {
console.log('Error fetching and parsing data ', error)
});
this.setState({
data: response.json()
});
}
Thank you in advance!!
A few things I want to say / ask about:
Typically you get the token after a successfull login, so a standard request for a token would be a post request with the user data.
An await in javascript works only inside async functions, read about it.
The most common practice for handling api responses (Promise) is to use then-catch blocks, read about it.
A typical JWT header you need to attach to your requests looks like this: "Authorization": "Bearer theactualtoken1234".
The body of the fetch post request is the actual data you're sending to the server. So its contents are totally arbitrary - I also don't have an idea what this "title": "" is doing...

How to redirect to new page with Authorization Headers after login?

I have a login page. On click of login button, the details are sent to server and are validated. A token is received in return after successful validation. I need to know how to redirect to a new page(/dashboard) using this token set as authorization header. Note that I am using vanilla Js
function login(){
var userEmail = document.getElementById("email_field").value;
var userPass = document.getElementById("password_field").value;
axios
.post('http://localhost:5000/login',{
username: userEmail,
password: userPass,
})
.then( (response) => {
if(response.data.success){
let token = response.data.token;
localStorage.setItem("SavedToken", token);
axios.defaults.headers.common['Authorization'] = token;
//some code here to redirect to localhost:5000/dashboard with authorization header
}
alert(response.data.message);
console.log(response)
})
}
I had the same issue when I was writing vanilla authorization app.
From my experience there is no easy way to achieve this.
The browser sends req and without us being able to meddle with the headers, without using http intercepting tool to catch outgoing browser http requests.
So, one way to allow to send http req and utilize the token we have is to use this work-around: the server serves HTML pages without any issue, but the body is empty. For each page the is a built in "onload" function. This function sends anther http req via "fetch" (or any other tools), and gets back the accual page after the token is validated by the server.
Then you render the accual page into the body.
For Example:
fetch("http://localhost:5454/signup")
.then((res) => res.json())
.then((res) => {
const token = res.token;
localStorage.setItem("token", token);
fetch("http://localhost:5454/", {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
})
.then((res) => res.text())
.then((rawHtml) => {
document.write(rawHtml);
document.close;
});
Another way to do it without sending multiple http request to each endpoint is the single page approuch:
You get only the main page of the application that has a nav bar in it. Each navbar click sends a req with the token and gets the HTML, and render is to a specific location within the main page.
As I said, this is a very crud way.
Another option is in the server, after the login and in the generation on the token - add the token to a cookie, that the browser will store, and will add to every outgoing request.
I will be glad to hear from anyone that has a better solution...
ANyway, hope this helps.

GitHub OAuth App - getting token

I have an simple web app I'm testing on localhost (using http-server) in which I'm trying to authorise it following the GitHub tutorial.
I was able to redirect to GitHub page so the user can login there and get the temporary code returned from GitHub as query parameter.
Yet I can't get auth token because every time I send a POST request with all the required data I'm getting CORB error.
The code I'm using to do that:
const getGitHubToken = async code => {
return await fetch(authData.accessTokenURL, {
method: 'POST',
body: {
client_id: authData.client_id,
client_secret: authData.client_secret,
code
},
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
};
So my questions are:
why isn't it working
is it safe to keep client_id and client_secret on client side
any suggestions if it's good idea to apply this approach when my aim is to create an app able to query GitHub API (general stats, public repos), how can I do it better?

fetch() cannot set cookies received from the server?

I am using Express.js server. With cookie-parser I have opened this endpoint
app.get("/s", (req,res) => {
res.cookie("bsaSession", req.session.id)
res.send("set cookie ok")
})
When I manually use the browser to http://localhost:5555/s where I have the website running the browser debug console shows that the cookie have been applied.
But when I use fetch API to do the equivalent, it does not set the cookie.
async trySetCookie()
{
await fetch("http://localhost:5555/s",{
method: 'GET',
credentials: 'same-origin'
})
}
Why?
I have found the solution. The core of this problem being that my button to trigger the fetch is on http://localhost:3000/. The server is on http://localhost:5555/ (I am simulating real environment on my own machine)
The problem is that this fetch call
async trySetCookie()
{
await fetch("http://localhost:5555/s",{
method: 'GET',
credentials: 'same-origin'
})
}
Without credentials, the browser cannot send or receive cookies via fetch (https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials)
With credentials as same-origin I can see the cookies coming from the server in the Set-Cookie response header, but nothing is being stored in the browser. One strange thing is that this response always have HttpOnly tagged after the cookie string regardless of my {httpOnly : true/false} settings on the server. In the case of manually using the browser to the page to do GET request, HttpOnly is being respected as usual, and the cookies are set.
So the solution is to set credentials as include to allow cross-origin cookie sending.
async trySetCookie()
{
await fetch("http://localhost:5555/s",{
method: 'GET',
credentials: 'include'
})
}
Also, on the server side you need to allow a particular origin manually with new headers:
app.get("/s", (req,res) => {
res.cookie("bsaSession", req.session.id, {httpOnly:false})
res.header('Access-Control-Allow-Origin', 'http://localhost:3000')
res.header('Access-Control-Allow-Credentials','true'
res.send("set")
})
Not doing this results in
XMLHttpRequest cannot load http://localhost:5555/s. Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.
But the cookie will be set regardless of this error. Still nice to include that header to silence the error.
If you are using cors middleware for Express it is even easier. You can just use these options
var corsOptions = {
origin: 'http://localhost:3000',
credentials: true
}
app.use(cors(corsOptions))
And of course credentials: 'include' is still required at the client side.
5argon's solution was great for me otherwise, but I had to set origin in express cors to true. So in backend:
app.use(
cors({
origin: true,
credentials: true,
})
);
And in fetch:
fetch("http://localhost:5555/s", {
method: 'GET',
credentials: 'include'
})

Categories