NodeJs - res.redirect changes the content displayed but not the URL - node.js

i have a react native frontend and a nodejs backend. In one of my API calls i am getting a redirectHTML from a gateway to display to the users. The redirectHTML obtained is used in react native Webview to get displayed. my problem is the only way to know that the transaction is success or not is from the url. I have tried res.redirect and res.writeHead and both change the content of the screen but the URL still remains the same.
Server side
router.get(
"/pay/authenticate/result",
async(req, res) => {
console.log(req.query)
// res.redirect(302,"/");
res.writeHead(302,{'Location':'https://www.google.com/'});
res.end("");
});
client Side
<WebView
style={{flex:1}}
source={{html:`${authenticateWebViewUrl}`}} //this is the redirectHTML that came from the response
onNavigationStateChange={(navState) => {
console.log(navState)
}}
scalesPageToFit={false}
javaScriptEnabled={true}
/>

res.redirect with 302 will add the temporary redirection, if you want a permanent redirection, prefer HTTP 301. Reference
res.writeHead + res.end does not, on its own, cause the redirection to a new URL.
Edit: Typo req and res.

Related

Redirecting a Node.js/Express request to another server

I got a situation where I need to redirect an HTTP request made to server X to another server Y,
with all of the requests' headers, params, body etc.
I tried:
app.post('/redirect-source', (req, res, next) => {
res.redirect(301, 'http://localhost:4000/redirect-target');
});
But the response I get when reaching this route is:
{"message":"Not Found"}
Although the server and route i'm redirecting to are live and I get an ok response when reaching it directly.
What am I missing?
edit:
I noticed that the target route works on Postman and not from the browser, my guess is because it's a POST request.
How can I configure the redirect to pass as POST/specific type?
Try to change the statusCode to 308.
Take a look at this https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301
Use the 301 code only as a response for GET or HEAD methods and use the 308 Permanent Redirect for POST methods instead, as the method change is explicitly prohibited with this status.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308
It turned out that status code 307 is the one that works for POST requests too. Like so:
app.post('/redirect-source', (req, res, next) => {
res.redirect(307, 'http://localhost:4000/redirect-target');
});

React + Express With Passport.js

I am setting up a full-stack application using React and Express JS.
I'm using Passport.js for authentication and have come across a slight problem...
So my front-end and back-end are two separate packages running on two different ports. On my express app, I have created a route like the following.
app.post('/api/account/login', (req, res, next) => {
passport.authenticate('local', {
successRedirect: '/dashboard',
failureRedirect: '/users/login',
}) (req, res, next);
});
This is pretty standard as far as Passport.js goes. Basically, if it authenticates the credentials I have provided, then it should redirect me to /dashboard. If not, then to the other mentioned route.
I am able to call reach this endpoint from my react application and get the correct response like the following in the network tab of chrome.
Request URL: http://localhost:3000/dashboard
Request Method: GET
Status Code: 304 Not Modified
Remote Address: 127.0.0.1:3000
Referrer Policy: no-referrer-when-downgrade
However, it doesn't actually redirect me to /dashboard. Is it not possible to do it this way?
Thanks.
It sounds like your React app is calling the route via ajax using something like fetch.
The way you're using Passport assumes that a browser is issuing the requests directly. On a successful login, Passport returns a Redirect response (HTTP 302 or similar), which the browser honors and redirects the user to.
Ajax requests don't work this way, since there isn't any navigation happening.
You'll need to handle this yourself on the React side of things. Your Express app will need to handle the session authentication by (for instance) returning a JSON message with a token or storing a session cookie. You'll need to update your React app to recognize this and then navigate to the correct route via client-side Javascript.
If you're using react-router, they have some sample code that might be helpful.

how to use passport SAML with both node (express) as backend and vue as client

