Overriding Email/Username with Cloud Code and PFFacebookUtils - javascript

While utilizing the Parse service, I'm trying to allow users to skip sign-in fields by logging into the app via Facebook and using the Facebook information to fill in fields. I'm using Parse's Cloud Code to make the swift client-side code as simple as possible. However, I'm getting a little talk-back from Parse.Cloud when trying to use the cloud code to set the Username and Email Address fields of Parse.User. I get the following errors when running it in-app.
[Error]: Can't modify username in the before save trigger (Code: 141, Version: 1.6.1)
And when removing the username function, I receive:
[Error]: Can't modify email in the before save trigger (Code: 141, Version: 1.6.1)
Through the same code, I'm also setting firstName and lastName without any errors using the same method. Below is the code used.
[CLOUD CODE]
function pad(num, size){ return ('000000000' + num).substr(-size); }
Parse.Cloud.beforeSave(Parse.User, function (request, response) {
var token = request.object.get("authData").facebook.access_token
Parse.Cloud.httpRequest({
url: 'https://graph.facebook.com/v2.1/me?fields=first_name,last_name,email&access_token=' + token,
success: function(httpResponse) {
var responseData = httpResponse.data;
request.object.set("email", responseData.email) // <- Error Occurs Here
request.object.set("username", responseData.email.split("#")[0].concat(".", pad(Math.floor(Math.random()*10000),4))) // <- Error Occurs Here
request.object.set("firstName", responseData.first_name)
request.object.set("lastName", responseData.last_name)
response.success()
},
error: function(){
response.error("Something went wrong.")
}
})
})
[SWIFT CODE]
#IBAction func loginWithFacebookButtonAction(sender: AnyObject){
loginWithFacebookButtonOutlet.setTitle("Logging In...", forState: UIControlState.Normal)
let permissions = ["public_profile", "user_friends", "email"]
PFFacebookUtils.logInWithPermissions(permissions, {
(user: PFUser!, error: NSError!) -> Void in
if user == nil {
println("Uh oh. The user cancelled the Facebook login.")
} else if user.isNew {
println("User signed up and logged in through Facebook!")
self.performSegueWithIdentifier("facebookToApp", sender: self)
} else {
println("User logged in through Facebook!")
self.performSegueWithIdentifier("facebookToApp", sender: self)
}
})
}
Other questions here that I've encountered are caused when the user tries to use .save() instead of response.success() which doesn't seem to be the problem in this situation.
Many thanks.

it seems that username & email can not be altered in beforeSave trigger as they should be unique
Right now, the email field cannot be modified on a beforeSave because this field should be unique. If you were to use a value that another user is already using, this would result in an error that your beforeSave method would not be able to handle.
reference: https://www.parse.com/questions/why-cant-i-modify-the-email-field-in-a-before-save-trigger

Related

firebase auth applyActionCode method does not turn emailVerified to true

