I am moving my python flask app to nodejs. Previously I was hashing the password with werkzeug.security.generate_password_hash(password). How do I verify the password hash in nodejs? Here is the code:
async function verify(password, hash) {
return new Promise((resolve, reject) => {
const [salt, key] = hash.split(":")
crypto.scrypt(password, salt, hash.length, (err, derivedKey) => {
if (err) reject(err);
resolve(key == derivedKey.toString('hex'))
});
})
}
Whenever I try out the code, it returns false. Could someone explain to me the solution?
you should get the user data first to get the hashed password and hashkey and then encrypt the passed password with the saved key then check is it equal to the hashed one.
// passing the incoming password from the user and the user data
// user = {password: '<the hased passowrd>', hashKey: '<the salt hash key for this user>'}
async function verify(password, user) {
const requestedPassword = await this.encryptPassword(password, user.hashKey);
return requestedPassword === user.password;
}
async function encryptPassword(password, hashKey) {
return await bcrypt.hash(password, hashKey);
}
Related
I'm using tRPC, NextJS, and PyShell for my project. I'll send user input to trpc and use that info as input to python script. Wait for python file done and return updated data to trpc, then send back to frontend.
Currently, it takes longer for python file to finish, and trpc does not send back the right info to frontend. Is there a way to fix that?
The code below is for trpc:
.mutation("upload", {
// validate input with Zod
input: z.object({ input: z.string() }).nullish(),
async resolve({ input }) {
var msg = ""
if (input?.input) {
console.log("-----------------------")
pyshell.on('message', async function (message) {
console.log("MSG: ", message);
msg = message;
});
pyshell.send(['sent', input.input]).end( function (err) {
if (err) console.error(err);
console.log("done");
});
}
return msg.length != 0;
},
});
msg is supposed to get updated with info from print statement in python file. It shows empty string in msg now.
I'm trying to call this Node.JS script (which is a unit test) to Python equivalent. It basically generates an AWS token, and uses that to send a .json file as HTTP post request. How can I re-write it in Python? I already did the token generation part.
Here is the NodeJS script:
describe('Send config file', () => {
let token = '';
before(function beforetest(done) {
this.timeout(10000);
tokengen(cognitoUser, authenticationDetails).then((result) => {
token = result.getAccessToken().getJwtToken();
done();
}, (err) => {
done(err);
});
});
it('should send config json', function test(done) {
this.timeout(10000);
chai.request(app)
.post(`${route}/sendjson`).set('accesstoken', token)
.send(vcamconfig)
.end((err, res) => {
expect(res).to.have.status(200);
expect(res.body).to.be.an('object');
expect(res.body).to.have.property('msg');
expect(res.body.msg).to.equal('Uploaded to S3');
console.log(res.body);
done();
});
});
});
And here is the python script I attempted so far to generate an AWS token. I need to use the token to send a file as a POST request.
from warrant.aws_srp import AWSSRP
import boto3
username = "user3"
password = "user3"
client_id = "myclientID1234567890"
user_pool_id = "us-east-1_sdhjsasls"
region = "us-east-1"
client = boto3.client('cognito-idp', region_name=region)
aws = AWSSRP(username=username, password=password, pool_id=user_pool_id, client_id=client_id, client=client)
tokens = aws.authenticate_user()
print(tokens)
I'm using firebase UI to authenticate users in a given frontend. Facebook authentication is enabled. I need to implement a facebook data deletion callback so I need to make my backend do two things:
delete/disable the facebook sign in method from the user that issued the data deletion request from facebook
delete every trace of facebook data from my firebase user (the provider user info) but without deleting the user
However, I can't find anything in firebase admin's documentation to delete facebook data. So, how can I delete the data?
PD: Keep in mind that I want to delete the user's provider user info, but not the whole user (because I need the user to stay there for data consistency)
If someone is looking for a NodeJS implementation, this is how you can do it:
module.exports = functions.https.onRequest(async (req, res) => {
try {
const signedRequest = req.body.signed_request;
const userObj = parseSignedRequest(signedRequest, FB_SECRET_KEY);
const userRecord = await admin
.auth()
.getUserByProviderUid("facebook.com", userObj.user_id);
await admin.auth().deleteUser(userRecord.uid);
res.json({
url: "<status_url>",
confirmation_code: "<code>",
});
} catch (e) {
// console.log(e);
res.status(400).json({
message: "Bad Request",
});
}
});
function base64decode(data: string) {
while (data.length % 4 !== 0) {
data += "=";
}
data = data.replace(/-/g, "+").replace(/_/g, "/");
return Buffer.from(data, "base64").toString("utf-8");
}
function parseSignedRequest(signedRequest: string, secret: string) {
var encoded_data = signedRequest.split(".", 2);
// decode the data
var sig = encoded_data[0];
var json = base64decode(encoded_data[1]);
var data = JSON.parse(json);
if (!data.algorithm || data.algorithm.toUpperCase() != "HMAC-SHA256") {
throw Error(
"Unknown algorithm: " + data.algorithm + ". Expected HMAC-SHA256"
);
}
var expected_sig = crypto
.createHmac("sha256", secret)
.update(encoded_data[1])
.digest("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace("=", "");
if (sig !== expected_sig) {
throw Error("Invalid signature: " + sig + ". Expected " + expected_sig);
}
return data;
}
PS. There is no way to just unlink providers via Firebase Admin SDK for now so best we can do is delete the user data.
During the user migration I want to return "Incorrect username or password." as error message instead of "User does not exist"
Have been searching on google for a while, cannot find out how to replicate the following JS example in this documentation
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-migrate-user.html
exports.handler = (event, context, callback) => {
var user;
if ( event.triggerSource == "UserMigration_Authentication" ) {
// authenticate the user with your existing user directory service
user = authenticateUser(event.userName, event.request.password);
if ( user ) {
event.response.userAttributes = {
"email": user.emailAddress,
"email_verified": "true"
};
event.response.finalUserStatus = "CONFIRMED";
event.response.messageAction = "SUPPRESS";
context.succeed(event);
}
else {
// Return error to Amazon Cognito
callback("Bad password");
}
}
else if ( event.triggerSource == "UserMigration_ForgotPassword" ) {
// Lookup the user in your existing user directory service
user = lookupUser(event.userName);
if ( user ) {
event.response.userAttributes = {
"email": user.emailAddress,
// required to enable password-reset code to be sent to user
"email_verified": "true"
};
event.response.messageAction = "SUPPRESS";
context.succeed(event);
}
else {
// Return error to Amazon Cognito
callback("Bad password");
}
}
else {
// Return error to Amazon Cognito
callback("Bad triggerSource " + event.triggerSource);
}
};
It uses callback('message') in nodejs but I cannot find out how to do that in Python.
Stumbled on to this question
I can't find callback parameter in python lambda handler
Tried returning message string, but get "Exception during user migration"
Try raising a ValueError:
raise ValueError("My custom message")
For me, this changed the wording of the exception returned to the boto client from
An error occurred (UserNotFoundException) when calling the AdminInitiateAuth operation: Exception migrating user in app client aeiouffhfha
to the somewhat more useful
An error occurred (UserNotFoundException) when calling the AdminInitiateAuth operation: UserMigration failed with error My custom message.
So I have also tried raising an exception':
raise Exception('Incorrect username or password')
This still gives "Exception during user migration" in the hosted UI, but that worked on a custom login UI which respects the error message.
We use AWS Cognito for authentication.
When we create a user, Cognito sends the following email with the following message:
Your username is {username} and temporary password is {####}.
as we know, the user is created with FORCE_NEW_PASSWORD status.
is that possible somehow to add access token to the email body so as to form a link to the page where user may change their password to activate account?
I have used aws-cognito in node js and angular2.
When you attempt first time login then user injects the temporary credentials, after this an OTP will be sent to either phone or email ( decided by user pool ).
The following is function used for login:
var authenticationData = {
Username: username, // req.body.username
Password: password // req.body.password
};
var poolData = {
UserPoolId: upid,
ClientId: cid,
AuthFlow: 'ADMIN_NO_SRP_AUTH'
};
var userPool = new AWS.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var authenticationDetails = new AWS.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var userData = {
Username: username,
Pool: userPool
};
var cognitoUser = new AWS.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
// not for first time login case and user has permanent credentials
},
onFailure: function (err) {
res.send(err); //login failure
},
newPasswordRequired: function (userAttributes, requiredAttributes) {
response = {"ChallengeName": "NEW_PASSWORD_REQUIRED", "userAttributes": userAttributes};
localStorage.setItem('userAttributes', JSON.stringify(userAttributes)); // I have used localStorage to save data temporarily
res.send(response);
}
}
Now, as you are asking for first time login.
So you need to pass the old password and username, user attributes to next API call to get the permanent credentials.
I have not sent token in email message
and keep that in localStorage so that can be utilised when user come back to the browser this way you will get the ID Token.
so you can use the code for update password as follows:
router.post('/updatepassword', function (req, res) {
var username = req.body.username;
var newPassword = req.body.newpassword;
var userAttributes = req.body.userAttributes;
var oldpassword = req.body.oldpassword;
var userPool = globalConfiguration(); // custom function to get pool data
var userData = {
Username: username, //req.body.username,
Pool: userPool
};
var params = {
UserPoolId: 'us-west-2_XxxxxXX', /* required */
Username: username, //req.body.username,
};
var authenticationData = {
Username: username, //req.body.username,
Password: oldpassword, //req.body.password,
};
var authenticationDetails = new AWS.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
// so only username and previous password are required.
var cognitoUser = new AWS.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) { },
onFailure: function (err) { },
newPasswordRequired: function (userAttributes, requiredAttributes) {
// the api doesn't accept this field back
delete userAttributes.email_verified;
delete userAttributes.phone_number_verified;
cognitoUser.completeNewPasswordChallenge(newPassword, userAttributes, this);
var success = {'success': 'success'};
res.send(success);
}
});
});
Hope this will helps you!