MSAL with Express and React: Authentication for root URL - node.js

I have a React frontend together with an express backend.
The goal is to only allow access to the frontend when the user was successfully authenticated by the express backend which uses MSAL (Microsoft Authentication Library).
Originally, I implemented the authentication flow by follwing this official Microsoft guide. However, this guide is only about using pure express without a real frontend. So I had to combine the information of this guide with my React frontend.
Step 1 was to run them both at the same port (localhost:3000) by building the frontend into a "build" folder and telling express to use the static files in this folder. This worked fine.
But now I am stuck with following problem: I want the authentication to be done when visiting localhost:3000. But currently, this URL is accessed without authentication. After the app.use(), app.get() is not called. It works only when app.get() is called with a somehow extended URL such as /login. Then the user will be authenticated and then redirected to localhost:3000.
Please see the express code:
//server.js
const path = require('path');
const express = require("express");
const msal = require('#azure/msal-node');
const SERVER_PORT = process.env.PORT || 3000; // use static build
const REDIRECT_URI = "http://localhost:3000";
// msal config
const config = {
auth: {
clientId: "xyz",
authority: xyz",
clientSecret: "xyz"
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: msal.LogLevel.Verbose,
}
}
};
// Create msal application object
const pca = new msal.ConfidentialClientApplication(config);
// Create Express App and Routes
const app = express();
// production mode: Build frontend using npm run build --> creates build folder. Use "build"-folder to serve static files with express
// use build folder
app.use(express.static(path.join(__dirname, './build')));
app.get('/', (req, res) => { // "/": app.get() is not invoked. "/login": works fine
const authCodeUrlParameters = {
scopes: ["user.read"],
redirectUri: REDIRECT_URI,
};
// get url to sign user in and consent to scopes needed for application
pca.getAuthCodeUrl(authCodeUrlParameters).then((response) => {
res.redirect(response);
}).catch((error) => console.log(JSON.stringify(error)));
});
// currently not being invoked
app.get('/redirect', (req, res) => {
const tokenRequest = {
code: req.query.code,
scopes: ["user.read"],
redirectUri: REDIRECT_URI,
};
pca.acquireTokenByCode(tokenRequest).then((response) => {
console.log("\nResponse: \n:", response);
res.sendStatus(200);
}).catch((error) => {
console.log(error);
res.status(500).send(error);
});
});
app.listen(SERVER_PORT, () => console.log(`Msal Node Auth Code Sample app listening on port ${SERVER_PORT}!`))
Why is app.get() (= the authentication flow) not invoked when using "/"? "*" does not work, too. Is it even possible to achieve the goal to do the authentication on localhost:3000 instead of localhost:3000/login?
If it is not possible, how can I prevent the user from accessing the frontend by just typing localhost:3000?
I also searched StackOverflow for this, but with no success.This question for example does not really work out for me, since it uses extra private routes. But I would like to avoid extra routes. Because I find so many examples that do exactly this, I start wondering if it's the only possible way.
Help will be appreciated.

To allow access to the frontend when the user was successfully authenticated by the express backend you can use react-router-guards.
Learn more here: https://www.npmjs.com/package/react-router-guards

Related

problem with routes with node-oidc-provider

I'm just starting with oidc-provider and I can't get express to recognize routes once I include oidc-provider.
In the simple setup below, I get a "unrecognized route on '/'. The well known url for setup does work, and the auth endpoint looks like it does as well.
const express = require('express');
const Provider = require('oidc-provider').Provider;
const app = express();
const configuration = {
// ... see /docs for available configuration
clients: [{
client_id: 'foo',
client_secret: 'bar',
redirect_uris: ['http://192.168.128.128:3000/oidc/cb'],
// ... other client properties
}],
};
const oidc = new Provider('http://localhost:3000', configuration);
app.use('/oidc', oidc.callback());
app.get('/', function(req, res) {
res.send('hello world');
});
oidc.listen(3000, () => {
console.log('oidc-provider listening on port 3000, check http://localhost:3000/.well-known/openid-configuration');
});
I don't understand the whole "mount" notion though I suspect it has something to do with my route problem. Why is this happening? What is the solution?
This is because you are doing:
oidc.listen(...)
And by doing that, you are ignoring all the routes added to app.
What you should do instead is to make the express app to listen, instead of the oidc provider:
app.listen(...)
🙂

Auth0 Express Server Issue Protecting Routes in Separate File

