XMLHttpRequest does not reload page - javascript

firebase.auth().onAuthStateChanged(function(user) {
if (user) {
console.log("Signed in");
return user.getIdToken(true).then(function(idToken) {
// Send token to your backend via HTTPS
// ...
console.log("Token = " + idToken);
var bearer = "Bearer " + idToken;
var xhr = new XMLHttpRequest();
xhr.open("GET", "dashboard");
xhr.setRequestHeader("Authorization", bearer);
return xhr.send();
}).catch(function(error) {
// Handle error
console.log(error);
});
}
else{
console.log("User signed out");
}
});
I am doing the following request. In my server I am receiving logs saying it has received the request and it has recognized it as an authenticated user and so on. In fact, to test, I sent it to a simple endpoint like /hello where I simply do res.render("Hello World!") in my express app but even then nothing happens on the page. Why isn't the browser refreshing the page to represent the response from the endpoint?

Why isn't the browser refreshing the page to represent the response from the endpoint?
Because the entire point of Ajax is that the response is passed to JavaScript so you can do what you like with it instead of loading a whole new page.
You can access the data with:
xhr.addEventListener("load", function () {
console.log(this.responseText);
});

What you're now building is essentially a webapp which communicates with an API from JS. Since you're doing logic client-side instead of server-side now, instead of using Express to redirect to another route after login you have to do client-side routing.
This means after you've received a successful response (200) from the API you have to direct the client to the desired route. So you would do something like:
if (user) {
window.location = "/dashboard";
}
However it seems like you're doing client-side logic by accident. Therefore I would recommend to stop using XMLHttpRequest and just use <form> as you were doing in your other post and continue your efforts to try to get that working. In that way you can continue building a server-rendered ExpressJS app.

Related

Security for arguments going to a php script

I am currently developing a register & login system with HTML, CSS, JavaScript and PHP.
Expiration
The user inputs his email and password in the with CSS styled input fields of html. Now if the user press the login button, JavaScript gets the user email and password of these input fields and calls the login.php script with the parameters email and password. The PHP script does the main work than and returns the access or denial to JavaScript. At last JavaScript shows the user error or give access based on the PHP script answer.
Detailsnippet of Javascript
function loginuser(email, password) {
if (email || password) {
query('./php/login.php', null, [['email', email], ['password', password]]).then(function(obj) {
console.log("REPLY");
if (obj.error) {
//ERROR
}
else {
//ACCESS
}
});
}
else {
//PASSWORD , EMAIL CAN NOT BE EMPTY
}
}
function query(url, cmd_type, data_array) {
var request = new XMLHttpRequest();
var params= '';
params = params + 'cmdtype=' + encodeURIComponent(cmd_type) + '&';
if (data_array) {
data_array.forEach(function(item) {
if (Array.isArray(item[1])) {
var serialized_tda = '';
item[1].forEach(function(tdai) {
serialized_tda = serialized_tda + "|" + tdai[0] + "," + tdai[1] + "|";
});
params = params + item[0] + '=' + encodeURIComponent(serialized_tda) + '&';
}
else {
params = params + item[0] + '=' + encodeURIComponent(item[1]) + '&';
}
});
}
return new Promise(function(resolve, reject) {
request.open('POST', url, true);
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.onreadystatechange = function() {
if (request.readyState === XMLHttpRequest.DONE) {
if (request.status === 200) {
var response = JSON.parse(request.response);
resolve(response);
}
else {
resolve({error: 'Cant connect!'});
}
}
};
request.send(params);
});
}
Doubts - The problem
Since I am calling a script with parameters it can be found in the network tab in browser dev console with all parameters I sent.
Does this disappear when I use a SSL certificate in production later, do I have to encrypt data in JavaScript before I can send it via parameters to php or is this normal?
No, using SSL will not cause that information to disappear from the Network tab in production.
SSL encrypts the data between the client (i.e. the user logging in) and the server that handles it. Sending it in plain text is generally okay, so long as it's always going via HTTPS, since that will encrypt the traffic and make it nonsense to any sniffing attack.
Furthermore, the information from the network tab is only visible to the client, who entered the password anyway (so they know it), so it should not be considered a security flaw with your app.
As the previous answer says, the browser’s developer tools only shows what the browser sees. Once it leaves your browser over HTTPS it is encrypted.
Encrypting passwords in the application before sending is no longer recommended because there will always be some other security compromise. For example, this was implemented in HTTP Digest authentication and the impact was having to store the password unhashed on the server.
Send your password as plaintext. Make sure your server only accepts HTTPS (permanent redirect) and store it in hashed form on the server.
Html click "submit" to send the form
Browser log the content of package
Browser encrypt the data if you are accessing the https site
Browser send the encrypted data
Server got your request
and you was seen the step 2 content

