How to get Auth0 user.sub in Node.js/Express backend? - node.js

I have a simple ToDo app using React frontend, Nodejs/Express API backend, and MySQL DB. My tasks DB has a userId column which pulls the user.sub value on the frontend before sending the task to the backend and creating the DB entry.
Next, I want users to be able to see only their own tasks as identified by the userId column - to do this, I want to pull the user.sub value of the current logged-in user and dynamically insert it into my backend SQL query.
Example:
Task.getAll = (userId, result) => {
let query = `SELECT * FROM tasks WHERE userId LIKE '${userId}'`;
sql.query(query, (err, res) => {
if (err) {
console.log("error: ", err);
result(null, err);
return;
}
console.log("tasks: ", res);
result(null, res);
});
};
If I hardcode an existing auth0 user ID (“auth0|123456789…”) into the statement above, it successfully filters tasks based on ID - so the logic seems sound. I just need to be able to pull the user.sub value, which I have not been able to figure out in the backend. I have been following this quickstart document - https://auth0.com/docs/get-started/architecture-scenarios/spa-api/api-implementation-nodejs:
Determine the User Identity The express-jwt middleware which is used to validate the JWT, also sets the req.user with the information
contained in the JWT. If you want to use the sub claim to identify the
user uniquely, you can simply use req.user.sub.
…This sounds like what I want to achieve, but I can’t figure out how to actually do this in practice. How exactly can I pull the user.sub value from the checkJwt middleware?

Related

Associating user data between multiple passport strategies

I'm using node, express, express-session, mongoose and passport-discord to authenticate users. But I also wish to use passport-steam to optionally link a user's steam account to their discord account.
The problem is, in order to link their steam account, I would also need a way of knowing which discord user is trying to do so, but the authentication callback doesn't include any request/response headers to access the current session, and the database I'm storing discord users in doesn't have any steam data in it until steam's authentication callback is called, at which point I have no idea which discord user is trying to authenticate their steam account.
To help remedy this, I've setup 3 mongoose models to have 3 separate database collections:
DiscordUser (stores discord user information, including their discordId)
SteamUser (stores steam user information, including their steamId)
User (stores required discordId and optional steamId)
I then try to serialize and deserialize the User object, but the passport.deserializeUser(obj, done) method seems to pass in an id instead of a User object, and I can't be sure if it's a discordId or a steamId, or which discord user I'm deserializing a SteamUser for, so I end up back at square one.
What's the best way to go about associating these users?
If needed, I can supply some code snippets upon request.
I found an article which helped solve this problem.
Here's what I did to fix it:
Using passReqToCallback: true in the new Strategy(...) options
In both strategy callback functions, adding req as the first parameter(ie., async function callback(req, accessToken, refreshToken, profile, done) { ... })
And then changing this:
router.get('/steam', passport.authenticate('steam'), (req, res) => {
//...
});
To this:
// Note that the route here is the same as the callback/redirect
router.get('/steam/redirect', (req, res, next) => {
passport.authenticate('steam', (err, user, { nextRoute }) => {
if (err)
return next(err);
if (nextRoute) {
// user was authorized with provider?
// send em back to profile
return res.redirect(nextRoute);
} else {
// give em some cookies
req.logIn(user, err => {
if (err)
return next(err);
return res.redirect('/');
});
}
})(req, res, next)
});
And then finally, modifying the User model schema to include the sessionID, which can be accessed via req.sessionID (or req.session.sessionID) from within the callback functions of both strategies.
This allows me to update the database upon successful login with my primary strategy(s) (discord, in this case), and then reference it from secondary strategies (steam, in this case), providing enough time to update the User database to have linked information between each strategy for as long as the session is still valid. After which, you can always simply reference the User database anywhere in code to check for valid primary and secondary strategy ids, allowing you to cross-reference other database collections for information, update the session id if the current session id doesn't match (and possibly forcing the user to re-authenticate?), etc.

How do I get the ID of an object from mongodb database in my react application?