I am trying to build a MERN stack Food Tracker application for a school project, I'm very new to the MERN stack, and I've come across a problem with making my server's Express routes private that I haven't been able to find a solution for through searching.
I've set up login functionality using Auth0. My routes and models are stored in separate files and my checkJwt middleware is stored in a separate file that I'm importing into my route files. I am also using cors. I've tested that everything is setup correctly for authentication by sending a request from my frontend to a route that was written directly into my server.js file, and I've also verified that my routes respond to my frontend when they are left public, but when I try to make my routes private, I get a 401 Unauthorized error.
checkJwt file:
const jwksRsa = require("jwks-rsa");
require("dotenv").config();
const audience = process.env.AUTH0_AUDIENCE;
const issuer = process.env.AUTH0_ISSUER;
module.exports = checkJwt = jwt({
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `${issuer}.well-known/jwks.json`,
}),
audience: audience,
issuer: issuer,
algorithms: ["RS256"],
});
Server.js Setup:
app.use(cors({ origin: appOrigin }));
app.options("*", cors());
const profile = require("./backend/routes/profile");
//Test Route
app.get("/api/messages", checkJwt, (req, res) => {
res.send({
msg: "Testing authentication.",
});
});
// External route
app.use("/routes/profile", profile);
profile.js - Route file:
//Import checkJwt
const checkJwt = require("../../config/checkjwt");
//Import model
const Profile = require("../models/Profile");
//Router
router.post("/update/:user_id", checkJwt, (req, res, next) => {
//Code to update user information in database//
});
And, just in case it might help, I'm using axios to make the request on the frontend. Here is that code:
const onSubmit = async () => {
try {
const token = await getAccessTokenSilently();
const response = await axios.post(
`${apiUrl}routes/profile/update/${sub}`,
{
data: qs.stringify({
username: username,
}),
headers: {
Authorization: `Bearer ${token}`,
},
}
);
setMessage(response.data);
} catch (error) {
setMessage(error.message);
}
};
I've tried rewriting the request, turning it into a Promise, but because the request works when the route is left public, and I'm able to make requests of private routes that are directly written into my server.js, I suspect the issue is somewhere in how I'm accessing those external routes. Any help is greatly appreciated.
*Edit: I realized this morning that there was another big difference between my test route and problem route that I hadn't considered. Test route is a GET route and problem route is POST. When I changed one of my GET routes in the profile.js route file to include the checkJwt, it worked fine. So the issue is that I can't POST. Looking into this issue now, but there isn't much out there on how to specifically setup POST routes or how they would be different than GET routes.
I looked at this tutorial from Auth0 website, which is using a Passport strategy: https://auth0.com/blog/create-a-simple-and-secure-node-express-app/#Accessing-Guarded-Routes
But I thought passport was the same as the checkJwt...just using different middleware to accomplish the same thing. Am I mistaken?
I originally followed this tutorial to set everything up: https://auth0.com/blog/complete-guide-to-react-user-authentication/
I also looked at this Auth0 Quickstart regarding permissions and scopes: https://auth0.com/docs/quickstart/backend/nodejs/01-authorization
It kind of made it sound like not adding scopes or permissions and setting it up like the private without scopes would allow full access to the routes, but now I'm back to wondering if I need to set up those permissions for post...but there isn't much documentation on how that works.
Can anyone point me in the direction of a better tutorial for securing and accessing my POST routes?

In what file do we connect react front end folder files with back-end folder files with server.js and mysql database connection?

I have front-end folder files with react components and front-end css libraries.
In different folder, I have back-end files with server.js routing with mysql connection.
When I enter inputs on the screen, the inputs are not saved to mysql database.
Questions:
In what file, do I connect my front-end with back-end?
What statement should I use to connect my front-end with back-end?
To start Front-end, I used: npm start and To start Back-end, I used: nodemon server.js.
Question: When I connect front-end and back-end, what file should I open so that the front-end talks with the back-end -> both are starting?
Question 1 answer
You can do this a number of ways. I've done this with Serverjs and react in the
following manner.
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const port = 80085;
const app = express();
const token = 'impossiblylongimportanttokenhere';
let nextId = 7;
You'll need to import CORS, a Parser, Express for routing purposes.
Assuming you'll have a login you'll want a authenticator function. Please note this is just for development.
function authenticator(req, res, next) {
const { authorization } = req.headers;
if (authorization === token) {
next();
} else {
res.status(403).json({ error: 'User must be logged in to do that.' });
}
}
You'll probably want to replace that with something more secure once you're don with development.
with app as our router express has several CORS methods to get us started likepostget
Here's an example of one if we had an array called friends and if we wanted to find that friend by ID.
app.get('/api/friends/:id', authenticator, (req, res) => {
const friend = friends.find(f => f.id == req.params.id);
if (friend) {
res.status(200).json(friend);
} else {
res.status(404).send({ msg: 'Friend not found' });
}
});
The best part is that express has a method called listen that will start as soon as we hit NPM RUN on a cli that's parked in the same file location as server.js. Make sure to specify a port that your system isn't already using
app.listen(port, () => {
console.log(`server listening on port ${port}`);
});
Question 2
In order to get connect to Server.js on our side you'll want use axios to make a GET/POST etc. call to whatever route you've made in your server.js above in the example it would be
.post('/api/friends', this.state.addFriend)
The biggest thing is you'll want multiple terminals running in order to have both the backend and the front end running at the same time. Start with backend first.

