Is sending login credentials to server for authentication and authorization in body with content-type: application/json acceptable? - html

I was looking through multiple stack overflow questions and answers but wasn't able to get anything definitive when it comes to making a request to a server for login authentication and authorization.
My question: Is sending login credentials to server for authentication and authorization in body with content-type: application/json acceptable?
const handleSubmit = (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const [email, password] = formData.values();
fetch('/login', {
method: 'POST',
headers : {'Content-Type' : 'application/json'},
body: JSON.stringify({email, password})
}).then(result =>{ //result is a ReadableStream object
return result.json(); //result.json() parses the data into useable format (json)
}).then(data => {
if(data.isAuthenticated){
handleUserAuthChange(true, ()=>{
history.push('/vehicles');
});
}
}).catch(err => console.log(err));
}

As long as you are using HTTPS, yes. This is a pretty common way of handling login requests

There is a great "tutorial" here on stackoverflow.
Unless the connection is already secure (that is, tunneled through HTTPS using SSL/TLS), your login form values will be sent in cleartext, which allows anyone eavesdropping on the line between browser and web server will be able to read logins as they pass through. This type of wiretapping is done routinely by governments, but in general, we won't address 'owned' wires other than to say this: Just use HTTPS.
In short, you want to always use HTTPS to be sure it's safe.

Related

/favicon.ico after login in Node JS

I have created a login/register system using express (passport) on my website and I am saving the originalUrl before the user was redirected to the login page but every time after the login, the user is redirected to /favicon.ico instead of the saved Url. Could someone tell me what is the cause of the issue?
My app.use():
app.use((req, res, next) => {
if (!['/login'].includes(req.originalUrl)) {
req.session.returnTo = req.originalUrl;
}
res.locals.currentUser = req.user;
res.locals.success = req.flash('success');
res.locals.error = req.flash('error');
next();
})
My /login get and post request:
app.get('/login', (req, res) => {
res.render('login');
})
app.post('/login', passport.authenticate('local', { failureFlash: true, failureRedirect: '/login' }), (req, res) => {
const redirectUrl = req.session.returnTo || '/';
console.log(redirectUrl);
delete req.session.returnTo;
res.redirect(redirectUrl);
})
You have a race condition in your session between two incoming requests.
Your app.use() middleware is going to see the /favicon.ico request and will overwrite the req.session.returnTo value that your login route may have just set. If these two requests come in one immediately after the other (which is likely when a browser first visits your site), then the /favicon.ico route will mess up the session state you just tried to set with the /login route.
I can't tell what that middleware is trying to do, but it looks like it's very capable of overwriting stuff in the session that other requests are in the middle of using. Redirects after login are much, much safer to do by putting the eventual redirect URL in the query parameter. Then it is stateless on the server and isn't subject to these types of race conditions when there is more than one incoming request to the server form the same user.
FYI, you could also fairly easily prevent this particular problem (though not other potential race conditions) by just putting this route handler before your middleware:
// put this before your middleware
app.get("/favicon.ico", (req, res) => {
res.sendStatus(404);
// or instead of a 404, send an actual favicon.ico file
// just don't let routing continue to your middleware
});
This would keep your middleware from running at all when /favicon.ico is requested and thus prevent that specific place that a race condition with your session data is caused.

Calling authentication API from different domain won't set cookies

