How to make Express return a new html with axios post - node.js

I have an express server. I have two routes as get methods.
app.get('/main',(req, res) => {
res.sendFile(`main.html`, {root: staticPath});
});
app.get('/signin', (req, res) => {
res.sendFile('signin.html', {root: staticPath});
});
I want to build my app as a single page react application. But before I let the user see this single page, I want to show a sign in, sign up screen. So when user clicks the sign in or sign up buttons, I want to send signin.html as response from the express server.
Here is my code on the browser from a react class
SignIn(){
axios.get('signin');
}
I can console.log() from the express route and verify that the code gets executed within the 'signin' route, but the html view doesn't change on the browser even though I send back a html file. How do I make it happen?

I'm by no means an expert, but here are my two cents. Instead of setting up your front end to receive an HTML file from the server, a more efficient approach would be the following.
Build the signup and login pages on the front end.
Set up routing between these pages.
Send the login/signup details from client to server via /login or /signup routes that you set up in Express. These details would usually be in the req.body object (make sure to install the bodyparser package from NPM).
You could then use JWTs to authenticate users and maintain sessions.
If you're looking for server-side rendering with React, here is an article for your reading pleasure :) Sorry if I made no sense.

Related

External web service redirects the user and posts data to my callback URL. How do I render the posted data to the user in an Express/Next.js app?

Any help would be hugely appreciated! Been stuck on this for a few days.
I have an Express/Next.js app where:
I send the user to an external website
user makes a payment
external website redirects and posts data back to my callback URL.
So now I have the user on a mysite.com/payment-complete route but also want to display the data that was sent back.
I have an app.post endpoint to successfully grab the data:
app.post("/payment-complete", async (req, res) => {
const transactionID = req.body.trans_id;
});
How would I pass the data to the user who is already on that route? Or pass the data before the page is rendered?
The flow of data is third party > my server > user and I'm not sure how to make this work.
I'd be grateful for any help/direction with this.
If anyone comes across this - the problem was that in Express (and other languages) you can't redirect or render a view for an AJAX POST request but you can if the POST request is coming from a submitted html form. The web service was in fact POST-ing the data and redirecting my users with a form and so I could render a view.
Following code worked using Handlebars templating engine to send the render.
router.post("/payment-ok", async (req, res) => {
const transactionID = req.body.trans_id;
return res.render("rendertest", { id: transactionID });
});
Should also possibly work with app.render to send a Next.js view instead, but I wanted to render from a separate routes file.

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.

In an Express.js server, how can I send an HTML (with style and js) acquired from a HTTP request, as a response?

This is an Express.js server. I'm trying to authenticate my Instagram API.
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const axios = require('axios');
const ejs = require('ejs');
var app = express();
// bodyparser middleware setup
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
var instagramClientId = '123123';
app.get('/instagram', (req, res) => {
axios({
method: 'post',
url: `https://api.instagram.com/oauth/authorize/?client_id=${instagramClientId}&redirect_uri=REDIRECT-URI&response_type=code`,
}).then((response) => {
res.send(response.data);
console.log(response.data);
}).catch((e) => {
console.log(e);
});
});
// port set-up
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`app fired up on port ${port}`);
});
This is the error I got. Looks like the html file was sent just fine, but the css and js weren't executed. You can see the error messages were all about style and js not being excuted.
Is this because I need to enable some options in my res.send() code?
I'm going to answer two questions here, the first is what I think you're actually having problems with and the second is what would technically be the answer to this question.
What I think your actual problem is: A misunderstanding of OAuth2.0
Looking at your code, it looks like you're trying to get a user to authenticate with Instagram. This is great, OAuth2.0 is fantastic and a good way to authenticate at the moment, but you've got the wrong end of the stick with how to implement it. OAuth2.0 is about redirects, not proxying HTML to the user. In this case it looks like you're using axios to make a server side call to the instagram OAuth endpoint and then sending the user that HTML. What you should actually be doing is redirecting the user to the Instagram URL you've built.
A high level of the "dance" you go through is the following.
The user requests to login with instagram by pressing a button on your website.
You send the user to an instagram URL, that URL contains your applications token plus an "approved" redirect url. Once the user has logged in with Instagram, Instagram will redirect the user to your approved redirect url.
The users browser has now been redirected to a second endpoint on your server, this endpoint recieves a one-time token from Instagram. You take that token on your server side and use axios (or similar) to make a server side request to fetch some user information such as their profile. Once you have that data, you can then create a user in your the database if needed and issue a new session token to them. Along with the profile call on this, you'll also get a token given directly to you (different from the one the users browser gave you) which will allow you to make requests to the Instagram API for the privileges you requested from the user originally.
This means you have 2 endpoints on your service, the "hello, I'd like to log in with instagram, please redirect me to the instagram login page" and then "hello, instagram said I'm all good and gave me this token to prove it, you can now check with them directly" (this is the callback endpoint).
You can manage this whole process manually which is great for understanding OAuth, or you can use something like Passport.js to abstract this for you. This lets you inject your own logic in a few places and handles a lot of the back and forth dance for you. In this instance, I'd probably suggest handling it yourself to learn how it all works.
Ultimately, you are not sending the user any HTML via res.send or anything similar. Instead your first endpoint simply uses a res.redirect(instagramUrl). You also thus do not make any HTTP requests during this portion, you do that on the "callback" after they've entered their username and password with Instagram.
Technically the correct answer to this question: proxy the JS and CSS calls, but this is really bad for security!
You're sending some HTML from a 3rd party in this case. So you will need to also allow the user access to the HTML and CSS. Security wise, this is quite iffy and you should really consider not doing this as it's bad practice. All of the JS and CSS links in the page are most likely relative links, meaning they're asking you for some JS and CSS which you are not hosting. Your best bet is to find these exact paths (ie: /js/app.min.js) and to then proxy these requests. So you'll create a new endpoint on your service which will make a request to instagrams /js/app.min.js and then send that back down with res.send.
Again, please do not do this, pretending to be another service is a really bad idea. If you need something from instagram, use OAuth2.0 to authenticate the user and then make requests using their tokens and the official instagram API.