Can you call "express()" more than once in an ExpressJS application? (Or: what exactly is "express()" doing?)

I've been using Express for a while but suddenly I'm unsure about something pretty basic --
I'm trying to add custom middleware to a KeystoneJS application -- specifically I'm adding a JWT token endpoint to a TinyMCE custom field
The custom field is
export let Wysiwyg = {
type: 'Wysiwyg',
implementation: Text.implementation,
views: {
Controller: Text.views.Controller,
Field: importView('./views/Field'),
Filter: Text.views.Filter,
},
adapters: Text.adapters,
prepareMiddleware,
};
and prepareMiddleware is
function prepareMiddleware() {
const tinymcePath = dirname(require.resolve('tinymce'));
const app = express();
app.use(cors());
app.use('/tinymce-assets', express.static(tinymcePath));
app.post('/jwt', function (req, res) {
// NOTE: Before you proceed with the TOKEN, verify your users' session or access.
const payload = {
sub: '123', // Unique user id string
name: 'John Doe', // Full name of user
// Optional custom user root path
// 'https://claims.tiny.cloud/drive/root': '/johndoe',
exp: Math.floor(Date.now() / 1000) + (60 * 60) // 60 minutes expiration
};
try {
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256'});
res.set('content-type', 'application/json');
res.status(200);
res.send(JSON.stringify({
token: token
}));
} catch (e) {
res.status(500);
res.send(e.message);
}
});
return app;
}
This is all called from a KeystoneJS app that has its own ExpressJS server running. What exactly is the call to express() above doing? The ExpressJS API docs say
**express()**
Creates an Express application. The express() function is a top-level function exported by the express module.
var express = require('express')
var app = express()
I always understood this to be creating a new HTTP server. Surely you don't want to do that twice in a single app unless you're serving on different ports (which I'm not trying to do)?
Similarly, the KeystoneJS docs say
If you need to provide your own custom middleware for your system you
can create a custom App and include it in your exported apps.
class CustomApp {
prepareMiddleware({ keystone, dev, distDir }) {
const middleware = express();
// ...
return middleware;
}
}
Here, again, they're calling express().
What exactly happens when you callexpress()? It starts a new Express app, according to the docs, but I always thought this was equivalent to starting a new server. Surely, I thought, you can't start two servers on the same port.
Thanks for helping clear up my confusion -- I'm obviously not seeing the forest for the trees.
express() basically just creates a stack of middleware functions. It's not a server on its own.
Because it's just a stack of middleware, an Express app can be 'mounted' into another app. An example is shown here (edited for brevity):
var sub2 = express();
sub2.get("/", (req, res) => { res.json({}); });
var app = express();
app.use("/foo", sub2);
Defining and use()ing a new Express instance is really no different from loading any other middleware stack, such as express.Router().
As for binding to ports, usually, you'll only call the listen() helper function on the upper-most Express app instance. All this does is set up a basic HTTP server (so you don't have to) and registers your Express instance as the callback. It's little different from doing http.createServer(options, myUpperMostExpressApp);.

express-jwt middleware uses full paths instead of subpaths when used with express routers

I am using Node/Express.js to build an API. The API is present in the /api prefix if the prefix is not set, then Express server will return a Vue JS frontend. So basically I want express-jwt to get used as a middleware only when the /api prefix is set. For that reason I am using express router by doing app.use('/api, router).
And in the router file, I am using the express.jwt with unless syntax, to prevent any unauthorized access.
I also made the choice to implement express-jwt in the router file because I don't want to add /api/somepath in the express-jwt.unless, instead, I would like it to use /somepath and consider it as /api/somepath because I am using it with the router.
My router file:
const router = require('express').Router()
const routeController = require('./routeController')
const ejwt = require('express-jwt')
const config = require('../config/config')
// Protected Routes
// Protect Routes
router.use(
ejwt({
secret: config.APP_SECRET,
getToken: req => {
const { token } = req.body
if (token) return token
return null
}
}).unless({
path: ['/', '/auth/generateToken']
})
)
router.use(function(err, req, res, next) {
if (err.name === 'UnauthorizedError') {
res.status(401).send({
status: false,
message: 'Access Forbidden'
})
} else {
next()
}
})
This didn't work for me. Whenever I try to access the frontend with /, the express-jwt middleware gets triggered and I can't even access my frontend. I can get this working If I also provide it all of my frontend routes as well. Which is not an all a good idea because the frontend has a lot of paths.
In short, I want the middleware to only check for the token if the path has /api prefix set. Thanks in Advance :)

Resources