OAuth 2, gSuite.gmail, Mail Plugin

I have been using the mail plugin in servoy to send outgoing email via gSuite/gmail. Currently I set the gSuite/gmail account to allow less secure apps and then authenticate with a gSuite/gmail username and password. This has worked great for many years.

gSuite will be turning off access to less secure apps (LSA) in two steps as follows
June 15, 2020 - Users who try to connect to an LSA for the first time will no longer be able to do so. This includes third-party apps that allow password-only access to Google calendars, contacts, and email via protocols such as CalDAV, CardDAV and IMAP. Users who have connected to LSAs prior to this date will be able to continue using them until usage of all LSAs is turned off.
February 15, 2021 - Access to LSAs will be turned off for all G Suite accounts.

Going forward authentication will have to be done with OAuth 2.0. I am wondering if the mail plug in will be modified in the near future to allow OAuth authentication. Alternatively if someone has a working example of how the current email plugin can be used with OAuth 2.0 (perhaps combining features of the OAuth plugin nd the Mail Plugin) to send email via gSuite or gmail that wouid be most appreciated.

Sincerely,

John McCann

From what I understand it comes down to this: you will need an oauth refresh token that is used as password and slightly different connection properties for this. It should work with these changes out of the box. Obviously, you will have to go through all the steps necessary for any oauth flow (setup your application for oauth, get a refresh token etc). I will have a look at that in January and see whether the plugin needs changes (the Java Mail libraries may need to be updated) and keep you updated.

:+1:t2:+1

Thanks so much Patrick. I look forward to you follow up post in early in 2020.

John McCann

The same is the case for Office 365: https://developer.microsoft.com/en-us/o … customers/

Today, we are announcing that on October 13th, 2020 we will stop supporting and retire Basic Authentication for Exchange Active Sync (EAS), Post Office Protocol (POP), Internet Message Access Protocol (IMAP), and Remote PowerShell (RPS) in Exchange Online. This means that new or existing applications using one or more of these API’s/protocols will not be able to use Basic Authentication when connecting to Office 365 mailboxes or endpoints and will need to update how they authenticate.

So for the mail and mailPro plugin AND the Exchange plugin, will stop working after 13 october.
They all can handle mail ONLY with username & password, which will not be supported later this year. :shock:

here is some info on how to implement oAuth for javamail: https://javaee.github.io/javamail/OAuth2

A short update on this. I have tested it with the mail plugin and the mail pro plugin.

The first part is OAuth standard procedure:

  1. you need to register your app with google via https://console.cloud.google.com/
  2. specifically, you need an OAuth 2.0 client ID, which can be created in “API & Services” / “Credentials”
  3. That will give you a client ID and secret

Once that is done, you need your user to authorize your app to use GMail, which can be done via the oauth plugin. The steps are roughly these:

plugins.oauth.serviceBuilder(CLIENT_ID)
		.clientSecret(CLIENT_SECRET)
		.callback(getAccessTokenCallback, 30)
		.scope('https://mail.google.com/')
		.build(plugins.oauth.OAuthProviders.GOOGLE)

CLIENT_ID and CLIENT_SECRET in the code above are the ones you received when registering your app.

In the callback method (getAccessTokenCallback) you will then get a plugins.oauth.OAuthService object when everything went OK and the user granted the rights you asked for. That object has a getAccessToken() method that returns the access token used as the password for sending emails. This one you will have to store for that user.

Once you climbed over that hurdle, you can do this with the MailPro plugin:

var smtpAcc = plugins.MailPro.SMTPAccount('smtp.gmail.com', 'from@gmail.com', accessToken);
smtpAcc.requiresAuthentication = true;
smtpAcc.useTLS = true;
smtpAcc.addSmtpProperty('mail.smtp.auth.mechanisms', 'XOAUTH2');
var msg = smtpAcc.createMessage('to@recipient.com', 'from@gmail.com', 'A test', 'My message')
var success = smtpAcc.sendMessage(msg);

For the standard mail plugin, something like this should work:

var props = [];
props.push("mail.smtp.ssl.enable=true");
props.push("mail.smtp.auth.mechanisms=XOAUTH2");
props.push("mail.smtp.host=smtp.gmail.com");
props.push("mail.smtp.auth=true");
props.push("mail.smtp.user=from@gmail.com");
props.push("mail.smtp.password=" + accessToken);
plugins.mail.sendMail('to@recipient.com', 'from@gmail.com', 'A test', 'My message', null, null, null, props);

I hope this helps. Let me know your findings.

One more remark: when testing this, you usually run off localhost:8080, which does not match any URL you registered with Google. To overcome this, you can edit the hosts file on windows and add something like

127.0.0.1 test.ruhsert.de

when test.ruhsert.de is the URL you entered when creating your client credentials. Then you need to run your NG Client from test.ruhsert.de:8080 instead of localhost:8080 and it should work (although showing a safety warning from google).

Hi all,

I’m following Patricks sample and I’m struggling to get it to work…

I’m getting my refresh token back from Google and trying to use it instead of a password.

Or do I need to go back to Google to exchange it for an access token?
Or does the mail plugin do this for me?