I am running a Flask API which sets cookies (JWT) if username & password is correct.
I am requesting the API from https://example.ngrok.io and the API is located at https://myAPIDomain.com.
The Set-cookie header is present in the response header, but no cookies are set (viewing Chrome application cookie storage).
Here is the backend configuration:
response.headers['Access-Control-Allow-Origin'] = request.headers['Origin']
response.headers.add('Access-Control-Allow-Credentials', 'true')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type')
response.headers.add('Access-Control-Allow-Headers', 'cache-control')
response.headers.add('Access-Control-Allow-Headers', 'X-Requested-With')
response.headers.add('Access-Control-Allow-Headers', 'Authorization')
response.headers.add('Access-Control-Allow-Headers', 'set-cookie')
response.headers.add('Access-Control-Allow-Headers', 'user-agent')
response.headers.add('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, DELETE')
Setting cookie in backend:
resp.headers.add("set-cookie",'cookieKey:cookieValue; Domain=myAPIDomain.com; Max-Age=3600; Secure; Path=/; SameSite=None')
Also tried not specifying the domain:
resp.headers.add("set-cookie",'cookieKey:cookieValue; Domain; Max-Age=3600; Secure; Path=/; SameSite=None')
None of these solutions worked.
Here is a picture of the response headers in Chrome:
https://i.imgur.com/D3cq16Z.jpg
The cookies that the API is supposed to set is used for future API endpoint authentication. So when I send another request:
var myHeaders2 = new Headers();
myHeaders2.append("Content-Type", "application/json");
var requestOptions2 = {
method: 'GET',
headers: myHeaders,
redirect: 'follow',
credentials: 'include'
};
fetch("https://myAPIDomain.com/endpointWhichRequiresCookies", requestOptions2)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
But cookies are not sent (obviously since chrome is not setting the cookies). Here is what the backend receives: https://codebeautify.org/online-json-editor/cb81fb64
I know a workaround would be to reply with cookies to frontend as JSON reply and frontend sends the cookies as different headers (since you cannot send "cookie" header from frontend), but this is not the best solution for us.
The only reason we are calling the API from ngrok is because we are doing localhost testing.
The cookies are being set with Postman, so I do not think the backend is at fault here.
Any ideas? We have been at this for days now, without being able to solve the issue.
Found the solution!
We are sending two requests:
Login Post request with username & password (using Fetch POST)
Request to get information from backend (using Fetch GET) - Backend will use the cookies that were set in request #1 to authenticate the request.
Problem was that we didn't send request #1 with credentials: "include", because we didn't think it was needed for the first request. Our second request always had credentials: "include", but apparently Chrome will disregard the cookies if you do not set credentials: "include" on both requests.

404 Error when making a POST request using fetch()

