I am using the email verification feature that Parse offers and would like my users to be able to resend the email verification if it fails to send or they cannot see it. Last I saw, Parse does not offer an intrinsic way to do this (stupid) and people have been half-hazzerdly writing code to change the email and then change it back to trigger a re-send. Has there been any updates to this or is changing the email from the original and back still the only way? Thanks
You should only need to update the email to its existing value. This should trigger another email verification to be sent. I haven't been able to test the code, but this should be how you do it for the various platforms.
// Swift
PFUser.currentUser().email = PFUser.currentUser().email
PFUser.currentUser().saveInBackground()
// Java
ParseUser.getCurrentUser().setEmail(ParseUser.getCurrentUser().getEmail());
ParseUser.getCurrentUser().saveInBackground();
// JavaScript
Parse.User.current().set("email", Parse.User.current().get("email"));
Parse.User.current().save();
You have to set the email address to a fake one save and then set it back to the original and then parse will trigger the verification process. Just setting it to what it was will not trigger the process.
iOS
if let email = PFUser.currentUser()?.email {
PFUser.currentUser()?.email = email+".verify"
PFUser.currentUser()?.saveInBackgroundWithBlock({ (success, error) -> Void in
if success {
PFUser.currentUser()?.email = email
PFUser.currentUser()?.saveEventually()
}
})
}
Poking around the source code for Parse server, there doesn't seem to be any public api to manually resend verification emails. However I was able to find 2 undocumented ways to access the functionality.
The first would be to use the internal UserController on the server (for instance from a Cloud function) like this:
import { AppCache } from 'parse-server/lib/cache'
Cloud.define('resendVerificationEmail', async request => {
const userController = AppCache.get(process.env.APP_ID).userController
await userController.resendVerificationEmail(
request.user.get('username')
)
return true
})
The other is to take advantage of an endpoint that is used for the verification webpage:
curl -X "POST" "http://localhost:5000/api/apps/press-play-development/resend_verification_email" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{ "username": "7757429624" }'
Both are prone to break if you update Parse and internals get changed, but should be more reliable than changing the users email and then changing it back.
We were setting emails to an empty string, but found that there was a race condition where 2 users would hit it at the same time and 1 would fail because Parse considered it to be a duplicate of the other blank email. In other cases, the user's network connection would fail between the 2 requests and they would be stuck without an email.
Now, with Parse 3.4.1 that I'm testing, you can do (for Javascript):
Parse.User.requestEmailVerification(Parse.User.current().get("email"));
BUT NOTE that it will throw error if user is already verified.
Reference:
http://parseplatform.org/Parse-SDK-JS/api/3.4.1/Parse.User.html#.requestEmailVerification
To resend the verification email, as stated above, you have to modify then reset the user email address. To perform this operation in secure and efficient way, you can use the following cloud code function:
Parse.Cloud.define("resendVerificationEmail", async function(request, response) {
var originalEmail = request.params.email;
const User = Parse.Object.extend("User");
const query = new Parse.Query(User);
query.equalTo("email", originalEmail);
var userObject = await query.first({useMasterKey: true});
if(userObject !=null)
{
userObject.set("email", "tmp_email_prefix_"+originalEmail);
await userObject.save(null, {useMasterKey: true}).catch(error => {response.error(error);});
userObject.set("email", originalEmail);
await userObject.save(null, {useMasterKey: true}).catch(error => {response.error(error);});
response.success("Verification email is well resent to the user email");
}
});
After that, you just need to call the cloud code function from your client code. From Android client, you can use the following code (Kotlin):
fun resendVerificationEmail(email:String){
val progress = ProgressDialog(this)
progress.setMessage("Loading ...")
progress.show()
val params: HashMap<String, String> = HashMap<String,String>()
params.put("email", email)
ParseCloud.callFunctionInBackground("resendVerificationEmail", params,
FunctionCallback<Any> { response, exc ->
progress.dismiss()
if (exc == null) {
// The function executed, but still has to check the response
Toast.makeText(baseContext, "Verification email is well sent", Toast.LENGTH_SHORT)
.show()
} else {
// Something went wrong
Log.d(TAG, "$TAG: ---- exeception: "+exc.message)
Toast.makeText(
baseContext,
"Error encountered when resending verification email:"+exc.message,
Toast.LENGTH_LONG
).show()
}
})
}
Related
I'm working on an NFT site in NextJS and trying to implement a redirect for the user after they successfully mint a token. Here's my mint code:
const mintToken = () => {
safeMint?.();
router.push('/success');
};
As you can see, after safeMint is called, I try to redirect to /success which is what happens. However, it redirects regardless of a successful mint, I want it to only redirect after the call to the smart contract succeeds. I've tried using callbacks and timeouts but nothing seems to work the way I've laid out above. Is there some way of getting or waiting for a success response before redirecting that I'm missing? Thanks!
Function return value is not available outside of EVM if you execute the function with a transaction.
You can wait for the transaction receipt. It contains the transaction status (success / revert), as well as event logs. Tx receipt is available only after the tx is included in a block.
Depending on your safeMint() implementation, it might mint tokens each time the transaction succeeds. But if your implementation allows for the function to succeed even without minting tokens, you might need to check the event logs to make sure that the NFT was really minted.
// transaction reverted
function safeMint() external {
require(failedCondition);
_mint(msg.sender, tokenId);
}
// transaction succeeded but no token was minted
function safeMint() external {
if (failedCondition) {
_mint(msg.sender, tokenId);
}
}
How to wait for the receipt with ethers:
const tx = await myContract.safeMint();
const txReceipt = await transaction.wait();
if (txReceipt.status) {
// not reverted
}
Docs:
https://docs.ethers.io/v5/api/providers/types/#providers-TransactionResponse
https://docs.ethers.io/v5/api/providers/types/#providers-TransactionReceipt
in safeMint function inside contract, you can return the tokenId (or you could return true)
const mintToken =async () => {
const result=await safeMint?();
if(result){
router.push('/success');
}
};
I have code which has remained essentially unchanged for months. The code extracted an email address and email password from an .env file and was used for a few difference cases. Whenever a single user signs up, they would receive an email; and whenever an owner signs many employees up, each of them would receive and email.
The email account we used to send these emails was a Gmail, and was created before the May 30th update and allowed less secure apps access. This allowed it to work sending an individual an email when they signed up, and provided there was a 1 second delay between each employee signed up, it was able to handle the en masse emailing as well.
The email account we use has since been changed, this new email was created after the May 30th update. I jumped through all of the extra hoops to enable less secured apps on this new account, so it sends individual sign up emails with no issue. However, as soon as I attempt to execute the en masse emails (even with the one second delay) I get the following error:
Error: Invalid login: 535-5.7.8 Username and Password not accepted.
Now, this really makes no sense because it is a copy and paste job of the email syntax that DOES work meaning this code below works...
try{
// Creates the Transporter
const transporter = nodemailer.createTransport({
service: "Gmail",
auth: {
user: `${process.env.SIGNUP_EMAIL}`,
pass: `${process.env.SIGNUP_PASS}`
}
})
// Creates the Mail Object
const mailOptions = {
from: `${process.env.SIGNUP_EMAIL}`,
to: `${actualEmail}`,
subject: `Thank you for joining the TOM Team!`,
text: `We have recieved your Account Signup and are please to welcome you to the TOM Experience!`
}
// Sends the mail
transporter.sendMail(mailOptions, (error, response) => {
if (error){
throw new Error('Something went wrong, please try again \n' + error)
}
})
} catch(error){
console.log(error)
}
But this code DOES NOT (breaks on the first attempt no matter how many others there are after)
try{
const transporter = nodemailer.createTransport({
service: "Gmail",
auth: {
user: `${process.env.SIGNUP_EMAIL}`,
pass: `${process.env.SIGNUP_PASS}`
}
})
// Creates the Mail Object
const mailOptions = {
from: `${process.env.SIGNUP_EMAIL}`,
to: `${actualEmail}`,
subject: `Thank you for joining the TOM Team!`,
text: `We have recieved your Account Signup and are please to welcome you to the TOM Experience! \nUpon recieving this email, you will have a new TOM Account for the mobile app. Please use this email, and the password "${passwordToUse}" to login and get started!`
}
// Sends the mail
transporter.sendMail(mailOptions, (error, response) => {
if (error){
console.log(error)
throw new Error('Something went wrong, please try again \n' + error)
}
})
}
catch(err){
console.log(err)
throw new Error(`Something went wrong emailing ${actualEmail}. Please check this is the proper address.`)
}
Is it just impossible to send gmails en masse now? Even with a 1 second delay? Even if not, why is the very first email failing? I truly have no idea why I'm facing these errors
I'm not sure why this is, but increasing the wait time from 1000 milliseconds to 1500, and then back to 1000 did the trick.
I'm not sure, maybe the new email just needed to get baby-stepped into sending multiple at a time.
I have Javascript App running under Node v8.11.2 which uses the Actions-On-Google library. I'm using the V2 API. I have account linking set up with Auth0 and am using the SignIn helper intent. Auth0 is set up to use Google, Facebook and Twitter.
The scopes I use are OPENID, OFFLINE_ACCESS, PROFILE and EMAIL.
Everything is working fine and when the User is authenticated I get an Access Token returned.
My question is, how do I get the Authentication Provider that was selected by the User so that I can use the Access Token correctly to retrieve profile elements such as the display name, email address etc??
The signin object passed to the Sign In Confirmation intent handler just contains the following regardless of the provider selected: -
{"#type":"type.googleapis.com/google.actions.v2.SignInValue","status":"OK"}
Any help greatly appreciated as I have a deadline and this is driving me a bit crazy now!
Thanks,
Shaun
If your question is about how to get the required information when you have your accessToken available then you could use what is shown in this answer.
In node this looks like that:
let link = "https://www.googleapis.com/oauth2/v1/userinfo?access_token="+accessToken;
return new Promise(resolve => {
request(link,(error, response, body) => {
if (!error && response.statusCode === 200) {
let data = JSON.parse(body);
let name = data.given_name ? data.given_name : '';
conv.ask(new SimpleResponse({
speech: "Hello "+ name + "!",
text: "Hello "+ name + "!"
}));
resolve();
} else {
console.log("Error in request promise: "+error);
resolve();
}
})
})
Everything you need should be in the data object.
Hope it helps.
I just want to have a function, which will be sending mail to different people.
I have written a Email Service, just like this:
public void SendEMail(EMail mail)
{
var body = JObject.FromObject(settings);
body.Merge(JObject.FromObject(mail));
GlobalTelemetry.TrackDependency("E-Mail Service", "Send", () =>
{
var result = AsyncHelper.RunSync(() => httpClient.PostAsync(azureFunctionUrl,
new StringContent(body.ToString(), Encoding.UTF8, "application/json")));
if (!result.IsSuccessStatusCode)
{
throw new InvalidOperationException(
$"E-Mail Service failed to send mails. Http Status {result.StatusCode}. Please check if the Url '{azureFunctionUrl}' is correct. Env: {Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}");
}
});
}
and by every other functions, which want to use this mail service, do just like this:
SendMail = mail =>
{
var fireAndForgetTask = Task.Run(() =>
{
try
{
eMailService.SendEMail(mail);
}
catch (Exception e)
{
Log.Fatal(e, $"Fail to send E-Mail to: '{mail.To}'");
}
});
};
it does work, but fireAndForgetTask is not used, I have no idea how to do it better.
Is it necessary to write a Email Job???
Some better suggestion is welcome :-)
I had a similar issue in my app for sending a password reset email, I did not want the delay of waiting for the email to send because if a hacker is trying to find out if an account exists for an email address the delay could indicate that the account does exist. So I wanted to fire and forget the email with no delay.
So I implemented a simple extension method named forget like this in a separate static class based on this so question:
public static void Forget(this Task task)
{
}
and then I send my email async and use that .Forget extension to not wait for it like this:
emailSender.SendPasswordResetEmailAsync(
Site,
model.Email,
sr["Reset Password"],
resetUrl).Forget();
note that I am not using "await" when I call this async method and the .Forget gets rid of the warning that would otherwise appear in Visual Studio when calling an async method without the await keyword
This is regarding Sendgrid incoming mail webhook, I have referred this URL SendGrid incoming mail webhook - how do I secure my endpoint, and got some idea how to go about this, but, as I am new to MVC / WebAPI, could anyone give me the controller method code snippet to catch the JSON format HTTP post and save to my application folder.
This is the solution I found after googling and with slight modifications:
[HttpPost, HttpGet]
[EnableCors(origins: "*", headers: "*", methods: "*")]
public async Task Post()
{
if (Request.Content.IsMimeMultipartContent("form-data"))
try
{
//To get complete post in a string use the below line, not used here
string strCompletePost = await Request.Content.ReadAsStringAsync();
HttpContext context = HttpContext.Current;
string strFrom = context.Request.Form.GetValues("from")[0];
string strEmailText = context.Request.Form.GetValues("email")[0];
string strSubject = context.Request.Form.GetValues("subject")[0];
//Not useful I guess, because it always return sendgrid IP
string strSenderIP = context.Request.Form.GetValues("sender_ip")[0];
}
catch (Exception ex)
{
}
}
I tried, retrieving the values as
String to = context.Request.Params["to"];
but, the value returned is not consistent, i.e. most of the times it is returning null and occasionally returns actual value stored in it.
If anyone have a better solution, please let me know.
Thank you
If for some reason ["to"] doesn't work for you, try to get ["envelope"] value,
context.Request.Form.GetValues("envelope")[0]
which looks like
{"to":["emailto#example.com"],"from":"emailfrom#example.com"}