Today I set up mailgun to enable password reset and verification emails on my parse-server.
After hour of troubleshooting why verification email is not working, I tried password reset and discovered that password reset was working fine.
I have tried to send a verification email to new users and old users without luck. Mailgun adapter setup should be fine, as it can send the password reset emails. At the moment I cannot find a solution on the verification email.
The server is running on Bluemix (similar to Heroku). The following environmental variables are set (I do not list everything as it includes keys and since reset password is working, they must be set correctly):
EMAIL_VERIFY_TOKEN_VALIDITY_DURATION = 7200
VERIFY_USER_EMAIL = true
My index.js file:
'use strict';
// Required Libs
require('dotenv').config()
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var express = require('express');
var ParseServer = require('parse-server').ParseServer;
var http = require('http');
// Start Express
var app = express();
// Validate Keys
if (!process.env.APP_ID) {
throw 'Please apply the Application ID from Parse.com';
}
if (!process.env.MASTER_KEY) {
throw 'Please apply the Master Key from Parse.com';
}
if (process.env.DATABASE_URI) {
var databaseUri = process.env.DATABASE_URI;
} else if (process.env.VCAP_SERVICES) {
var vcapServices = JSON.parse(process.env.VCAP_SERVICES);
const pattern = /mongo/i;
for (var i = 0; i < vcapServices['user-provided'].length; i++) {
if (vcapServices['user-provided'][i].name.search(pattern) >= 0 ||
vcapServices['user-provided'][i].credentials.uri.search(pattern) >= 0) {
var databaseUri = 'mongodb://' +
vcapServices['user-provided'][i].credentials.user +
':' + vcapServices['user-provided'][i].credentials.password +
'@' + vcapServices['user-provided'][i].credentials.uri;
break;
}
}
} else {
throw 'Please provide DATABASE_URI to an instance of MongoDB or deploy to Bluemix with a Compose MongoDB service';
}
// Server Location
var port = process.env.VCAP_APP_PORT || process.env.PORT || 1337;
var host = process.env.VCAP_APP_HOST || 'localhost';
var mountPath = process.env.PARSE_MOUNT || '/';
//var serverUrl = ((process.env.HTTPS) ? 'https://' : 'http://') + host + ':' + port + mountPath;
var publicServerUrlRoot = process.env.PUBLIC_SERVER_URL || host
var publicServerUrl = ((process.env.HTTPS) ? 'https://' : 'http://') + publicServerUrlRoot + mountPath;
// Specify the connection string for your mongodb database
// and the location to your Parse cloud code
var parseConfig = {
databaseURI: databaseUri,
appId: process.env.APP_ID,
masterKey: process.env.MASTER_KEY,
cloud: process.env.CLOUD_CODE_MAIN || __dirname + '/cloud/main.js',
serverURL: publicServerUrl,
// Setup email verification
verifyUserEmail: process.env.VERIFY_USER_EMAIL || true,
emailVerifyTokenValidityDuration: process.env.EMAIL_VERIFY_TOKEN_VALIDITY_DURATION || 2 * 60 * 60,
publicServerURL: publicServerUrl,
appName: process.env.APP_NAME,
emailAdapter: {
module: 'parse-server-simple-mailgun-adapter',
options: {
// The address that your emails come from
fromAddress: process.env.DEFAULT_FROM_ADDRESS,
// Your domain from mailgun.com
domain: process.env.MAILGUN_DOMAIN,
// Your API key from mailgun.com
apiKey: process.env.MAILGUN_API_KEY,
}
}
};
// Optional Keys
if (process.env.FILE_KEY) {
// String
parseConfig.fileKey = process.env.FILE_KEY;
}
if (process.env.CLIENT_KEY) {
// String
parseConfig.clientKey = process.env.CLIENT_KEY;
}
if (process.env.JS_KEY) {
// String
parseConfig.javascriptKey = process.env.JS_KEY;
}
if (process.env.REST_KEY) {
// String
parseConfig.restAPIKey = process.env.REST_KEY;
}
if (process.env.DOTNET_KEY) {
// String
parseConfig.dotNetKey = process.env.DOTNET_KEY;
}
if (process.env.ALLOW_CLIENT_CLASS_CREATION) {
// Boolean
parseConfig.allowClientClassCreation = process.env.ALLOW_CLIENT_CLASS_CREATION;
}
if (process.env.ENABLE_ANONYMOUS_USERS) {
// Boolean
parseConfig.enableAnonymousUsers = process.env.ENABLE_ANONYMOUS_USERS;
}
if (process.env.OAUTH) {
// Object: https://github.com/ParsePlatform/parse-server/wiki/Parse-Server-Guide#oauth
parseConfig.oauth = process.env.OAUTH;
}
if (process.env.FACEBOOK_APP_IDS) {
// Array
parseConfig.facebookAppIds = process.env.FACEBOOK_APP_IDS;
}
// Create Parse Server instance
var api = new ParseServer(parseConfig);
// Serve the Parse API on the / URL prefix
app.use(mountPath, api);
// And listen to requests
app.listen(port, function() {
//console.log('parse server url ' + serverUrl);
console.log('parse public server url ' + publicServerUrl);
console.log('parse-server running on port ' + port + '.');
});
require("cf-deployment-tracker-client").track();
To verify the user email I use two methods:
I have anonymous users enabled and I have subclassed PFSignUpViewController. The only thing the subclassing does is to changed the colors and fonts. The server side is set to verify user emails, so an email should be sent when a user is signed up automatically.
On sign up I get the following on the server log:
RTR/1kitespotterparseserver.mybluemix.net - [17/10/2016:08:27:35.196 +0000] "POST /parse/users HTTP/1.1" 201 85 116 "-" "KiteSpotter/623 CFNetwork/758.4.3 Darwin/15.5.0" 169.54.202.26:49150 x_forwarded_for:"37.6.22.73" x_forwarded_proto:"https" vcap_request_id:58e37dd1-42ac-4c08-4704-44983ab869fb response_time:0.429154539 app_id:9fe504a4-346d-4c67-a268-ef61a25cc006 x_global_transaction_id:"3652227649"
Unfortunately no email is coming and nothing is logged on mailgun. So the server never reaches mailgun.
To resent verification email to the user I use the following code (on the client side):
class func resendVerificationEmail(_ block: @escaping (_ success: Bool) -> Void) {
if let email = PFUser.current()?.email {
PFUser.current()?.email = email+".verify"
PFUser.current()?.saveInBackground(block: { (success, error) -> Void in
if success {
Log.info?.message("Verify Email. Temp email saved.")
PFUser.current()?.email = email
PFUser.current()?.saveInBackground(block: { (success, error) in
if success {
Log.info?.message("Verify Email. New email saved.")
} else {
Log.error?.message("Verify Email. New email save failed.\n\(error?.localizedDescription)")
}
block(success)
})
} else {
Log.error?.message("Verify Email. Temp email save failed.")
block(success)
}
})
}
}
--
class func verifyEmailAction(_ notifyVC: UIViewController?) -> UIAlertAction {
let emailVerificationAction = UIAlertAction(title: "Verify Email", style: UIAlertActionStyle.default) { (action) in
ParseLoginHelper.resendVerificationEmail({ (success) -> Void in
let alertController: UIAlertController
if success {
alertController = UIAlertController(title: "Verification Email Sent", message: "Sent to \(StringOrEmpty(PFUser.current()?.email))\nPlease check your emails", preferredStyle: .alert)
} else {
alertController = UIAlertController(title: "Verification Email Failed", message: nil, preferredStyle: .alert)
}
let OKAction = UIAlertAction(title: "Dismiss", style: .default, handler: nil)
alertController.addAction(OKAction)
notifyVC?.present(alertController, animated: true, completion: nil)
})
}
return emailVerificationAction
}
Once this is sent to the parse server the following appears on the server log:
RTR/0kitespotterparseserver.mybluemix.net - [17/10/2016:08:30:14.104 +0000] "PUT /parse/classes/_User/clCOWBsNLz HTTP/1.1" 200 72 40 "-" "KiteSpotter/623 CFNetwork/758.4.3 Darwin/15.5.0" 169.54.202.27:33533 x_forwarded_for:"37.6.22.73" x_forwarded_proto:"https" vcap_request_id:4c3e8056-9d70-4b1d-5c15-8a97c616f5ad response_time:0.449085887 app_id:9fe504a4-346d-4c67-a268-ef61a25cc006 x_global_transaction_id:"2500672239" 2016-10-17T11:30:14.554+0300
RTR/0kitespotterparseserver.mybluemix.net - [17/10/2016:08:30:14.959 +0000] "PUT /parse/classes/_User/clCOWBsNLz HTTP/1.1" 200 37 40 "-" "KiteSpotter/623 CFNetwork/758.4.3 Darwin/15.5.0" 169.54.202.27:33640 x_forwarded_for:"37.6.22.73" x_forwarded_proto:"https" vcap_request_id:3bbd5653-05f3-46ce-79ad-9772741fbdcf response_time:0.631187244 app_id:9fe504a4-346d-4c67-a268-ef61a25cc006 x_global_transaction_id:"3241831095"
On the iOS app, I get a pop up with "Verification Email Sent" and the following log:
2016-10-17 11:30:14.858 am EEST | INFO | ParseLogin.swift:51 - Verify Email. Temp email saved.
2016-10-17 11:30:15.953 am EEST | INFO | ParseLogin.swift:55 - Verify Email. New email saved.
Again nothing is logged on mailgun.
This was a trick that was working fine on parse.com.
If I request a password reset for the user. This is done from the parse sign up page. I get the following:
Server Log
RTR/1kitespotterparseserver.mybluemix.net - [17/10/2016:08:38:03.159 +0000] "POST /parse/requestPasswordReset HTTP/1.1" 200 37 2 "-" "KiteSpotter/623 CFNetwork/758.4.3 Darwin/15.5.0" 169.54.202.26:52006 x_forwarded_for:"37.6.22.73" x_forwarded_proto:"https" vcap_request_id:f37a23a9-17eb-473e-4439-dd6b3f8f2176 response_time:0.140150335 app_id:9fe504a4-346d-4c67-a268-ef61a25cc006 x_global_transaction_id:"2067630247"
Client Log
Nothing
Mailgun Log
10/17/16 04:38 AM Delivered: postmaster@mg.paz-labs.com → kitespotter2@paz-labs.com 'Password Reset for KiteSpotter'
10/17/16 04:38 AM Accepted: postmaster@mg.paz-labs.com → kitespotter2@paz-labs.com 'Password Reset for KiteSpotter'
And an email with instruction on how to reset my password.
The solution was very simple. It was a typo on the index.js
file.
verifyUserEmail: process.env.VERIFY_USER_EMAIL || true,
should be
verifyUserEmails: process.env.VERIFY_USER_EMAIL || true,
In my opinion, I should be getting a compile or runtime error on the first one which I did not get.