Modify POST request body in service worker

I am trying to add a parameter to the body of a POST request in a service worker but the original body is send. I use the following code
let token = '';
self.addEventListener('message', function (event) {
if (event.data && event.data.type === 'SET_TOKEN') {
token = event.data.token;
}
});
self.addEventListener('fetch', function (event) {
const destURL = new URL(event.request.url);
const headers = new Headers(event.request.headers);
if (token) headers.append('Authorization', token);
if (destURL.pathname === '/logout/') {
const promiseChain = event.request.json().then((originalBody) => {
return fetch(event.request.url, {
method: event.request.method,
headers,
// this body is not send to the server but only the original body
body: JSON.stringify(Object.assign(originalBody, { token })),
});
});
event.respondWith(promiseChain);
return;
}
const authReq = new Request(event.request, {
headers,
mode: 'cors',
});
event.respondWith(fetch(authReq));
});
Generally speaking, that should work. Here's a very similar live example that you can run and confirm:
https://glitch.com/edit/#!/materialistic-meadow-rowboat?path=sw.js%3A18%3A7
It will just POST to https://httpbin.org/#/Anything/post_anything, which will in turn echo back the request body and headers.
If your code isn't working, I would suggest using that basic sample as a starting point and slowing customizing it with your own logic. Additionally, it would be a good idea to confirm that your service worker is properly in control of the client page when its makes that request. Using Chrome DevTool's debugger interface, you should be able to put breakpoints in your service worker's fetch event handler and confirm that everything is running as expected.
Taking a step back, you should make sure that your web app isn't coded in such a way that it requires the service worker to be in control in order to go things like expire auth tokens. It's fine to have special logic in the service worker to account for auth, but make sure your code paths work similarly when the service worker doesn't intercept requests, as might be the case when a user force-reloads a web page by holding down the Shift key.

"redirect_uri_mismatch" when sending authentication code to GoogleAPI

I am having trouble with the authentication process for the GoogleAPI. In the end I want to be able to read the users steps using the GoogleFit API and then store that value in a database. Currently I'm using restdb.io and executing javascript in codehooks.
The documentation from Google that I am following can be found here, clicking on the HTTP/REST option in the code examples. At the moment I am at step 5: I have gotten the users authentication code and stored it in the database. Now I have to POST the code along with some other parameters and get the access and refresh tokens.
If the POST is successful (from what I understand) I should get back a 200-OK message that the request was valid. Google will then POST a JSON body with the access and refresh token to the redirect_uri that I have specified in my GoogleAPI credentials page and the initial request. At redirect_uri I have to handle the request and save the two values.
The problem is that I receive a redirect_uri_mismatch - Bad Request message from Google as a response when executing the request. I get it at the log.debug("ERROR HERE: " + [...]); in the code below:
async function mainFunction(){
const authCode = THIS_IS_MY_AUTHENTICATION_CODE;
try {
var answer = await postRequestToGoogle(authCode);
//do stuff with response from Google
} catch (error) {
//do stuff
}
}
async function postRequestToGoogle(authCode){
//body for the request
const params = "code=" + authCode + "&" +
"client_id=THIS_IS_MY_CLIENT_ID" + "&" +
"client_secret=THIS_IS_MY_CLIENT_SECRET" + "&" +
"redirect_uri=THIS_IS_MY_REDIRECT_URI" + "&" +
"grant_type=authorization_code";
try{
const result = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: params})
.then(res => {
log.debug("ERROR HERE: " + JSON.stringify(res.json()));
return res.json();
})
//return JSON back main function
return result;
}catch(error){
//do stuff
}
}
I looked up the error message and tried some things:
Copy and pasted multiple different Authorized redirect URI from the GoogleAPI credentials page into the code to make sure that there is no problem with
http/https
www/no www
trailing slashes
typos or capitalization
Waited for changes to be processed by Google (read that it can more than 30min)
Changed all the other parameters to see if the redirect_uri is actually the problem
If code is changed the message is invalid_grant - Bad Request
If client_id is changed the message is invalid_client - The OAuth client was not found
If client_secret is changed the message is invalid_client - Unauthorized
If the grant_type is changed the message is unsupported_grant_type - Invalid grant_type
That's why I think the issue is the redirect_uri, but it's unclear to me how since I copy&pasted it. Something that came to mind was that maybe the value of redirect_uri gets changed when it's read by Google? Or maybe when the request is being put together? Do some characters have to be replaced?
I tried to analyze the request with Wireshark but didn't think about the fact that it's HTTPS so I would have I would have to decrypt it.. Is that something I should look into?
Thank you for taking the time to read all of this! If you have any advice please let me know :)
Update 16.11.20:
I have created a new OAuth 2.0 Client ID and used the new id/secret in my request. The resulting message the same as before. I will wait and try again tomorrow to see if maybe Google needs some more time. Then I'll try to delete all current IDs and start with a fresh GoogleAPI project.
Update 19.11.20:
Creating a new OAuth 2.0 Client ID did not resolve my problem, neither did creating a whole new GoogleAPI project and adding those credentials into the request. I am in contact with the developers of restdb.io and have asked them to add the Google Auth Library: Node.js Client to the list of supported Node.js packages. Hopefully that will help, I will give it a try as soon as it can be used :)
Update 02.12.20:
No progress so far, but I'm optimistic that the developers will add the package soon. I will post a final update as soon as I am done with this project.