I am learning by building. I am building a blog management system using Reactjs, Nodejs, Mongodb.
I would like to store some frontend values in the database so that anyone I give admin permission can post, edit and delete such values. These values are website name, sub-name, sidebar bio description, header image and bio image.
This is the code to create the value:
//create new frontend paramters into database
router.post("/", async (req, res) =>{
const newFrontendValues = new Frontend(req.body);//we called the frontend model we created and we used req.body
try{
const savedFrontendValues = await newFrontendValues.save()//we tried to save the frontend values created
res.status(200).json(savedFrontendValues)
}catch(err){
res.status(500).json(err)
}
});
I wrote the code in node to get the values like this after creating them:
//get frontend parameters
router.get("/:id", async (req, res) =>{
try{
const frontend = await Frontend.findById(req.params.id)
res.status(200).json(frontend)
}catch(err){
res.status(500).json(err)
}
})
my server api code
app.use("/api/frontend", frontend)
In react, I wanted to call the _id of the values but I am lost. I really don't know how to go about that.
It is working fine as desired in postman because I can directly implement the value _id.
See attached image
But in React, I wanted that to be dynamic.
Here is my React code:
useEffect(() => {
const fetchFrontendValue = async () =>{
const res = await axios.get("/frontend")
console.log(res.data)
}
fetchFrontendValue()
}, [])
How do I add the _id to this
axios.get("/frontend")
You'd want to look at get request parameters. Usually as a convention, people pass them in the URL. So it would be something like http://localhost:5000/api/frontend?id=617944dc7e00022337a483be78 and on the API side, you'd use req.body.id to pass that to the database. There are other ways to do it too, but this is the most common because some old browser drop the parameters attached to a GET request. Here's a link:https://www.w3schools.com/tags/ref_httpmethods.asp
You should consider going for a complete solution. On a basic level, you should be following these steps
Implement a backend route /getall that fetch out all items in DB in this manner
await Frontend.find({})
Render the fetched list on frontend side in a way that each item would be a React UI item and as part of each item, you have the buttons for deleting and updating the item data
{backendData?.map((item, index)=><SingleItem key={item?._id} data={item} />)}
As each SingleItem has update and delete buttona and also the mongodb ID as part of data, so on clicking update and delete button, you will get the id from the data and call relevant DB Url on backend side

How can I transfer data from one route to another

I implement user registration. I have two routes.
import express from 'express';
import User from '../models/user';
const router = express.Router();
router.post('/signup', async (req, res) => {
try {
...
const user = new User(
red.body.name,
red.body.username,
red.body.user_email,
red.body.user_password,
red.body.user_phone,
);
...
} catch (err) {
return res.status(500).json({
err: err
});
}
});
router.post('/verify', async (req, res) => {
try {
...
console.log(user);
...
} catch (err) {
return res.status(500).json({
err: err
});
}
});
I need to send data from the route, user registration, to the route, user confirmation.
How to send user data to the second route for confirmation?
There are 2 strategies to implement this feature (maybe more):
Strategy 1: Store user in server.
You can store user object and assign it a unique key (e.g. uuid) in server. The user data can be stored as global variable (in memory of Node.js process), or it can be stored in memory database (e.g. Redis) if you are using multiple Node.js process.
In POST /signup route handler, the user data can be written to server, and this newly created user id would be returned to browser. Then, in POST /verify route handler, server would retrieve corresponding user data via its id.
Strategy 2: Store user in browser.
Another strategy is returning the whole user data to browser in POST /signup route handler, and let browser send it back in the following POST /verify request. There are 2 ways to implement this design (maybe more):
Return user data to browser via Set-Cookie. Browser would send user data as cookie automatically.
Return user data to browser as plain response body. Browser take it and save it in localStorage or sessionStorage. Then, when sending POST /verify request, browser would read that data and put it as plain HTTP request body.
The best way to approach this would be to store the user data as a browser cookie. Storing user data in local storage is not recommended as it can be accessed by JavaScript and hence poses a security threat.

Bot Framework Node.js ad hoc message TO A SPECIFIC USER

