How to add facebook authentication to my react app? - node.js

I'm using express as backend. I implemented facebook authentication at the backend.
router.get('/login/facebook',
passport.authenticate('facebook',{scope:['email']}));
router.get('/login/facebook/callback',
passport.authenticate('facebook',{
successRedirect : '/home',
failureRedirect:'/'
})
);
Now I want to call this through my react app, so that when the user lands up at home page, first he should be authenticated by facebook then only he can see homepage. How can I do this ?
I tried using react-router, but I can't understand how to call backend using react-router.
I also fetched /login/facebook using fetch command :
componentDidMount(){
fetch("127.0.0.1:3001/login/facebook");
But it gave me CORS error.
My react app is at 127.0.0.1:3000 and express server at 127.0.0.1:3001.

If this issue is only in dev mode, then Daniel's answer is correct.
In any case, I recommend to avoid calling the :3001 api directly from the :3000 app. Here's what I would do.
I will edit the fetch call as follows,
componentDidMount(){
fetch("/login/facebook");
}
This call will be received by the backend which serves the react application.
Now there are three cases,
Case 1: Your file serving app will have a proxy method which can forward requests to an API. For example read it here
Case 2 This is my Recommended approach. I would simply write the authentication logic in the :3000 server and only use the :3001 API for handling business logic of the app.
Case 3: If you have a backend app (:3000), say written using expressJs, you can forward the request to the :3001 API. Here is a sample code for that,
client.send({
method: req.method,
path: req.url,
data: req.body,
params: req.params
}).then( (response) => {
// Something
}).catch( (err) => {
// Handle error
});
Here the client is a module which uses the request module to make HTTP calls.
You can implement the above call as an express middleware to use it for all HTTP calls.

There are several options:
If you are using webpack dev server, you can set up a proxy to your api. See here
You can temporary disable cors validation during development. See here

Related

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 call microservice (built in express js) from angular app which is already calling sails js backend?

I already have an application with angular front end and sails js backend. But due to some reason I moved some backend functions in seperate express project like micro-service. But Now I want to call those functions. Kindly tell me how to do this.
There are two ways to do this:
CORS
Your microservice should enable cors so that the browser doesn't block its responses. In Express this can be as simple as:
const cors = require('cors');
app.use(cors());
Depending on weather the endpoint requires credentials (login) you may have to set a few options for cors:
app.use(cors({
credentials: true,
origin: true,
methods: 'POST,GET,PUT,OPTIONS,DELETE'
}));
see my answer to this other question: Node.js with Angular - CORS error when i send request
Check out the documentation of the cors middleware for configuration options: https://www.npmjs.com/package/cors
Application Gateway
You can also run all your microservices behind a reverse proxy such as Nginx or Apache2 or HAproxy. This strategy is what makes Amazon work before 2014 (before browsers implemented CORS).
Here's a simple example of an Nginx configuration for proxying your microservice:
# The ^~ operator matches all urls that begins with the following string:
location ^~ /some/microservice {
proxy_pass http://some.microservice.myapp.com;
}
A reverse proxy that maps multiple microservices to url paths on a single domain name is often called an Application Gateway in the industry. Application Gateways also provide additional benefits such as load-balancing, firewalling the internal servers form the internet etc. But for our purposes the main reason to use it is to allow microservices to be served form the same domain as your front-end code which means making requests to microservices does not trigger cross-origin policy.
Either way, to request data from your microservices you'd just make an ajax request. Either via old-school XMLHttpRequest or the more modern fetch API:
// Assuming you've configured CORS correctly on your server:
fetch('https://some.microservice.myapp.com/getdata')
.then(function(response) {
return response.json();
})
.then(function(result) {
console.log(result);
});
// Assuming you are using an application gateway:
fetch('https://myapp.com/some/microservice/getdata')
.then(function(response) {
return response.json();
})
.then(function(result) {
console.log(result);
});

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.

Node.js / Express.js + Angular router - server overwriting client view with response object when using direct link

I am building a node.js app with express, I am hosting an Angular SPA in the public folder.
The app runs and the hosting works fine when I use the angular router for navigation around the website, but when I directly try to access the link, for example: http://192.168.1.4:3000/posts, the entire body of the website is just the JSON response object, without the app
this is the Node.js code handling the get request
postRouter.route('/')
.options(cors.corsWithOptions, (req, res) => {
res.sendStatus(200);
})
.get(cors.cors, (req, res, next) => {
posts.find({})
.then((post) => {
res.status(200);
res.setHeader('Content-Type', 'application/json')
res.send(post);
}, (err) => next(err))
.catch((err) => next(err));
})
this is my angular service sending out the get request
getPosts(): Observable<Post[]> {
return this.http.get(baseURL + 'posts')
.catch(error => { return this.processHttpService.handleError(error); });
}
Post Component .ts file
ngOnInit() {
this.postService.getPosts()
.subscribe(posts => { this.posts = posts, console.log(this.posts); },
errmess => this.errMess = <any>errmess);
}
Again, when i use my Angular 5 client app hosted in the public folder, built with ng build --prod, the JSON object is retrieved from the mongodb database and is displayed correctly on my website, along with the rest of the app, the header, the body, and the footer.
it might also be worth noting that the console.log on the ngOnInit() is not displayed on the browser when using the direct link.
Any advice/fix is greatly appreciated
You have a clash of routes between angular and your express application. Angular is served up on one route (I'm guessing the / route) and then it sort of "hijacks" the users navigation. It does this by not actually changing web pages, instead it just changes the URL in the navigation bar, but never actually makes a web request to get to that resource.
You've then got endpoints on a web server listening on those endpoints. This means the moment you visit the /posts page, you're not asking angular to do anything. In fact, angular isn't even loaded because that only gets loaded on the / route. Instead you're going straight to your API.
There are ways around this, to start with many people put their API fairly separately, either on a subdomain or mounted on /api (such as /api/posts). Then your angular app can be served up on the / route. There are other techniques you can use to then allow a user to go to /posts and still get your angular app loaded.
You can use a few approaches for this such as the hash location strategy, or you can serve up your angular application from any route on the application (* in express) and load the angular app which will then take over. This second approach is most comment, it usually results in hosting your api on a sub domain and then serving your angular app on the * route of the normal domain name. For example: api.myapp.com will serve only JSON responses, but any route on myapp.com will serve the angular app, such as myapp.com/posts.

How can I redirect a page from an external Node/Express server

I have a React/Node/Express web app up and running with two separate servers. React is running on localhost:8080 and proxying API requests to my node server running on localhost:3000. This will be my setup for production, so if possible, I would like to leave this structure intact.
The problem I'm running into is part of my API needs to redirect the user to a page to grab a token from the Spotify API, and then redirect back to my correct page upon successful authentication.
The API call proxy is being done like this:
const axios = require('axios');
const axiosInstance = axios.create({
baseURL: process.env.NODE === 'production' ? '' : 'http://localhost:3000'
});
module.exports = axiosInstance;
Up to this point, this works great, as I can keep my React and Node server completely separate.
As I mentioned above, I need to redirect my React front-end through an API call on my back-end, to a different page (Spotify authentication) to authenticate users, which then Spotify redirects back to my site after a user allows or disallows access to their Spotify accounts.
On my front-end - localhost:8080, the user clicks a button which calls this function.
authenticate = () => {
axios.get('/login')
.then(data => console.log(data));
}
Which calls this endpoint on localhost:3000.
router.get('/', (req, res) => {
let scopes = 'user-read-private user-read-email';
let client = process.env.SPOTIFY_CLIENT;
let redirect = 'http://localhost:3000/login/redirect';
res.redirect(`https://accounts.spotify.com/authorize?response_type=code'
&client_id=${client}${scopes ? `&scope=${encodeURIComponent(scopes)}` : ''}
&redirect_uri=${encodeURIComponent(redirect)}&state=${state}`);
});
When I click the login button on my front-end, I get this response, which I believe to be from Spotify as I can successfully make cross-origin requests to other endpoints in my API.
Failed to load... No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
However, when I manually navigate to localhost:3000/login, the redirect works and I'm sent to the Spotify authentication page.
In addition, I tested trying to redirect my front-end to any url, and it doesn't work.
So my question is, how can I redirect my front-end through an API call from a server that doesn't also serve my static front-end content?
The reason this isn't working is because your axios.get('/login') is an AJAX request to your server which your server is then telling the AJAX request to be redirected to Spotify, but Spotify doesn't have your front-end server registered so you get the CORS error, since your request was initiated while on the front-end server URL.
Your AJAX request is following that redirect that was given, not trying to load your browser on http://localhost:3000, which would then redirect it to the Spotify authorization page.
Now, rather than doing:
authenticate = () => {
axios.get('/login')
.then(data => console.log(data));
}
You could do:
authenticate = () => {
window.location.href = "http://localhost:3000"
}
This would direct your front-end application to visit the URL of your API server and follow the redirect immediately to Spotify and then you could authorize and be redirected back to your API server, which could then redirect back to your front-end application.
Truthfully, this is a bit overkill, since presumably your React application and node application look like they would be running on the same domain outside of development, given the relative URL's you're using. If you're using create-react-app, you should follow their instructions on configuring your proxy via this or manually via this.
tl;dr you need to tinker with how your proxy is configured or change from doing an AJAX request there to actually going directly to /login (not doing an AJAX request).

Resources