Has anyone successfully sent email via google using oauth2?
There may be something really simple that I’m missing…

Has anyone successfully sent email via google using oauth2?

Yes, I have when I tested that and posted the sample code above.

What goes wrong in your case? What error do you get? What plugin are you trying it with? Are you setting this mail.smtp.auth.mechanisms=XOAUTH2?

Hi Patrick,

I’m using Servoy’s mail plugin.

	var properties = [];
	properties.push("mail.smtp.ssl.enable=true");
	properties.push("mail.smtp.auth=true");
	properties.push("mail.smtp.auth.mechanisms=XOAUTH2");
	properties.push("mail.smtp.host=smtp.gmail.com");
	properties.push("mail.smtp.username=" + globals.myEmailAddress);
	properties.push("mail.smtp.password=" + globals.myGmailPassword); //actually an Oauth2 token
    //properties.push('mail.smtp.port=587');
    //properties.push('mail.smtp.starttls.enable=true');
	//
	
	var success = plugins.mail.sendMail(to, from, subject, msg,null,null,null,properties);

with this I get an error

Failed to send mail to<<recipient> 334 eyJzdGF0dXMiOiI0MDAiLCJzY2hlbWVzIjoiQmVhcmVyIiwic2NvcGUiOiJodHRwczovL21haWwuZ29vZ2xlLmNvbS8ifQ==

334 is an odd SMTP error.

I’m wondering whether the token I get back from Google is bad.

This my code for obtaining the token (may this is where I’m going wrong:)

/**
 * @properties={typeid:24,uuid:"F90B3954-3412-42F5-8B80-DBB1D5128E55"}
 */
function authorise_sending_email() {
	plugins.oauth.serviceBuilder(CLIENT_ID).clientSecret(CLIENT_SECRET).deeplink('google_oauth_callback').callback(google_oauth_callback, 30).scope('https://mail.google.com/').build(plugins.oauth.OAuthProviders.GOOGLE);
}

/**
 *
 * @properties={typeid:24,uuid:"7836A708-1111-4E6A-8B88-015BC5E18F80"}
 */
function google_oauth_callback(result, auth_outcome) {
	//plugins.dialogs.showInfoDialog('google_oauth_callback ' + result,'OK');

	//plugins.dialogs.showInfoDialog(auth_outcome,'OK');
	//plugins.dialogs.showInfoDialog(auth_outcome['code'],'OK');
	//plugins.dialogs.showInfoDialog(auth_outcome['scope'],'OK');

	var token = auth_outcome['code'];
	if (token) {
		var query = 'SELECT user_id FROM users WHERE user_name ILIKE ?';
		var args = [security.getUserName()];
		/** @type {JSFoundSet<db:/dbname/users>} */
		var fs = databaseManager.getFoundSet('dbname', 'users');
		if (fs.loadRecords(query, args) && fs.getSize() == 1) {
			var user = fs.getRecord(1);
			user.google_api_token = token;
			databaseManager.saveData();
			plugins.dialogs.showInfoDialog('Gmail','Successfully saved token for sending emails.', 'OK');
			forms.main.controller.show();
		}
		refreshUserEmailProperties();
	}
}

I use auth_outcome[‘code’] to get the token. If I use auth_outcome.getCode() I get null.
Also Google seems to call back twice. My deeplink comes back right away, while the google_oauth_callback fails after 30 seconds.

I can’t test this right now, but in my post above I say “getAccessToken()” should be used. If you type your auth_outcome parameter to plugins.oauth.OAuthService, you should see that method.

Hi Patrick,

I have taken a look at the Google OAuth Playground

https://developers.google.com/oauthplayground/

and what I get back when I check auth_outcome[‘code’] is in the exact same format as an authorisation code. And I’m trying to use it as a password which may be why I can’t send emails.
getAccessToken() does not return anything, even when I typecast the auth_outcome.

Is there a Servoy-way to fetch the access_token from Google, as in step 2 of the playground: ‘Exchange authorisation code for tokens’ or does Servoy do this for me?

It would be very good to see some working sample code for this.

Hi Patrick,

I appear to have solved the problem for now by using the way described in the webinar March 2019, using

	service = plugins.oauth.getOAuthService(plugins.oauth.OAuthProviders.GOOGLE,CLIENT_ID,CLIENT_SECRET,'https://mail.google.com/',null,'on_authorize');
	application.showURL(service.getAuthorizationURL());

I did not try this a first because of the depression notice:
Creates an OAuth service that can be used to obtain an access token and access protected data. This method will be deprecated in the following versions, the preferred way is plugins.oauth.serviceBuilder with a callback function.

patrick:
A short update on this. I have tested it with the mail plugin and the mail pro plugin.

The first part is OAuth standard procedure:

  1. you need to register your app with google via https://console.cloud.google.com/
  2. specifically, you need an OAuth 2.0 client ID, which can be created in “API & Services” / “Credentials”
  3. That will give you a client ID and secret

Once that is done, you need your user to authorize your app to use GMail, which can be done via the oauth plugin. The steps are roughly these:

plugins.oauth.serviceBuilder(CLIENT_ID)
	.clientSecret(CLIENT_SECRET)
	.callback(getAccessTokenCallback, 30)
	.scope('https://mail.google.com/')
	.build(plugins.oauth.OAuthProviders.GOOGLE)



CLIENT_ID and CLIENT_SECRET in the code above are the ones you received when registering your app.

In the callback method (getAccessTokenCallback) you will then get a plugins.oauth.OAuthService object when everything went OK and the user granted the rights you asked for. That object has a getAccessToken() method that returns the access token used as the password for sending emails. This one you will have to store for that user.

Once you climbed over that hurdle, you can do this with the MailPro plugin:



var smtpAcc = plugins.MailPro.SMTPAccount(‘smtp.gmail.com’, ‘from@gmail.com’, accessToken);
smtpAcc.requiresAuthentication = true;
smtpAcc.useTLS = true;
smtpAcc.addSmtpProperty(‘mail.smtp.auth.mechanisms’, ‘XOAUTH2’);
var msg = smtpAcc.createMessage(‘to@recipient.com’, ‘from@gmail.com’, ‘A test’, ‘My message’)
var success = smtpAcc.sendMessage(msg);




For the standard mail plugin, something like this should work:



var props = ;
props.push(“mail.smtp.ssl.enable=true”);
props.push(“mail.smtp.auth.mechanisms=XOAUTH2”);
props.push(“mail.smtp.host=smtp.gmail.com”);
props.push(“mail.smtp.auth=true”);
props.push(“mail.smtp.user=from@gmail.com”);
props.push(“mail.smtp.password=” + accessToken);
plugins.mail.sendMail(‘to@recipient.com’, ‘from@gmail.com’, ‘A test’, ‘My message’, null, null, null, props);




I hope this helps. Let me know your findings.

If you are using the serviceBuilder then the auth_outcome in your callback method is a fully configured service in case of success (so if result is true).
The service object doesn’t have a getCode method, but a getAccessToken() which returns the token you need to sign the requests.
However, I suggest you to use the createRequest or create(Get/Post/…)Request methods on the service because those also take care of the signing or refreshing the token in case it’s expired.
You can see more samples on how to use those methods on the OAuthService in the developer.

 function someFunction() {
          plugins.oauth.serviceBuilder("0lqd1s0aw...")		//client/application ID
			.clientSecret("bIk6163KHi...")		//client secret
			.defaultScope("email")				//ask permission to get the user email
	 		.state("secret123337")				//anti forgery session state, required by the Facebook api
			.deeplink("deeplink_method")		//OPTIONAL deeplink method name or last part of your redirect URL, see docs
	 									//if missing, a global method with the name 'deeplink_svy_oauth' will be generated
	 		.callback(callbackFunc, 30) 			//see function below, timeout is 30 seconds
	 		.build(plugins.oauth.OAuthProviders.FACEBOOK);
}
	 
function callbackFunc(result, auth_outcome) {
	  if (result)
	  {
	  		//SUCCESS
	  		var service = auth_outcome;
	 		application.output(service.getAccessToken()); // this is not really needed, but maybe you want to see what the access token is

                       //ACCESS SOME PROTECTED RESOURCE
	 		var response = service.executeGetRequest("https://graph.facebook.com/v2.11/me");
	 		if (response.getCode() == 200) {
	  			application.output(response.getBody());
	 			var json = response.getAsJSON();
	 			application.output("Name is "+json.name);
	 		}
			else {
	 		application.output('ERROR http status '+response.getCode());
			}
	 	else {
			//ERROR
			application.output("ERROR "+auth_outcome);//could not get access token, request timed out, etc..
		}
	  }
}

Google also provides a REST service to access a users’s emails with oauth:
https://developers.google.com/gmail/api … ssages/get

Yes, I see you can also create drafts and send them. The downside is that you may have to create separate code for those using Office365 and other services. The mail plugin abstracts that away. Since sending emails via Google and Office365 will soon require oAuth, it would be useful if Servoy published working examples for these two leading services. Being able to access your Facebook profile is fun, but sending email is critical for most Servoy systems.

+1 Sending email is critical in our applications.

With Google you can use an app password (allso when Basic Auth is disabled). Would that work with the Servoy SMTP send email?

I was able to get the OAuth plugin working with Office365 and send email with the standard plugin using the token. The mail properties look like this for my instance

var properties = new Array()
properties.push('mail.smtp.host=smtp.office365.com')
properties.push('mail.smtp.port=587')
properties.push('mail.smtp.auth=true')
properties.push('mail.transport.protocol=smtp')
properties.push('mail.smtp.starttls.enable=true')
properties.push('mail.smtp.auth.mechanisms=XOAUTH2')
properties.push('mail.smtp.username=' + fromEmail)
properties.push('mail.smtp.password=' + accessToken)
properties.push('security.require-ssl=true')

For Google, those might change slightly. Servoy mail plugin uses standard JavaMail and there are some notes on using OAuth with JavaMail here: https://javaee.github.io/javamail/OAuth2

MailPro is also using standard java mail, so it basically works the same.