I have been staring at this for hours and can't find a solution and that is even though by all suggestions it SHOULD be quite easy - https://learn.microsoft.com/en-us/bot-framework/nodejs/bot-builder-nodejs-proactive-messages.
I have created a simple code which will "register" the user and save their data in my cosmosDatabse on Azure. That works perfectly.
//ON "register" SAVE USER DATA AND SAY REGISTERED MESSAGE
bot.dialog('adhocDialog', function(session, args) {
var savedAddress = session.message.address;
session.userData.savedAddress = savedAddress;
//REGISTERED MESSAGE
session.endDialog("*Congratulations! You are now registered in our network! (goldmedal)*");
})
.triggerAction({
matches: /^register$/i
})
But how can I then access that specific user and send him a message if, say, a condition is met? (in fact on HTTP request)
I am fairly certain we have to write the conversation ID or user ID somewhere. The question is where?
function startProactiveDialog(address) {
bot.beginDialog(address, "A notification!");
}
This is how simple I think it should be. But where do you specify the user then?
You've saved the address of the user inside of your database by saving it to session.userData.savedAddress. When the event triggers, perform a query to your database that checks for the users that meet two criteria.
They're registered to listen for the event
Their address has been saved inside of the database.
In your case, you can save a property to the session.userData object, a property that lists which events they're listening for. If you just need to send a message to the user, then you can simply use bot.loadSession(savedAddress) to ping the user.
Edit:
So instead of looking specifically by user ID, you should send a query to your CosmosDB that looks for entries that have a "listen-to" Boolean-type flag corresponding to the event.
You're not worrying about the user ID at first, you're just retrieving all entries with a query that would (broadly speaking) look like this:
SELECT * FROM BotState WHERE data LIKE 'listenForEvent=1.
So to setup your session.userData so that the above theoretical query would work, you would need to modify that snippet of code in your question to something like the following:
bot.dialog('adhocDialog', function(session, args) {
var savedAddress = session.message.address;
session.userData.savedAddress = savedAddress;
session.userData.listenForEvent = 1 // Our property we're going to look for.
session.endDialog("*Congratulations! You are now registered in our network! (goldmedal)*");
})
.triggerAction({
matches: /^register$/i
})
Actually, the savedAddress should be an instance of IAddress, and also, the function loadSession(address: IAddress, callback: (err: Error, session: Session) => void): void; and address(adr: IAddress): Message; under Message class all require IAddress as the parameter.
So first of all, you should save the entire address json object in cosmosDB for later using.
As botbuilder for Node.js is built on Restify or Express, you can build an addition route for your user to trigger and send proactive messages. The work flow could be following:
Guide user to register & Save the user's address object with the account mapping in your DB
Create a Route in Restify or Expressjs for trigger the proactive message:
server.get('/api/CustomWebApi', (req, res, next) => {
//find the user's address in your DB as `savedAddress`
var msg = new builder.Message().address(savedAddress);
msg.text('Hello, this is a notification');
bot.send(msg);
res.send('triggered');
next();
}
);
or if you want to leverage loadSession
server.get('/api/CustomWebApi', function (req, res, next) {
bot.loadSession(savedAddress, (err, session) => {
if (!err) {
session.send('Hello, this is a notification')
session.endConversation();
}
})
res.send('triggered');
next();
});
I created a users.json file, to which I save all the users. It works the way I need it to. I guess database would be better, but I don't really have a clue where to begin with that. Database is a whole new chapter I have not encountered yet, so it doesn't make sense to work on it when the project needs are resolved.

Hows should I structure middleware to verify an ID exists in an external service in a Node/Express app?

I've currently written middleware to verify an ID exists in an external services (Salesforce). I initially wrote it when it was a single use app, but now I'm trying to make it work with different routes, so I want it to be fairly generic.
I don't even know if middleware is the right way to go, or if I should just call the function before saving the specific form.
I've got a form where someone puts in some information about a project, and the salesforce ID. For background, the salesforce ID is actually an auto-increment number, and I need to convert that to the actual salesforce system ID before I use jsForce to create a new object linked to that ID.
My route looks like this:
router.post('/invoice/add', ensureLoggedIn, invoiceController.validateInvoice, catchErrors(sfdc.validateSFID), catchErrors(invoiceController.saveInvoice))
So, I've got a middleware that does this:
exports.validateSFID = async(req, res, next) => {
const salesforceProjectNumber = req.body.SF_Opportunity
const sfResult = await conn.search(`FIND ... long query`, (err, result) => {
if (err || result.searchRecords.length !== 1) {
req.flash('error', 'Unable to find a Salesforce Job with that ID number.')
console.error(`ERROR: ${req.user.displayName} errored when looking up job number ${salesforceProjectNumber}.`)
return result
}
})
if (sfResult.searchRecords.length > 0) {
req.body.salesforce_Opportunity_id = sfResult.searchRecords[0].Id //Create a generic variable to hold the salesforce opportunity so it works regardless of the custom object name
res.locals.Opportunity_Clean_Name = sfResult.searchRecords[0].Name
}
next()
}
The query rarely throws an error, but in this case, an error is basically returning !1 records.
When that happens, I want to flash a message on the screen saying the ID wasn't found, but keep the form filled in.
When an ID is found, I want to proceed to save it and NOT display the form fields anymore.
This middleware needs to work regardless of the form I'm using, I want to be able to pipe in the middleware from any form that might require a user to enter a salesforce job as a field.
Any thoughts on how best to handle it all?
You can use your middleware by using app.use() function
app.use((req, res, next) => {
// Every time a request has made, this middleware will fire
console.log('Howdy');
})

Resources