I have an app with node (express) backend, and vue client.
I'm trying to add SAML SSO using passport. (makes sense to do it on the server node app).
it works perfect when used in express app. but when I applied it to a structure of express backend and vue client - it fails to make the redirection to the Idp.
when user enters my login page, vue client (Login.vue) calls node backend for verifying the user. (api verifyuser)
node call passport.authenticate('saml', ...) and I expected a response I can send back to the vue function that called me, and there, in Login.vue - to make the redirection.
but here comes the problem:
in the backend node app, the redirect response is sent after my code is executed, inside passport strategy. So it is sent automatically to the browser, not returning to the vue script that called this node api.
So the redirection is done in the background, the user don't see any redirect. the original login page is still shown.
And my vue function gets the response back from the API - only after the browser sends the redirect (in the background) to the IDP, and gets the login html page response from the IDP.
So the data I get back - is an html of the IDP login page, instead of a redirection data.
How can I solve it?
I'm new to client technologies and js and node including, so I really don't know how such a flow should be handled. searching 3 days for solution.
Thanks a lot for you assistance!
here is my snippets of code:
Login.vue:
<input class="button wide cropBottom io-mango ae-5 margin-top-0 toRight" v-on:click="userLogin" type="button" value="Log In"/>
...
userLogin: function() {
...
...
$(".overlay").show();
this.$http.post(process.env.BASE_URL + "verifyuser", oUser) //call backend node express app
.then(function(data) {
...
here I gets only an html login page which the IDP sent as a response to the redirect with the SAML Request.
}
Backend node express app:
verifyuser.js:
module.exports = function (app) {
app.post('/verifyuser', (req, res, next) => {
var SamlStrategy = passportSaml.Strategy;
passport.use(new SamlStrategy(
{ ...
});
passport.authenticate('saml', {session: false}, function (err, user, info) {
...
})(req,res,next);
//tried to get the redirect here, but res still don't have it. only after this function is called, the SAML request is created from the req, and the location url is made.
});
I've found a solution.
I changed the Vue client:
instead of calling the server using ajax, and expecting a data response to come back,
I called the server using post of a form.
that way, the browser redirects to the server when I call it, and when the passport library in the server returns a redirect response- it is done in the forground, and the user can see it.
In Single logout, passport have done a better job:
the passport API just returns the logout request created.
then I can decide myself if I want redirect from the server, or I want to send the redirection request to the waiting client function - and do the redirection from there.

Browsersync middleware: Redirect from a post request

We are using browsersync in development. For a payment confirmation, the payment provider posts back to a predefined url.
The problem is, that Browsersync only displays Cannot POST /payment/success.
So I was thinking about writing a middleware that takes the POST request and redirects to the same URL as a GET request. The problem is, I'm not sure if this is possible with browser sync. When I try to call res.redirect('/') it throws an error:
browserSync.init
server:
baseDir: "...."
index: "/statics/master.html"
middleware: [
modRewrite(['!\\.\\w+$ /statics/master.html [L]']),
(req, res, next) ->
res.redirect('/') # this throws a TypeError: res.redirect is not a function
]
Why is res.redirect not defined? I thought browser sync is based on express?
How could I access the body of the post request? req.body returns undefined
browserSync uses Connect for the middleware and doesn't share the same api as Express it seems.
For a redirect you can at least do the following:
res.writeHead(301, {Location: url});
res.end();
Have a look at their documentation for accessing the request body. The section in Middleware regarding body parsing may yield some results for you.

ExpressJS : res.redirect() not working as expected?

I've been struggling for 2 days on this one, googled and stackoverflowed all I could, but I can't work it out.
I'm building a simple node app (+Express + Mongoose) with a login page that redirects to the home page. Here's my server JS code :
app
.get('/', (req, res) => {
console.log("Here we are : root");
return res.sendfile(__dirname + '/index.html');
})
.get('/login', (req, res) => {
console.log("Here we are : '/login'");
return res.sendfile(__dirname + '/login.html');
})
.post('/credentials', (req, res) => {
console.log("Here we are : '/credentials'");
// Some Mongoose / DB validations
return res.redirect('/');
});
The login page makes a POST request to /credentials, where posted data is verified. This works. I can see "Here we are : '/credentials'" in the Node console.
Then comes the issue : the res.redirect doesn't work properly. I know that it does reach the '/' route, because :
I can see "Here we are : root" in the Node console
The index.html page is being sent back to the browser as a reponse, but not displayed in the window.
Chrome inspector shows the POST request response, I CAN see the HTML code being sent to the browser in the inspector, but the URL remains /login and the login page is still being displayed on screen.
(Edit) The redirection is in Mongoose's callback function, it's not synchronous (as NodeJS should be). I have just removed Mongoose validation stuff for clarity.
I have tried adding res.end(), doesn't work
I have tried
req.method = 'get';
res.redirect('/');
and
res.writeHead(302, {location: '/'});
res.end();
Doesn't work
What am I doing wrong? How can I actually leave the '/login' page, redirect the browser to '/' and display the HTML code that it received?
Thanks a million for your help in advance :)
The problem might not lie with the backend, but with the frontend. If you are using AJAX to send the POST request, it is specifically designed to not change your url.
Use window.location.href after AJAX's request has completed (in the .done()) to update the URL with the desired path, or use JQuery: $('body').replaceWith(data) when you receive the HTML back from the request.
If you are using an asynchronous request to backend and then redirecting in backend, it will redirect in backend (i.e. it will create a new get request to that URL), but won't change the URL in front end.
To make it work you need to:
use window.location.href = "/url"
change your async request (in front end) to simple anchor tag (<a></a>)
It's almost certain that you are making an async call to check Mongoose but you haven't structured the code so that the redirect only happens after the async call returns a result.
In javascript, the POST would look like something this:
function validateCredentials(user, callback){
// takes whatever you need to validate the visitor as `user`
// uses the `callback` when the results return from Mongoose
}
app.post('/credentials', function(req, res){
console.log("Here was are: '/credentials'";
validateCredentials(userdata, function(err, data){
if (err) {
// handle error and redirect to credentials,
// display an error page, or whatever you want to do here...
}
// if no error, redirect
res.redirect('/');
};
};
You can also see questions like Async call in node.js vs. mongoose for parallel/related problems...
I've been working on implementing nodemailer into my NextJS app with Express. Was having this issue and came across this. I had event.preventDefault() in my function that was firing the form to submit and that was preventing the redirect as well, I took it off and it was redirecting accordingly.
Add the following in your get / route :
res.setHeader("Content-Type", "text/html")
Your browser will render file instead of downloading it

Resources