Retrieve Authenticate User's Orders - javascript

GET method
Use accessToken of an authenticated user.
Only non-admin account can proceed.
User should be able to retrieve his orders only.
Router
router.get("/my-orders", auth.verify, (req, res) => {
const user = auth.decode(req.headers.authorization);
if (!user.isAdmin) {
UserController.getMyOrders(req.body).then(getMine => res.send(getMine));
} else {
return res.status(403).send("Access denied.");
}
});```
Controller
module.exports.getMyOrders = (body) => {
return User.find({}, {
"isAdmin": 0,
"_id": 0,
"password": 0
});
}
I am getting everything. Can someone help me code how to filter the user where the token belongs and retrieve his orders and not able to get other users' orders?

By passing an empty object in your .find method, you are telling mongodb to look for everything. I'm assuming in body you have some data to find a specific user, if so you would use that. eg. if body contains a username, you would write...
module.exports.getMyOrders = (body) => {
return User.find({username: body.username});
}
Here is some more info on db.collection.find()
EDIT - Look up user by JWT:
router.get("/my-orders", auth.verify, (req, res) => {
//Here you have decoded your JWT and saved it as user
const user = auth.decode(req.headers.authorization);
if (!user.isAdmin) {
//here you are passing user instead of req.body
UserController.getMyOrders(user).then(getMine => res.send(getMine));
} else {
return res.status(403).send("Access denied.");
}
});
module.exports.getMyOrders = (user) => {
//now you are using 'username' from the decoded jwt to look up the user
return User.find({username: user.username});
}

Related

userSession is null after Auth.signUp with "autoSignIn" enabled (AWS Cognito)

I need to get the jwtToken from the Auth.signUp. Is this possible if i enable autoSignIn:{enabled:true}?
const signUp = async () => {
await Auth.signUp({
username: email,
password,
attributes: {
email, // optional
name,
},
autoSignIn:{
enabled: true
}
})
.then((data) => {
console.log(data.user); //user.signInUserSession is null
})
.catch((err) => {
if (err.message) {
setInvalidMessage(err.message);
}
console.log(err);
});
await Auth.currentAuthenticatedUser()
.then(user =>{
console.log(user)
})
.catch(error => {
console.log(error) //"User is not authenticated"
})
};
I call I want the jwttoken from the userSession data for conditional rendering and I store the token in my router.js. The response object from Auth.signUp contains a CognitoUser which has a signInUserSession value but its's null.
EDIT: Tried to call Auth.currentAuthenticatedUser() after but yields an error that user is not authenticated. But when i restart my app, the user will be authenticated. I still cant authenticate user on the same app "instance"
import { Auth, Hub } from 'aws-amplify';
const listener = (data) => {
switch (data.payload.event) {
case 'autoSignIn':
console.log('auto sign in successful');
console.log(data.payload) //returns user data including session and tokens.
//other logic with user data
break;
}
};
Above is the code to initalize the Hub listener provided by amplify api. Ater user presses sign up, I called to get user session data when user is automatically signed in.
Hub.listen('auth', listener)

How to check if key exists on mongoose document?

I have a User Model and recently added some key to it. This means that existing users will not have this key initially and new users do. Now I have a route where I want to check if the particular key exists on the user object so that I can add it if it returns false.
This is my route currently:
router.post("/new-application", verifyUser, (req, res) => {
const { application } = req.body;
User.findById(req.userId)
.then((user) => {
if (user.hasOwnProperty("applications")) {
console.log("has applications");
} else {
console.log("has not applications");
user["applications"] = initialApplications;
}
user.save().then((updatedUser) => {
// console.log(updatedUser);
});
})
.catch((err) => {
console.log("err fetching user: ", err);
res.end();
});
});
The problem is that if (user.hasOwnProperty("applications")) always returns false even after I added it to the user. I also tried if("applications" in user). That also does not work.
So how can I check if a key or field exists on a Mongoose object.
A simple way of checking if the field exists or not can be done by $exist.
router.post("/new-application", verifyUser, (req, res) => {
const { application } = req.body;
User.findById({$and: [{_id: req.userId}, {applications: {$exists:false}}]})
.then((user) => {
// it will return the user only when
// applications doesn't exist
})
.catch((err) => {
console.log("err fetching user: ", err);
res.end();
});
});
Note: the reason your old user doesn't show the applications is they don't have it when you saved them and changing the model now won't add this property to old models. So, we can use the $exists operator to check it.

Mongoose - How to Chain Save So Data Can Be Saved to Multiple Collections

I have read all sorts of variations of this on stackoverflow but I cannot seem to find a post that exactly explains what I'm trying to achieve, at the same time I believe this has to be a very common task during saving data.
So I need to save data to one collection and then read the _id from that doc and save it to a doc in a different collection. I have the following code and I can see the correct data with console.log but I don't see the data being saved to the database.
Appreciate if someone can guide me in the right direction.
Thank you!
router.post('/signup', async (req, res) => {
const { email, password, name, country } = req.body;
try {
const user = new User({ email, password });
await user.save((error, doc) => {
if (error) {
console.log(error);
} else {
const userProfile = new UserProfile({ userId: doc._id, name, country });
userProfile.save((error, doc) => {
if (error) {
console.log(error)
} else {
console.log(doc) // Can see this log with the correct data
}
});
}
});
const token = jwt.sign({userId: user._id}, 'MY_KEY');
res.send({ token });
} catch(error) {
return res.status(422).send(error.message)
}
})

How can I differentiate between authenticated users?

I am building an app that has both merchants and clients. Merchants offer their services and clients can book services from the merchants.
They BOTH are authenticated with Firebase and are on the Authentication list you can find on the Firebase Console.
On sign up, merchants' info go to a collection called 'businesses'. Clients go on a collection called 'users'.
This is how I create a 'user' document
async createUserProfileDocument(user, additionalData) {
if (!user) return
const userRef = this.firestore.doc(`users/${user.uid}`)
const snapshot = await userRef.get()
if (!snapshot.exists) {
const { displayName, email, photoURL, providerData } = user
const createdAt = moment().format('MMMM Do YYYY, h:mm:ss a')
try {
await userRef.set({
displayName,
email,
photoURL,
createdAt,
providerData: providerData[0].providerId, //provider: 'google.com', 'password'
...additionalData,
})
} catch (error) {
console.error('error creating user: ', error)
}
}
return this.getUserDocument(user.uid)
}
async getUserDocument(uid) {
if (!uid) return null
try {
const userDocument = await this.firestore.collection('users').doc(uid).get()
return { uid, ...userDocument.data() }
} catch (error) {
console.error('error getting user document: ', error)
}
}
This is how 'users' sign up
export const Register = () => {
const history = useHistory()
async function writeToFirebase(email, password, values) { //function is called below
try {
const { user } = await firebaseService.auth.createUserWithEmailAndPassword(email, password)
firebaseService.createUserProfileDocument(user, values)
} catch (error) {
console.error('error: ', error)
}
}
//Formik's onSubmit to submit a form
function onSubmit(values, { setSubmitting }) {
values.displayName = values.user.name
writeToFirebase(values.user.email, values.user.password, values) //function call
}
This is how a 'merchant' registers. They sign up with email + password and their info from a form go to a collection called 'businesses'
firebaseService.auth.createUserWithEmailAndPassword(values.user.email, values.user.password)
await firebaseService.firestore.collection('businesses').add(values) //values from a form
Here is where I would like to be able to differentiate between 'users' and 'merchants', so that I can write some logic with the 'merchant' data. so far it only works with 'users'
useEffect(() => {
firebaseService.auth.onAuthStateChanged(async function (userAuth) {
if (userAuth) {
//**how can I find out if this userAuth is a 'merchant' (business) or 'user' (client)
const user = await firebaseService.createUserProfileDocument(userAuth)
setUsername(user.displayName)
//if (userAuth IS A MERCHANT) setUserIsMerchant(true) **what I'd like to be able to do
} else {
console.log('no one signed in')
}
})
}, [])
The recommended way for implementing a role-based access control system is to use Custom Claims.
You will combine Custom Claims (and Firebase Authentication) together with Firebase Security Rules. As explained in the doc referred to above:
The Firebase Admin SDK supports defining custom attributes on user
accounts. This provides the ability to implement various access
control strategies, including role-based access control, in Firebase
apps. These custom attributes can give users different levels of
access (roles), which are enforced in an application's security rules.
Once you'll have assigned to your users a Custom Claim corresponding to their user role (e.g. a merchant or client Claim), you will be able to:
Adapt your Security Rules according to the claims;
Get the Claim in your front-end and act accordingly (e.g. route to specific app screens/pages, display specific UI elements, etc...)
More precisely, as explained in the doc, you could do something like:
useEffect(() => {
firebaseService.auth.onAuthStateChanged(userAuth => {
if (userAuth) {
userAuth.getIdTokenResult()
.then((idTokenResult) => {
// Confirm the user is a Merchant or a Client
if (!!idTokenResult.claims.merchant) {
// Do what needs to be done for merchants
} else if (!!idTokenResult.claims.client) {
// Do what needs to be done for clients
}
} else {
console.log('no one signed in')
}
})
}, [])
You may be interested by this article which presents "How to create an Admin module for managing users access and roles" (disclaimer, I'm the author).

Why is my [Op.or] only partially working?

I am trying to write an endpoint for searching for a user with a username or id that matches the passed string. Below is the current code I have set up for an endpoint on localhost at .../api/users/:search.
//GET user by id or password
router.get('/:search', (req, res, next) => {
User.findOne({
where: {
[Op.or]: [
{
id : { [Op.eq]: req.params.search }
},
{
username : { [Op.eq] : req.params.search }
}
]
}
}).then(data => {
res.json(data)
}).catch(next)
});
I believe I've set everything up correctly, but I'm only able to get a response when I pass in an existing id. Example: I have a user with the id "1" and the username "hyena12". If I do a GET on .../api/users/1, it will return the user. But if I do a GET on .../api/users/hyena12, the response just says "error".
I am using Sequelize with Node and am using PostMan to try and test this. Postgres DB is being used on the backend.

Categories

Resources