I'm doing a login prototype for my app, on the backend I'm trying to recieve the info (email and password) from the frontend to compare with the registered info from the database and validate the user login.
This is what I have done
const infos = request.query;
const email = infos.email as string; // email input frontend
const password = infos.password as string; // password input frontend
const checkLogin = await db('usersLoginsDB') // catching equal from database
.where('usersLoginsDB.email', '=', email)
.where('usersLoginsDB.password', '=', password)
.select('usersLoginsDB.email', 'email')
.select('usersLoginsDB.password', 'password')
After this, I have a checkLogin as type any[] with email and password, and I can't do something like checkLogin.email to use this info.
I tried this and kind of worked
let resultEmail = checkLogin.map(a => a.email) as unknown;
let testEmail = resultEmail as string;
But I'm not sure this is right, I would like to know if there is another way to do it, to get this checkLogin.email to work, or to get the email info from the database in a different way other than using .where and .select.
Thanks in advance.
Probably checkout knex or any other query builder. You don’t need to query everything all at once. You can do this in simple steps:
Query dB to check if the user email exists if it doesn’t throw an error.
If user email exists you need to now verify password. Query the DB to verify it.
If all goes well you can sign in the user. For example, if you have JWT setup send the token back to the user.
Related
I am using firebase to generate JWT tokens to authorize access to a hasura graphql server.
I want an end user to have a callable firebase function that they can call from the app so they can change the x-hasura-role in their claims without changing other parts of their claims. I am guessing the best way to do this is to export the old custom user claims and set a new role inputted by the user.
PseudoCode:
exports.changeUserType = functions.https.onCall( async (data, context) => {
var userType = data.usertype;
// get the old user claims somehow
// check if user should be able to change their userType via a graphql query
...
// edit the user claims
return admin.auth().setCustomUserClaims(userType, {
'https://hasura.io/jwt/claims': {
'x-hasura-role': userType,
'x-hasura-default-role': 'orgdriver',
'x-hasura-allowed-roles': ['orgauditor', 'orgdriver', 'orgmanager', 'orgadmin', 'orgdirector'],
'x-hasura-user-id': user.uid // <-- from the old claims so user can't edit
}
});
If there is a better way to do this, maybe by grabbing a user's id from the auth database by checking who ran the function please tell me. Thank you in advance.
When a Firebase Authenticated user hits a Firebase Function, their uid is passed in through context. I would ensure they are authenticated first:
if (context.auth == undefined) {
throw new functions.https.HttpsError(
'failed-precondition',
'The user must be authenticated.',
);
}
Then I would grab their uid:
const uuid = context?.auth?.uid as string;
Then you can get their user using the firebase-admin library's getAuth():
// get user
const user = await getAuth().getUser(uuid);
Now finally you can set your new custom claim property:
// set the hasura role
return await getAuth().setCustomUserClaims(uuid, {
...user.customClaims,
'x-hasura-role': userType,
});
Be sure to import:
import { getAuth } from 'firebase-admin/auth';
In this way you can safely know the user is authenticated and a uid exists, then you can simply grab the user and all their existing claims, then when you go to update destructure all existing claims values, and update the one value you want.
In this way get all the user's old claims, ensure they are authenticated, retain all old claim properties, and update the one thing you want to update.
I hope that helps out!
I have been trying to complete a login/Sign Up API using Node Js and MongoDB, and everything works fine except Forgot Password. I send an email to the user with a link to add new password.
The issue I am having is, How will I extract that specific user when he/she presses the reset button and only update that user's data in the database.
Classic rest password should have this flow:
user select the reset password
user enter its mail
a mail is sent from the system
mail contains a link (with expiration time for security) something like this:
https://acme.com/security/password/reset?code=8df024dfd526
code=8df024dfd526 in the link is related to the mail, so when is clicked, a final UI form will be prompted to the user. You could use this code to identify the email, sending it to your backend
You cannot ask the email again because anyone could change anyone's password.
Some times this code is known as one-time password (OTP).
As its name says: You must ensure that this code works just one time. I mean if user click again, you should show an error.
More details here.
implementation
You just need to persist somewhere (database) the following information:
the generated alphanumeric code with the requested email.
an expiration time for any code
usage count of code
Your email link to the user should be unique and can identify the user.
I do this:
// Generate hashed reset password token
User.resetPasswordToken = (user) => {
const resetToken = crypto.randomBytes(20).toString('hex')
// Update user with hashed token and expire time
user.resetPasswordToken = crypto.createHash('sha256').update(resetToken).digest('hex')
user.resetPasswordExpire = Date.now() + 10 * 60 * 1000
// Return unhashed token for use in email url
return resetToken
}
Here I do two things: 1) update the User in the DB with a resetPasswordToken and an expiration time/date for the resetPassword. Both saved in the database. 2) return an unhashed resetPassword for use in the email link to be sent (see below).
const resetToken = User.resetPasswordToken(user)
await user.save()
const resetUrl = `this link`
// Send email
const message = `<p>Please follow ${resetUrl} in order to create a new password. The link is valid for 10 minutes.</p>`
User now clicks the link and the endpoint extracts the reset token, hashes it and checks if it corresponds with the token saved on this particular user in the database from req.params.resettoken:
const resetPasswordToken = crypto.createHash('sha256').update(req.params.resettoken).digest('hex')
const timeNow = Date.now()
let user = await User.findOne({
where: {
resetPasswordToken,
deletedAt: null,
},
})
Then I perform some checks before I save the new password (encrypted) the user has typed in. I reset the reset token and expiration password in the database to null.
user.password = await encrypt(req.body.password)
user.resetPasswordToken = null
user.resetPasswordExpire = null
try {
await user.save()
} catch (error) {
return next(new ErrorResponse(`The new password could not be saved, please try again later`, 500))
}
When running the following code
const userDetails = {
Username: email,
Pool: userPool
};
const cognitoUser = new amazonCognitoIdentity.CognitoUser(userDetails);
console.log(cognitoUser.getUsername());
the output is the email.
How can I get Cognito user's GUID? (i.e : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
NOTES:
The use-case is general and not related to a specific function like 'signup' or 'login'.
The only parameter the client pass is an email, (i.e no tokens) for example when users forget their password.
As of today you cannot do it.
The UUID/sub is an internal attribute. You can search for users having it with the ListUsers API, but you cannot retrieve it in any other way than through a token.
I have cloud function triggered when a new object is added to a collection. It looks like this:
exports.emailAdmin = functions.firestore
.document('user-books/{userId}/books/{ASIN}')
.onWrite(event => {
That event.data.data() is an object added to the sub-collection ("books"). The userId comes from the Firebase authentication system. I.e. a user signed in, and added an object to the collection "user-books" with his/her "uid".
I tried:
firestore
.collection('users')
.doc(uid)
.get()
But that, "of course", fails because I don't have a collection called "users". How do I get to the "authentication database"?
The purpose is to convert the "uid" to that person's "email".
Import firebase-admin:
import * as admin from 'firebase-admin';
Then fetch the auth user:
const uid = ...;
const authUser = await admin.auth().getUser(uid); // this returns a promise, so use await or .then()
console.log(authUser.email);
You have no access to the user collection from firebase. What I did was creating a separate User's Table where I store all the users Data. I add the new user to my collection on the onCreate Hook like this:
exports.addNewUserToUserTable = functions.auth.user()
.onCreate(event => {
let user = event.data;
let database = admin.firestore();
return database.collection('user').doc(user.uid).set({
email: user.email
});
Another problem is, that at the moment it is not possible to get the logged in user's ID on Firestore Database Triggers (It works for the real time db). I'm still waiting for this feature to be released...
It seems like your question might be incomplete- you reference an event.data.data() line that didn't appear in your code snippet. Could you review it and add it if it's missing?
Also, just to clarify your purpose- you want to be able to access the email address of a user who has been authenticated, and tag it to the book that has been added by that user?
Thanks.
Since context.auth.uid is not available for firestore. You can assign userId to document and read it in the rules.
match /collectionA/{docId} {
allow update: if request.resource.data.userId == request.auth.uid;
}
exports.createProfile = functions.firestore
.document('collectionA/{docId}')
.onCreate((snap, context) => { // or onUpdate
const newValue = snap.data();
const {userId} = newValue;
});
I have a web app being built in express.js with a postgresql db.
I was wondering about how to implement the security, but everyone uses something different ( i guess thats a good thing? ).
Different modules different authentication sequences etc.
What I have at the moment:
1) User form post to for example /login
2) app routes to specific route
3) in route I try the following
var localconstring = "postgres://" + usr + ":" + pass + "#ip:port/db";
var client = new pg.Client(localconstring);
client.on('drain', client.end.bind(client));
client.connect(function (err, client, done) {
The database uses md5 so the pass is already protected by the db.
What should really happen?
Should I salt and hash the username and password and then save the salted/hashed credentials alongside the salt and then use the md5 of the db also?
If so which module?
Should I be logging in like that or try to do a select * from pg_roles/users ??
Thanks a lot!
(regarding the salt and hash if possible some detailed examples as I am pretty knew with authentication security)
Forgot to mention. cookies..
After the authentication I set the following cookies:
res.cookie('user', req.body.lguser.username, { signed: true })
res.cookie('watcher', o, { signed: true })
And look em up afterwards
req.signedCookies.user !== undefined
Is the signed attribute secure?
You should generate a key. This key should be saved on a cookie and on the database. Then when the user makes a petition, you can get the key on the cookie and search the user on the database.
There are libraries that help you on this, take a look at Passportjs:
http://passportjs.org/
First of all md5 is NOT seure anymore, so I would recommend you using 'sha512'.
A snippet would be something like this:
var crypto = require('crypto');
var salt = crypto.pseudoRandomBytes(32);
crypto.pbkdf2(userPassword,salt,1024,32,function(err,finalPassword){
//on the db you save the salt as a field and the SALTEDPASSWORD !!
//in this case the finalPassword
}
So when the user logs-in you get the user from the db by username and do the following:
//after getting the user from DB recalculate the hash
crypto.pbkdf2(passw,user.salt,1024,32,function(err,corrPass){
if(corrPass.toString() == user.password.toString()) // log in the user
//where user.password is the result from the db query
}
And I do recommend using passport like the other dude said, it simplifies all of the cookie stuff.
Hope it helped !