How to use <a> & href tag in React-Router

I need to use an in React Router in order to use Passport.js in my application for OAuthentication. Whenever I create an anchor tag and click it in React it just goes to a blank page in React Router and I don't even get the callback from my Node server on the backend with all the verification from Google/Facebook/Linkedin
What is a useful way to have an href tag in React-Router so that my backend can register it, go through its api flow with the callbacks, and then send it to the right place in React-Router?
The problem is this
The Problem of using axios.get() for OAuthentication with Passport.js
EDIT
In this post they have the same problem that I do and the solution was just a simple href tag, however I have react-router on my application and its possible they did not, also they have no example
Previous post about Authentication with Passport.js with React front
EDIT
I am moving this project from jQuery focused to React, React-Router and Redux so a majority of my server-side Node code didn't need updating but here is ther part of my routes that handles the OAuth with Passport. I just need my front to reach this, let it do its thing and then send back to me
router.get('/linkedin', passport.authenticate('linkedin'),
function(req, res){
console.log('nexted')
}
);
router.get('/linkedin/callback',
passport.authenticate('linkedin', { failureRedirect: '/index' }),
function(req, res) {
console.log('here')
res.redirect('/professionals');
});
I noticed in the comments that you mention having CORS problems, if I'm right I believe you are using the authorization code grant OAuth flow, I had the same issue with GitHub when I had my SPA and my backend in different servers. If this is the case serve the static files of the SPA through the backend server. If you do this you should put the code below as your last route in order for React Router to work. You can find a repo where I did the same here
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/client/index.html'));
});
Note: If you don't want to do this you can use the implicit grant OAuth flow, and make the authentication in the SPA.

Protect non-api (res.render) route with express-jwt token in Node.js

First of all, I have read all tutorials on protecting REST API routes with jwt (express-jwt & jsonwebtoken), and it works fine for that purpose.
This works fine:
app.use('/api', postApiRoute);
And this also works, somewhat, I mean.. it does verify the token when I use it to show a webpage with angular http request calls, but when you add expressJwt({secret: secret.secretToken}), you cannot just access localhost:3000/api/post anymore. The expressJwt({secret: secret.secretToken}) is the problem here.
app.use('/api', expressJwt({secret: secret.secretToken}));
app.use('/api', userApiRoute);
What I really need is to protect a non-json but html/text request route with jwt like eg.:
app.get('/admin*', expressJwt({secret: secret.secretToken}), function(req, res){
res.render('index', {
//user: req.session.user, <- not sure how to do the equivalent, to extract the user json-object from the express-jwt token?
js: js.renderTags(),
css: css.renderTags()
});
});
.. without having to make http requests in angular/js, but using express' render function.
I need to do this since my application has 2 primary server routed views, so 1 where admin scripts are loaded from, and 1 where the frontend (theme) assets gets loaded.
I cant however get jwt/tokens to work with server rendered views, only json api requests.
The error i'm getting is: "UnauthorizedError: No Authorization header was found"
Couldn't find any information about (server rendered views protected with jwt, only serverside api requests and client side angular/ajax http requests) this, so I hope my question is clear, and that I do not have to fall back to using sessions again.
Not sure if I understood correctly, but if you are talking about entry html routes (i.e., loaded directly by the browser and not by you angular app), then you simply have no way of instructing the browser as to how to set the authorization header (no without introducing some other redirect based auth flow).

Resources