I am following this tutorial from Hakernoon to set up Node.js with MySQL using my machine as local server.
I am using a few different tools, but they don't seem to be a problem. I'm using:
Ubuntu 16.04
Node.js
MySQL Workbench (He uses Homebrew because his is a Mac)
MySQL
knex.js
Express
*(Ubuntu and Workbench are the only differences)
Summarizing,the whole tutorial works well except when I reach the part of 'Login'. Here, we build a form to enter the user name and password making a POST requests. Then, if everything is OK, it returns a 200 status, else shows a message saying the login failed. And here is the problems. Every time I try to log in (even when being sure the credentials are correct and knowing that that user is stored in the database) it tells me that the login fails.
Checking on the browser inspect tool, it shows a Fail to load resources: the server responded with a status of 404 (Not found). It also points out that the problems is in the fetch() function I use in my post function to make the POST requests.
I checked the documentation on fetch() but is does not clarify much. Also I tried to contact the author of the tutorial and I got no answer. What am I doing wrong with fetch to give me the 404 status?
Also, every time I create a new user (also a POST request), it does it with no problem and I can see it in the database in MySQLWorkbench.
This is the file (app.js) with the code to create users and login:
const CreateUser = document.querySelector('.CreateUser')
CreateUser.addEventListener('submit', (e) => {
e.preventDefault()
const username = CreateUser.querySelector('.username').value
const password = CreateUser.querySelector('.password').value
post('/createUser', { username, password })
})
const Login = document.querySelector('.Login')
Login.addEventListener('submit', (e) => {
e.preventDefault();
const username = Login.querySelector('.username').value
const password = Login.querySelector('.password').value
post('/login', {username, password})
.then(({status}) => {
if(status === 200)
alert('login success')
else
alert('login failed')
})
})
function post (path, data) {
return window.fetch(path, { <========= Here is where the browser shows the problem
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
}
***I am not posting the rest of the code to not make it too long, if more info is needed, let me know). Thanks!

Vue Axios CORS policy: No 'Access-Control-Allow-Origin'

I build an app use vue and codeigniter, but I have a problem when I try to get api, I got this error on console
Access to XMLHttpRequest at 'http://localhost:8888/project/login'
from origin 'http://localhost:8080' has been blocked by CORS policy:
Request header field access-control-allow-origin is not allowed
by Access-Control-Allow-Headers in preflight response.
I have been try like this on front-end (main.js)
axios.defaults.headers.common['Content-Type'] = 'application/x-www-form-urlencoded'
axios.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
and this on backend (controller)
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
and vue login method
this.axios.post('http://localhost:8888/project/login', this.data, {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Origin, Content-Type, X-Auth-Token"
}
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err.response);
});
I've searched and tried in stackoverflow but does not work, how I can solve it? thank you so much for your help
CORS is the server telling the client what kind of HTTP requests the client is allowed to make. Anytime you see a Access-Control-Allow-* header, those should be sent by the server, NOT the client. The server is "allowing" the client to send certain headers. It doesn't make sense for the client to give itself permission. So remove these headers from your frontend code.
axios.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
this.axios.post('http://localhost:8888/project/login', this.data, {
headers: {
// remove headers
}
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err.response);
});
For example, imagine your backend set this cors header.
header("Access-Control-Allow-Methods: GET");
That means a client from a different origin is only allowed to send GET requests, so axios.get would work, axios.post would fail, axios.delete would fail, etc.
This may occur you are trying call another host for ex- You Vue app is running on localhost:8080 but your backend API is running on http://localhost:8888
In this situation axios request looking for this localhost:8080/project/login instead of this http://localhost:8888/project/login
To solve this issue you need to create proxy in your vue app
Follow this instruction Create js file vue.config.js or webpack.config.js if you haven't it yet inside root folder
then include below
module.exports = {
devServer: {
proxy: 'https://localhost:8888'
} }
If you need multiple backends use below
module.exports = {
devServer: {
proxy: {
'/V1': {
target: 'http://localhost:8888',
changeOrigin: true,
pathRewrite: {
'^/V1': ''
}
},
'/V2': {
target: 'https://loclhost:4437',
changeOrigin: true,
pathRewrite: {
'^/V2': ''
}
},
}
}
If you select the second one in front of the end point use the V1 or V2
ex - your end point is /project/login before it use V1/project/login or V2/project/login
as per the host
Check this Vue project - https://github.com/ashanoulu/helsinki_city_bike_app/tree/main/Front_End/app-view
Version - Vue3
For more details visit - Vue official documentation
in my case
curl && postman works but not vue axios.post
Access to XMLHttpRequest at 'http://%%%%:9200/lead/_search' from origin 'http://%%%%.local' has been blocked by CORS policy: Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.
So, the issue is on vue side not the server!
The server response contains "access-control-allow-origin: *" header
I had the same problem even everything was fine on the server side..
The solution to the problem was that API link I hit was missing the slash (/) at the end so that produced CORS error.
in my case adding this in my php backend API function it worked
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS, post, get');
header("Access-Control-Max-Age", "3600");
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');
header("Access-Control-Allow-Credentials", "true");
You may try :
At the backend,
npm install cors
then, at the backend app.js , add the following,
const cors = require('cors')
app.use(cors({
origin: ['http://localhost:8082'],
}))
Hopefully, It may help.
Dev Proxy is your solution
With DevProxy you define a specific path, or a wildcard (non static) that Node (the server runs vue-cli dev server) will route traffic to.
Once defined (a single entry in vue.config.js), you call your api with the same URI as your UI (same host and port) and Vue is redirecting the request to the API server while providing the proper CORS headers.
look more at https://cli.vuejs.org/config/#devserver-proxy
I'm building an app in Vue.js and added global headers in the main.js file
Example:
axios.defaults.headers.get['header-name'] = 'value'
For handling CORS issues you may now have to make changes on the client side, it is not just a server issue.
Chrome has a few plugins: https://chrome.google.com/webstore/search/cors?hl=en
for some cases, it is not vue issue. sometimes it's back-end issue.. in my case I've made API in nest JS, and I didn't enable CORS = true.. That's why I am getting CORS policy error.
in my case, the API would return CORS policy, but the problem lied with my url.
my calls were like "https://api.com//call", that extra slash was causing the problem.
changing the url to "https://api.com/call" fixed the error.

Angular 4 Access-Control-Allow-Origin error with localhost url

I'm leaning Angular, trying to use HTTP get to a json file on localhost url http://api.test/work.but I get the error bellow:
Failed to load http://api.test/work: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.
by the way I use laravel to generate json
This means that, http://api.test/work.but does not allow cross origin request. If this link is hosted by you, you will have to allow cross origin request.
In your response headers, you will have to send:
Access-Control-Allow-Origin: *
This means, any body request to your server.
If you want to allow access to specific domains, you can specify them.
Read more abouut CORS here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
I was getting the same error; "Failed to load http://web-api_link: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.". I already had CORS installed on my chrome browser. It was on. I clicked on it and checked for "Enable cross-origin resource sharing". I disabled it, and enabled it again. It worked for me!
private callServiceGet(url: string, wC?: boolean): Promise<any> {
return this.http.get<any>(url, { observe: 'response', withCredentials: wC })
.catch(err => [ err ])
.map(data => ({ status: data.status, data: data.body }))
.toPromise();
}
with wC you have credentials to allow localhost too. It's simply a boolean, so set it to true if you want a complete access.
for example you could use it like this:
getJSON(): Promise<any> {
return this.callServiceGet(`http://api.test/work`, true);
}