I am developing firebase authentication system where a user is sent email to verify email adr. I got everything working eventually. The user signs up and the email (with the link) is sent to the signed up edmail adr. I use custom email action handler (https://firebase.google.com/docs/auth/custom-email-handler) to respond to a click on the link. On my node.js express route, I get oobCode (which firebase documentation say is "A one-time code, used to identify and verify a request") and pass it as an argument to firebase.auth().applyActionCode(oobCode) which returns a void promise when resolved. see code below.
firebase.auth().applyActionCode(oobCode)
.then( () => {
return admin.auth().updateUser(currentUser.uid, {
emailVerified: true,
})
})
.then( () => {
return res.status(301).redirect(`../unps/${currentUser.uid}`)
})
.catch( (error) => {
return res.status(500).json({ unp_error: `error message: ${error.message} - error code:
${error.code}` })
});
My undestanding of the documentation is that, applyActionCode method, if resolved, will set the emailVerified to true but this does not happen even though there is no error. I had to call updateUser to change emailVerified to true. Shouldnt this be done automatically by the method applyActionCode if a valid oobCode is presented as argument? What am I missinmg? Pleaase help?
All information about the Firebase Authentication user in your application code is taken from the ID token. This ID token is valid for an hour, and automatically refreshed by the SDK about 5 minutes before it expires. Until the token is refreshed, it may not reflect the latest value of emailVerified or other information about that user profile on the server.
It is indeed normal that you need to force a refresh of the token, to get the updated status before it auto-refreshes. When you do that, you shouldn't have to call admin.auth().updateUser(...) though.

Parse Login in node.js - Login successful but 'There is no current user'

I'm having trouble interacting with my Parse data in node.js. I'm able to login successfully, but Parse.User.current() returns null. After running the below code, I'd like to query data that has ACL read/write only for that user. Currently, that query returns empty, but if I change that data to public read/write, I can see the results of the query output in the terminal.
Here is my node.js code:
Prompt.get([{
name: 'username',
required: true}, {
name: 'password',
hidden: true}], function (err, result) {
if (err) {
console.log('Error: ' + err);
} else {
Parse.User.logIn(result.username, result.password, {
success: function(user) {
console.log('LOGGED IN');
console.log(user);
console.log(Parse.Session.current());
console.log(Parse.User.current());
... (query happens below this)
And my console output:
prompt: username: pablo
prompt: password:
LOGGED IN
ParseUser { _objCount: 0, className: '_User', id: 'EXyg99egkv' }
ParsePromise {
_resolved: false,
_rejected: true,
_resolvedCallbacks: [],
_rejectedCallbacks: [],
_error: 'There is no current user.' }
null
Thanks in advance.
Is this not a usecase for Parse.User.become()? From the parse docs:
If you’ve created your own authentication routines, or otherwise
logged in a user on the server side, you can now pass the session
token to the client and use the become method. This method will ensure
the session token is valid before setting the current user.
Parse.User.become("session-token-here").then(function (user) {
// The current user is now set to user.
}, function (error) {
// The token could not be validated.
});
I had similar problems and found this Parse blog that explains the issue:
Also in Cloud Code, the concept of a method that returns the current user makes sense, as it does in JavaScript on a web page, because there’s only one active request and only one user. However in a context like node.js, there can’t be a global current user, which requires explicit passing of the session token. Version 1.6 and higher of the Parse JavaScript SDK already requires this, so if you’re at that version, you’re safe in your library usage.
You can execute queries with user credentials in a node.js environment like this:
query.find({ sessionToken: request.user.getSessionToken() }).then(function(data) {
// do stuff with data
}, function(error) {
// do stuff with error
});
If you wish to validate that token before using it, here's an explanation of how you could go about doing that:
one way would be to query for an object known to be only readable by the user. You could have a class that stores such objects, and have each one of them use an ACL that restricts read permissions to the user itself. If running a find query over this class returns 0 objects with a given sessionToken, you know it's not valid. You can take it a step further and also compare the user object id to make sure it belongs to the right user.
Session tokens cannot be queried even with the master key.

Access denied [403] when updating user accounts client-side in Meteor

I'm reading through the docs for Meteor here and the useraccounts package here but can't find an answer. I've added the useraccounts package successfully and have created a few users, but now I want to add some data to the record in the collection for a given user.
For example, after account creation and login. I want the user to be able to add/edit some fields on their record (short biography, etc..), but I keep getting a 403 error whenever performing a Meteor.users.update(..).
My login config file can be found here.
The code that's causing an error:
Template.editProfile.events({
'submit form': function(e) {
e.preventDefault();
var profileInfo = {
displayName: $(e.target).find('[name=displayName]').val(),
tagLine: $(e.target).find('[name=tagLine]').val(),
aboutMe: $(e.target).find('[name=aboutMe]').val()
};
Meteor.users.update(
{ _id: Meteor.userId()},
{ $set: profileInfo},
function (err) {
if(err) {
console.log('there was an error submitting editProfile data');
console.log(err);
} else {
Router.go('profile');
}
}
);
}
});
Doing console logs show the Meteor.userId() coming back correctly so I'm not sure what the problem is. I'm assuming it's an issue with allow/deny but I don't even know where to begin to troubleshoot.
The exact error is:
error: 403
errorType: "Meteor.Error"
message: "Access denied [403]"
reason: "Access denied"
By removing the insecure package, client-side write access will be denied by default.
If you want to allow clients to write directly to a collection, you need to define rules.
For example:
Meteor.users.allow({
update: ownsDocument
});
ownsDocument = function (userId, doc) {
return doc && doc.userId === userId;
};
The ownsDocument() function checks if the userId specified owns the document. In addition to the update callback, you can set rules for insert and remove.
Read more about Meteor's collection.allow(options), access a demo app or clone the repository.

What is the syntax for deleting/releasing Twilio phone numbers in Node.js?

I'm getting a 404 error when trying to delete a Twilio phone number via the API.
Here's my code:
var twilioSID = user.numberSID; // PN946a0603c974be563c5916f865be4d0b
var accountSid = '{removed}';
var authToken = '{removed}';
var client = require('twilio')(accountSid, authToken);
client.incomingPhoneNumbers(twilioSID).delete(function(err, deleted) {
if (err){
console.log(err);
} else {
console.log('Deleted from Twilio');
}
});
Here is the error I'm getting in the console:
{
status: 404,
message: 'The requested resource /2010-04-01/Accounts/{removed}/IncomingPhoneNumbers/PN946a0603c974be563c5916f865be4d0b.json was not found',
code: 20404,
moreInfo: 'https://www.twilio.com/docs/errors/20404'
}
The Twilio API doesn't have hardly any documentation for deleting numbers either. Any ideas on why this is not working?
#parkeragee Your solution is working
client.incomingPhoneNumbers(poneNumberSID).delete(function(err, deleted) {
if (err){
console.log(err);
} else {
console.log('Deleted from Twilio');
}
});
They changed it from delete to remove in their node js module.
As of this writing, the function to remove Twilio phone numbers via the API is called remove. If you try to use delete, you should receive the following error:
client.incomingPhoneNumbers(...).delete is not a function
I wasn't able to find any reference in the Twilio API docs; found this by reading the source, Luke.
Here's an example invoking remove:
client.incomingPhoneNumbers(sid).remove()
.then(function(deleted) {
// Success
})
.catch(function(error) {
// Handle error
});
According to their REST API documentation, you can send an HTTP DELETE request to a url like /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers/{IncomingPhoneNumberSid}. So the URL in the error message looks almost right, except for the .json on the end. Either way it looks like a bug in their code if the phone number is in fact still attached to your account.
My issue was that I was overwriting my production phone nuber numberSID variable with my test phone number. So the user.numberSID; that I was assigning to a variable was for the Twilio test phone numbers. So, when it was searching for that numberSID, it returned a 404.

Parse custom webhook: can I query my tables?

In a Parse custom webhook, which is of the form:
app.post('/receiveSMS', function(req, res) {
Where receiveSMS is hooked up to the Twilio api and this method is properly called (I have logs to prove it), but I'm trying to query on my tables within this method and it doesn't seem to be working.
Is this allowed, or is there anything special I need to do to make this work?
var contactObj = Parse.Object.extend("Contact");
var contactQuery = new Parse.Query(contactObj);
console.log(req.body.From);
contactQuery.each(function(contact) {
and the body of the each call never gets called.
Is this allowed, and if so, what am I doing wrong here?
Update -- The entirety of the webhook code block is:
app.post('/receiveSMS', function(req, res) {
console.log('receive SMS');
console.log(req.body.Body);
res.send('Success');
if(req.body.Body.toLowerCase() == "in" || req.body.Body.toLowerCase() == "out") {
twilio.sendSMS({
From: "(xxx) xxx-xxxx",
To: req.body.From,
Body: "It's been noted, and notifications have been sent. Check us out!"
}, {
success: function(httpResponse) {
console.log(httpResponse);
response.success("SMS Sent!");
},
error: function(httpResponse) {
console.error(httpResponse);
response.error("Uh OH, something went wrong");
}
});
if(req.body.Body.toLowerCase() == "in") {
console.log("in was received");
// eventQuery
var contactObj = Parse.Object.extend("Contact");
var contactQuery = new Parse.Query(contactObj);
console.log(req.body.From);
// contactQuery.equalTo("phone", req.body.From);
contactQuery.first({
success: function(contact) {
console.log("found contact");
console.log(contact);
}, error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
}
}
});
This code is called and the logs "console.log('receive SMS')" and the like are all called, except for what is inside the query's first call.
Queries on tables is fine, but you can't use the each() function, as that is restricted to only work in background jobs.
You'll have to use find() or first() or get() depending on your needs.
UPDATE
OK, after seeing your full code I have some ideas as to why it isn't working. First off you're sending res.send("Success"); before you're finished, I'm not positive but I think this causes it to stop running the rest of your code (haven't checked, could be wrong).
Also you're doing multiple async operations without chaining them so the contactQuery.first() will run before the twilio.sendSMS() is finished.
Inside twilio.sendSMS() you're calling response.success() / response.error(). These are for cloud methods, not web hooks, so I expect these would be throwing errors server-side (check the logs on the Dashboard).
Inside contactQuery.first() you are using alert() which isn't supported in cloud code.
I'm not sure if those mistakes will be caught early and throw errors or if they'll raise run-time exceptions, but they should be fixed, your code re-deployed and try again. Then report any errors in the server logs.
Yes, it's allowed, I'm using the same web hooks.
My guess is that you probably have defined security restriction on your Contact class that prevent the query to fetch anything. What's the security setting on this class ?
You can either try to relax the constrains, or login as a dummy user, and execute the query (approach that I chose).
cheers
-A

Categories

Resources