How do you use an Oauth callback between React and Express? - node.js

I'm trying to integrate MailChimp's API into my React App which will allow users to authorize their MailChimp accounts for use in my app. I haven't found a tutorial on it, so I'm following this tutorial which uses only express: https://www.codementor.io/mattgoldspink/integrate-mailchimp-with-nodejs-app-du10854xp
I've gone through Mailchimp to set up my app/my client secret/client id, etc:
Redirect URI
```http://127.0.0.1:3001/mailchimp/auth/callback````
I kept the same express code as the tutorial, except I put my client secret in a .env file:
server.js
const querystring = require('querystring');
const mailchimpClientId = `${process.env.MC_CLIENT}`
app.get('/mailchimp/auth/authorize', function (req, res) {
res.redirect('https://login.mailchimp.com/oauth2/authorize?' +
querystring.stringify({
'response_type': 'code',
'client_id': mailchimpClientId,
'redirect_uri': 'http://127.0.0.1:3000/mailchimp/auth/callback'
}));
});
However, in the tutorial, the callback function is in an HTML file written like this:
<!doctype html>
<html>
<head>
<title>Integrate MailChimp</title>
</head>
<body>
<a class="btn btn-primary" href="/mailchimp/auth/authorize">Connect with MailChimp</a>
</body>
</html>
I've added this (using JSX syntax):
MailChimp.jsx
class MailChimp extends Component {
render() {
return (
<div>
<h1>MailChimp Auth</h1>
<a href={'http://127.0.0.1:3000/mailchimp/auth/authorize'}>Mailchimp</a>
</div >
)
}
}
export default withRouter(MailChimp);
On clicking that link inside my React App's route localhost:3001/mailchimp, I'm sent to mailchimp where I sucessfully login with my account (not the one requesting permission) and I am returned to the react app.
However, I'm getting the following error:
GET /mailchimp/auth/callback 404 2.818 ms - 162
Failed to load resource: the server responded with a status of 404 (Not Found)
I've scoured the web trying to find a working example of using React & Express for MailChimp Oauth for app authorization but I haven't found one. My question is, did I set something up wrong in the redirect, or is there a better recommendation for handling this request in React?

The 404 error is saying that you don't have a route that maps to /mailchimp/auth/callback. Looks like from your code you haven't written that.
Unless you haven't provided the code for it, you need the route handler mentioned with the code in the tutorial:
app.get('/mailchimp/auth/callback', function(req, res) {
request.post('https://login.mailchimp.com/oauth2/token')
.send(querystring.stringify({
...
}

Related

Preventing click jacking on MERN App using X-Frame-Options or helmet

I am trying to create web application using React and Node that is secure from Click jacking, I have understood that i need to either set the X-Frame-Options to deny manually or use helmet to prevent other sites loading my web app using iframes. I have set the below config in my index.js of server
const helmet = require("helmet")
app.use(helmet())
When i try to call a get Login API from inside an iframe of a decoy website(basic html page), i am getting a status 200 response and the web app is loaded into the iframe. But i believe the web app should fail to load in the iframe of decoy site. My Login component looks something like this.
function Login(){
useEffect(()=>{
Axios.get(`http://localhost:8888/isLoggedIn`, {
headers:{
"x-access-token": localStorage.getItem("token")
}
}).then((response)=>{
console.log(response)
if(response.data.loggedin){
navigate("/")
}
})
}
As mentioned in the problem, I am getting 200 for isLoggedIn call.
When i checked the api call from the decoy site network tab, I see the isLoggedIn call has origin set to "http://localhost:3000", which is actual domain of react app.
Below is the decoy site html code.
<body>
<div id="decoy_website">
Decoy Web Site for Secure Login Page
</div>
<iframe id="target_website" src="http://localhost:3000/login">
</iframe>
</body>
Please let me know if i am not using the helmet the correct way, or the additional steps that need to be done to prevent my site from click jacking.

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.

React router 4 - Link to page outside react app

I'm building a node + react app that uses passport's facebook authentication. Getting this authentication to work involves hitting an express route '/auth/facebook'. Unfortunately as soon as the react app loads up react router 4 doesn't allow links to directly hit the express server and instead searches for a react route matching 'auth/facebook'. In short how do I link to a route within my application but outside of the react app when using react router 4?
React Router is only for client side routing. Use fetch API or a similar library for that.
I'll state one way of solving your problem (using fetch and without react router).
Remove the href from the <a> tag
Add an event listener for the click event, <a onClick={makeCall}>
Then in the makeCall function, you can call the backend using the fetch API(or axios or whatever),
makeCall() {
fetch('/auth/facebook', options)
.then((res) => {
// Something
})
.catch((err) => {
// handle error
});
}

How to properly structure vue / node app when using instagram api

