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);
});
Related
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.
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)
i can't send the firebase token to the backend, i thought the problem was that the function was not asynchronous but it still didn't work for me, please i need help, thanks!
user.getIdToken(true)
.then(function(idToken) {
const path = 'http://localhost:8000/api/google-login'
console.log(idToken)
axios.post(path , idToken)
.then((response) => {
console.log('anda o no anda')
})
.catch((error) => {
console.log(error);
});
}).catch(function(error) {
console.log(error)
});
the error in console.
POST http: // localhost: 8000 / api / google-login 500 (Internal Server Error)
but if I copy the idtoken and send it manually to the backend it works.
you can do it now, you were sending the token without your key
'''
axios.post(path , {'token_id':idToken})
'''
i came up with this problem with ReactJS and ExpressJS: So basically user uploads some info on /info route with React & axios. then user gets route params from server side to redirect to:
axios.post('/info', SomeData)
.then(res => res.data)
.then(data =>{
window.location.replace(`/info/${data.id}`)
})
this is piece of cake but when user redirects to that page problem occurs, i need to get data from that page. i can get route params and perform request on client side like this:
componentDidMount(){
const { match: { params } } = this.props;
axios.get(`/api/info/${params.id}`)
}
but how can i get request on server side? how can express access that "id" to search it in database and query data with it to send back to client? like:
app.get('/api/info/:id', async (req,res)=>{
await db.find({id: req.params.id}, (data) =>{
res.status(200).send({data})
})
})
Any help? Thanks!
From the component itself same as the GET use your param and call your service same thing to POST
postUserInfo = () => {
const userInfo ={};
axios.post(`/api/info/${params.id}`,userInfo).then(()=>{
console.log("user info posted");
})
}
Example:
<Form onSubmit={this.postUserInfo}> </form>
My frontend is Reactjs and backend Nodejs and expressjs with Postgresql database.
I have a simple signin page which checks user authentication from database.
In my Reactjs app, after signing in, user uploads files and then there is a GET method on my nodejs which send files (res.sendFile) when user wants to get the file from server. It is just a simple
<img alt='none' src=`http://example.com/source/${filename}` />
in my Reactjs app which does request for file.
Problem: if I am not logged in to my app, I can paste the URL in my browser and the file is displayed which is NOT what I want.
I want the GET method on nodejs should check for authentication of user either if the user is signed in or not, and then only fulfill the request of sending file.
How can I do it?
Should I use some kind of POST method in my Reactjs app before it makes any GET request to the same location of GET method then parse the information then handle it to app.get etc...
This is my nodejs + expressjs.
server.js
app.post('/signin', (req, res) => { signin.handleSignin(req, res, db, bcrypt)})
app.get('/source/:fileid', (req, res) => {
const { fileid } = req.params;
res.sendFile(__dirname + /data/ + fileid);
});
./controllers/signin.js
const handleSignin = (req, res, db, bcrypt) => {
const { email, password } = req.body;
if (!email || !password ) {
return res.status(400).json('Incorrect form submission');
}
db.select('email', 'hash').from('login')
.where('email', '=', email)
.then(data => {
const isValid = bcrypt.compareSync(password, data[0].hash);
if (isValid) {
return db.select('*').from('users')
.where('email', '=', email)
.then(user => {
res.json(user[0])
})
.catch(err => res.status(400).json('unable to get user'))
} else {
res.status(400).json('wrong credentials' )
}
})
.catch(err => res.status(400).json('wrong credentials'))
}
module.exports = {
handleSignin: handleSignin
}
You have to implement authentication mechanism via cookie or session. After successful login you will set a cookie in the browser and on each HTTP req you will have access to cookie data.
Create a middleware function which will check for valid cookie data in req object for each API requests.
If a user is not logged in and trying to access the URL you won't receive data in the cookie and you can unauthorized (401) the access to that particular resource.
// On valid credentials, you can set the cookie like this
res.cookie(cookieName, cookieData, cookieOptions);
and middleware function can go like this
function checkSession(req, res, next) {
if(!req.cookies || !Object.keys(req.cookies).length){
res.sendStatus(401)
}
else next();
}
You can find more details on how to use cookie-parser here.