How to render to new HTML page on success of API fetch request in NodeJs?

I want to render a new HTML page on user request, which is only accessible if the authorization header is set correctly. I initially thought that the browser would redirect to the page, but found out that this is not the case. I have found some ways to handle this, for example replacing the DOM, but I don't think that is a good solution.
Here is the fetch call from UI, which returns the HTML, but does not render it currently:
fetch('/protected_route', {
headers: {
'authorization': 'Bearer ' + sessionStorage.getItem('token')
}
}).then(response => {
// What to do with the response
});
Here is the server code, if that helps:
app.get('/protected_route', (req, res) => {
const bearer = req.headers['authorization'];
if(typeof bearer === 'undefined') {
res.json({message: 'Not logged in'});
}
else {
const token = bearer.split(' ')[1];
jwt.verify(token, config.secret, (error, data) => {
if(error) {
res.json({message: 'Error verifying token'});
}
else {
res.render('protected_route');
}
});
}
});
The problem you are facing is when you tried to open a new HTML page and send back an html file via res.render(), this will send HTML content back to request. When using API call via AJAX or fetch or request or any other API client they are developed for single page application and these calls prohibits browser from rendering to new html page. API calls from such sources process over data and browser have no control over response received.
If you need to render another HTML page than use form-submit to call API, as this is the only way that let browser act upon response, and display response in new page. Since res.render() returned HTML file content, thus a new page act like a file is opened.
If you want to use single page application then you had to process over HTML received in response and then replace whole loaded HTML with new one, you had to make changes in DOM if need to use some API call module.
You can check this github project explaining all basic front-end and backend links for starters.
The only way to solve it I've found is using document.write(data). This statement result in rendering the received page but inside the same document, not changing the browser window to the new page and not including a new enter in the history of the browser

Consume a webservice with basic authentication using Soup

As part of a gnome-shell extension, I try to consume a webservice using xmlrpc. The webservice expects a basic authentication header. Using Soup, I got the following code (basically a blueprint from the great openweather extension):
function load_json_async() {
if (_httpSession === undefined) {
_httpSession = new Soup.Session();
} else {
// abort previous requests.
_httpSession.abort();
}
let message = Soup.xmlrpc_message_new (
"https://api.sipgate.net/RPC2",
"samurai.BalanceGet",
new GLib.Variant('()',1.0)
)
_httpSession.connect('authenticate',
Lang.bind(
this,function(session,message, auth,retryFlag){
auth.authenticate("xxxxx","xxxxx");
}
)
)
_httpSession.queue_message(
message,
Lang.bind(this,
function(_httpSession, message) {
try {
if (!message.response_body.data) {
log("hello1 "+message.response_body.status)
return;
} else {
log("got message-status:"+message.status_code)
}
log(message.response_body.data)
} catch (e) {
log("exception:"+e)
return;
}
return;
}));
return;
}
I am using Soup for building up the connection. The authenticate signal is executed before the queue-callback is executed.
Still, in the beginning within the callback, the response_body holded the status code 401 instead of the expected authorization. The given credentials where incorrect.After correcting this, the call went through. However, you always need two calls to the provider this way: first to get the information it uses BasicAuth, and the second to actually do the call.
Is there a way to give the authentication information directly with the first call?
It's possible to add the authorization directly into the request header
let auth = new Soup.AuthBasic()
auth.authenticate("xxx","xxx");
message.request_headers.append("Authorization",auth.get_authorization(message))
This prevents a first request without auth header and also allows use of REST services that don't return the correct status code on unauthorized requests and forward to a login page instead.

Categories

Resources