So I have an application that requires the use of connecting to the instagram api, fetching user data, and displaying that data.
My application needs to work like this: use is on a page where he/she clicks a "connect" button. They are then connected to the instagram api, and REMAIN on the same page. The page is then populated with their current instagram information.
Currently, I have a project which is structured like this:
myapp
--client // All Vue.js files in here
--server // All server endpoints here. ALSO WHERE INSTAGRAM CONNECTION HAPPENS
I ran into a little hiccup while following this which is just a tutorial on how to settup user auth with node/ instagram api.
In my server project, I have a router.js:
const AuthenticationController = require('./controllers/AuthenticationController')
const AuthenticationControllerPolicy = require('./policies/AuthenticationControllerPolicy')
const config = require('./config/config.js')
module.exports = (app) => {
app.post('/register', AuthenticationControllerPolicy.register, AuthenticationController.register)
app.post('/login', AuthenticationController.login)
//BELOW IS TAKEN FROM TUTORIAL MENTIONED ABOVE.
app.get('/auth/instagram', function (request, response) {
console.log(`REDIRECT URI ${config.instagram.authURL}`)
response.redirect(config.instagram.authURL)
})
app.get('/auth/confirmed', function (request, response) {
console.log(`ARE WE IN HERE????? ${request.query.code}`)
response.send(request.query.code)
})
}
If I go to my server port: http://localhost:8081/auth/instagram I can see that connecting to instagram works, and Im redirected to the confirmation page, and the code is displayed as expected.
MY ISSUE/QUESTION is now... how do I connected that back to my client application? Now that my server has that data, how do I send that data back to my client app?
My component is simple right now:
<template>
<v-container>
<v-layout>
<v-flex xs6>
<panel title="Connect To Instagram">
<form
name="ig-connect-form"
<v-btn
dark
class="red lighten-1"
#click="connect">
Connect
</v-btn>
</form>
</panel>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
import AuthenticationService from '#/services/AuthenticationService'
export default {
data () {
return {
igusername: '',
password: '',
error: null
}
},
methods: {
async connect () {
try {
// This triggers the connection on my server.... how do I get data back from different server route now??
console.log(response)
} catch (error) {
this.error = error.response.data.error
}
}
}
}
</script>
<style scoped>
</style>
Do I require setting up vue server side rendering? Is there a simpler way of just having my client app watch for data at the auth/confirmation port on my server app? Hope this is clear. im trying to keep as concise as possible.
You don't send the data from the server to the client, but instead make a request from the client to the server which then answers with the data. However, a couple of more steps is required.
Something like this:
Client clicks connect
Client is redirected to the auth page
Client authenticates with Instagram
Client is redirected back to your site with code=xxxxxxxx in the URL
Server uses that code to get an access token from the Instagram API
Server saves the access token for this specific user
Server can now make queries to the Instagram API with that access token
Client can now ask the server for whatever data and display it on the page

Running a node.js file from a click event

I am new to node.js. I am connecting with an api with express/node.js with ejs templating. I wanted to push some information from the browser to the api. At the moment, I can push from the command line. I understand I cannot call a node.js file from the browser directly but I was wondering when I click submit on a form if it can call node.js file and run it...what should I use, modules, routes, ajax, or any other solutions you recommend? I appreciate the help.
Well, it's a strange question. Your Node application needs to either listen for HTTP requests or WebSocket connections (possibly using some abstraction like Socket.io that can provide fallbacks where WebSocket is not available) and handle the requests or messages sent on the socket. In those handlers you can do whatever you need. This is just a basic principle of client/server architecture.
Now, to choose what technology is best suited for your needs it all depends on how often you need to make that communication, whether or not you need realtime communication, how much data do you need to pass around, if you need to support serving data initiated by the server or only by the client etc.
To answer you question - yes, you can submit a form and make it execute some code in your Node application. This is exactly how forms work - they make a connection, usually GET with data in the query string or POST with data in the body (both can easily be read by Node) and then your handler on the backend handles the request, does whatever it needs and sends a response.
Consider this simple example using express and body-parser:
const app = require('express')();
const { urlencoded } = require('body-parser');
app.use(urlencoded({ extended: true }));
app.use('/', (req, res) => {
const { method, path } = req;
const { x } = req.body;
console.log(`Client request: ${method} ${path} (x = ${x})`);
res.end(`
<!doctype html>
<html>
<head>
<title>Form handling example</title>
</head>
<body>
<p>x = ${x}</p>
<form method="POST" action="/">
Enter x: <input type="text" name="x">
<input type="submit" value="Submit">
</form>
</body>
</html>
`);
});
app.listen(4447, () => console.log('Listening on http://localhost:4447/'));
Create a new directory, save this code as server.js, run:
npm init -y
npm install express body-parser -S
node server.js
and access the printed URL in the browser.
When you click the submit button you'll see what will happen in the Node app.
Your node app will have a route set up which accepts GET or POST requests. Then it does some logic and returns some data in a response.
Your web page will send the form to your node route as GET or POST via an AJAX call, and likewise it will receive a response from the AJAX call. Then you can take this response and use it locally in the webpage however you like.

Resources