I've been working on a java application to use my Gmail connection to send an email but am hitting a wall.
I've found out that I need to use OAuth which the current implementation of JavaMail uses, but I haven't really found a clear cut guide to how to do this. I'm an amateur programmer and well, kind of a dummy when it comes to comprehension (I'm working really hard for some C's in school, lol) but I'm trying to learn here.
Most of what I've put together has been culled from other things I've found, trying to get something working, which it kind of is. But the problem that I'm hitting is one of two things.
Either I can't get authorized, then I get an email from Google saying an app was trying to access my email without complying with modern security standards, or that I need to log in via a web browser.
From what I've found, a few years back Google changed their security which is why we need to use OAuth now, which, luckily JavaMail supports, but I can't get it working. And a lot of the help I've seen here on Stack Overflow or other places is nearly 5 years old.
Right now, I'm trying to make a java program in eclipse to just send an email, and I'm using github.com/google/gmail-oauth2-tools
To try and make the OAuth token, but there's an error
Error Line
The method in OAuth2SaslClientFactory
public SaslClient createSaslClient(String[] mechanisms,
String authorizationId,
String protocol,
String serverName,
Map<String, ?> props,
CallbackHandler callbackHandler) {
boolean matchedMechanism = false;
for (int i = 0; i < mechanisms.length; ++i) {
if ("XOAUTH2".equalsIgnoreCase(mechanisms[i])) {
matchedMechanism = true;
break;
}
}
if (!matchedMechanism) {
logger.info("Failed to match any mechanisms");
return null;
}
return new OAuth2SaslClient((String)props.get(OAUTH_TOKEN_PROP), callbackHandler);
}
The code from OAuth2SASLClient
public OAuth2SaslClient(String oauthToken, CallbackHandler callbackHandler) {
this.oauthToken = oauthToken;
this.callbackHandler = callbackHandler; }
That return line is saying the constructor can't handle that, but the constructor in OAuth2SaSLClient.java is actually set up to have a (String, Callback) parameter.
If somebody has something quick and easy that works, I'd love to see it,even though it appears that this needs to use a specific way of creating an OAuth token.
I can post my code if it helps, but I need to clean it up first, it's sort of a first draft / messy collage of things I've tried to get working.
Or a javamail / Oauth tutorial for dummies.
The end goal here is to tie this to a program that can send out a quick notification when something happens. Which I can't seem to do.
Mixing OAuth and javamail won't work, you need to choose your API: either the Gmail v4 API (with OAuth) or the javamail API. You said you are just trying to send emails from your own account, so I'd suggest you use the javamail IMAP API. When going through the javamail API you don't need Oauth at all.
For example:
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.port", "587");
Session session = Session.getInstance(props, new javax.mail.Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(GOOGLE_USERNAME, password);
}
});
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(FROM_EMAIL, FROM_NAME));
// rest of the email settings
If you needed to send emails on behalf of other people, or need to do more advanced mail operations such as create a Gmail draft, then the Gmail API would be the proper approach.
Note: if you are going to use the Gmail API, be sure to use the newer Gmail API: https://developers.google.com/gmail/api/quickstart/java
The old Google Data API is deprecated
Related
I get this error when I try to send a mail using JavaMail API. I am sure that the username and password are 100% correct. The Gmail account which I'm connecting is an older account, because they say it takes time for it to work with new accounts.
DEBUG SMTP RCVD: 535-5.7.1 Username and Password not accepted. Learn more at
535 5.7.1 http://mail.google.com/support/bin/answer.py?answer=14257 x35sm3011668
wfh.6
javax.mail.SendFailedException: Sending failed;
nested exception is:
javax.mail.AuthenticationFailedException
at javax.mail.Transport.send0(Transport.java:218)
at javax.mail.Transport.send(Transport.java:80)
at Main.(Main.java:41)
at Main.main(Main.java:51)
and this is my code:
import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;
public class Main
{
String d_email = "abc#gmail.com",
d_password = "pass",
d_host = "smtp.gmail.com",
d_port = "465",
m_to = "abc#gmail.com",
m_subject = "Testing",
m_text = "testing email.";
public Main()
{
Properties props = new Properties();
props.put("mail.smtp.user", d_email);
props.put("mail.smtp.host", d_host);
props.put("mail.smtp.port", d_port);
props.put("mail.smtp.starttls.enable","true");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.debug", "true");
props.put("mail.smtp.socketFactory.port", d_port);
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback", "false");
SecurityManager security = System.getSecurityManager();
try
{
Authenticator auth = new SMTPAuthenticator();
Session session = Session.getInstance(props, auth);
session.setDebug(true);
MimeMessage msg = new MimeMessage(session);
msg.setText(m_text);
msg.setSubject(m_subject);
msg.setFrom(new InternetAddress(d_email));
msg.addRecipient(Message.RecipientType.TO, new InternetAddress(m_to));
Transport.send(msg);
}
catch (Exception mex)
{
mex.printStackTrace();
}
}
public static void main(String[] args)
{
Main blah = new Main();
}
private class SMTPAuthenticator extends javax.mail.Authenticator
{
public PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication(d_email, d_password);
}
}
}
I had same issue :
I refer this link, I have followed below steps it worked for me.
By default Gmail account is highly secured. When we use gmail smtp from non gmail tool, email is blocked. To test in our local environment, make your gmail account less secure as
Login to Gmail.
Access the URL as https://www.google.com/settings/security/lesssecureapps
Select "Turn on"
The given code snippet works fine on my Gmail account, so this problem lies somewhere else. Did you follow the link given in the error message? It contains the following hints:
Make sure that you've entered your full email address (e.g. username#gmail.com)
Re-enter your password to ensure that it's correct. Keep in mind that passwords are case-sensitive.
Make sure your mail client isn't set to check for new mail too often. If your mail client checks for new messages more than once every 10 minutes, your client might repeatedly request your username and password.
Especially the last point is important. Google is very strict in this. If you're trying to connect Gmail for example more than 10 times in a minute programmatically, then you may already get blocked. Have a bit of patience, after some time it will get unblocked.
If you'd like more freedom in sending mails, I recommend to look for a dedicated mail host or to setup your own mail server, such as Apache James or Microsoft Exchange. I've already answered this in detail in one of your previous questions.
I encountered the exact same problem, for me the reason is I have turned on 2-step verification on my gmail account.
After generating a new application-specific password and use that in my java application, this "535 5.7.1" issue is gone.
You can generate a new application-specific password following this official google guide.
I faced the same problem although my username and password were correct, but after some research, I was able to send email through applications by enabling the Less secure app access option on my Gmail account. You can find this feature under security option in the left menu.
Related links:
I can't sign in to my email client
Enable access to less secure applications
Now, Google not supported Less Secure Apps so we are getting this issue.
You follow bellow steps to resolve your Authentication issue during send mail using SMTP.
1 - Enable 2-step Verification in your mail
2 - Once you enabled above, you will able to see App Passwords option in same Security tab
3 - Now generate App Password for Other(Custom)
4 - Use this generated password in place of your gmail password in your code.
Enjoy :)
I have the same error message and this is how I have solved the issue,
Create an app password: here is how we can generate an app password,
1. Visit your App passwords page. You may be asked to sign in to your Google Account.
2. At the bottom, click Select app and choose the app you’re using.
Click Select device and choose the device you’re using.
3. Select Generate.
4. Follow the instructions to enter the App password (the 16 character code in the yellow bar) on your device.
5. Select Done.
I worked for a Spring boot app and I get the app password say, sadsadaffferere for the email address, xyz#gmail.com. So, I need to configure the app properties like the following,
# the email settings
# ------------------
spring.mail.host=smtp.gmail.com
spring.mail.username=xyz#gmail.com
spring.mail.password=sadsadaffferere
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.socketFactory.port=465
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
spring.mail.properties.mail.smtp.socketFactory.fallback=false
support.email=xyz#gmail.com
Everything works fine afterwards
You need to turn on the 'less secure apps allowed' in gmail settings.
If the same credential was working earlier and it stopped working then the primary reason for this problem is password mismatch/changed on gmail client and not updated in Jenkins or other CI server. If that is not the case then check for reasons mentioned by #BalusC
i turned off antivirus and enabled less secure app in gmail, but now google has disabled this option, for that to work you need to do two steps verification and genetare 16 digit password and paste in application.properties replace this 16 digit password.
I have a Spring Boot application that sends emails. For any action that requires notification, a Mail instance is created with status PENDING in the database and a job is run to send the pending emails every minute. The status of the Mail instances are set as PENDING, SENT or FAILED.
try {
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", host);
props.put("mail.smtp.port", port);
props.put("mail.smtp.from", myEmailAddress);
props.put("mail.smtp.timeout", 2000);
Session session = Session.getInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
MimeMessage message = new MimeMessage(session);
MimeMultipart content = getContent(mail, message, username);
message.setContent(content);
Transport.send(message);
mail.setStatus(MailStatus.SENT);
mailService.save(mail);
} catch (MailConnectException mce) {
mail.setStatus(MailStatus.FAILED);
mailService.save(mail);
} catch (Exception e) {
// other actions
}
Now, this works fine if a valid email id is provided. But when the receiving email address is a non-existent one like somerandomname#gmail.com, there are no exceptions thrown. From what I read from similar questions in SO and elsewhere, I understand that mail sending is an asynchronous process and hence there is no way to determine that a given email is existing or not (or other issues like Inbox full). That is why after the Transport.send(message); statement, the mail.setStatus(MailStatus.SENT); statement will always be executed irrespective of the email address being present. Later the mail will actually be attempted to be sent and I get an email in myEmailAddress with content like the following:
The response from the remote server was: 550 5.1.1
somerandomname#gmail.com User unknown
Okay, accepted until this point. But now I need a way to alert the user that the email couldn't be sent because they entered an invalid email so that they can update their email. More specifically, I need to set the status of the Mail instance to FAILED. What is the best way to achieve this?
If you use a service like AWS SES to send your mail, you can receive notifications about bounces (and complaints) either simply as notifications for a human to read, or as programmatic triggers that could be used to call an alert endpoint on your spring boot server, for example through AWS Lambda. There are other services that may be able to do the same, such as SendGrid.
Your Spring Boot application would have to retrieve the notifications (an email itself) from your inbox corresponding to the sender address of the initial email. There are various options how to achieve this:
Use a service with provides callbacks (triggers) for incoming messages as mentioned by Benjamin
Poll your inbox using a standard protocol like POP or IMAP
Hint for the second option: https://docs.spring.io/spring-integration/reference/html/mail.html#mail-inbound
I want to have LinkedIn authentication in my website. Their API returns the desired information, the create account function is working. However, I seem to have some problems when I try to login on the site.
It seems that I get a UserEmailAddressException when I call the LoginUtil.login method.
at com.liferay.portal.service.impl.UserLocalServiceImpl.authenticate(UserLocalServiceImpl.java:2480).
It fails at
if (authType.equals(CompanyConstants.AUTH_TYPE_EA)) {
if (!Validator.isEmailAddress(login)) {
throw new UserEmailAddressException();
}
}
Here is my code :
boolean rememberMe = true;
String authType = CompanyConstants.AUTH_TYPE_EA;
try {
LoginUtil.login(request, response,
String.valueOf(user.getUserId()), user.getPassword(), rememberMe, authType);
}
catch (UserEmailAddressException ueae) {
ueae.printStackTrace();
}
The users authenticate via email address, so I guess that should be the correct authentication type?
I have added company.security.auth.type=emailAddress in portal-ext.properties, but I get the same error as without this setting.
Because Liferay documentation is unsatisfying, I would like to know how to do a proper call to the login() function so that my user will login with its LinkedIn account.
It's hard to answer this question from the amount of code that you give.
First of all: About documentation. Judging by the use of LoginUtil, you seem to be modifying Liferay's internal code in order to implement your functionality. This is an internal API that is not guaranteed to be stable and will most likely be documented last (the API documentation has improved a lot, but it's mostly about the external, public API).
You might want to look into the implementation of ServletFilters that Liferay uses for implementing other external single sign on systems. Many customers/users have implemented these successfully (I haven't looked at the state of that documentation though, but there are several SSO implementations that you can find)
Further, it will probably help, which email address is supposed to be invalid - from your code it looks like you're calling with user.getUserId() (this is numeric), but you state that you demanded the login to be through email.
Lastly, if you have configured the login method through the UI, it is saved to the database - and that setting would win. So please check ControlPanel/Portal/Portal Settings/Authentication/"How do users authenticate?" to make sure that the setting is actually asking for the email address.
I try to send a message in a grails application. I use Java code with this problem tho, here is my code
SimpleMailMessage message_ref = new SimpleMailMessage();
JavaMailSenderImpl sender_ref = new JavaMailSenderImpl();
sender_ref.setHost("smtp.gmail.com")
sender_ref.setUsername("testAccount#googlemail.com")
sender_ref.setPassword("topsecret")
message_ref.setTo("testRecipient#gmx.de")
message_ref.setSubject("Hello there")
message_ref.setText("How are you")
sender_ref.send(message_ref)
I'm getting the following exception:
SMTPSendFailedException: 530 5.7.0 Must issue a STARTTLS command first
I found a similar problem here on stackoverflow here
Must issue a STARTTLS command first. Sending email with Java and Google Apps
but it didn't help me cause he used an different approach.
Can somebody tell me what's wrong? I'm expecting the error is not in the code but in some configuration file and this is where my knowledge edges.
Quoting from the Grails mail plugin docs:
grails {
mail {
host = "smtp.gmail.com"
port = 465
username = "youracount#gmail.com"
password = "yourpassword"
props = ["mail.smtp.auth":"true",
"mail.smtp.socketFactory.port":"465",
"mail.smtp.socketFactory.class":"javax.net.ssl.SSLSocketFactory",
"mail.smtp.socketFactory.fallback":"false"]
} }
I can't help you much, but your problem is basically that you need to use SSL to communicate with the server. Google does not allow plain-text communication for a lot of good reasons. I don't know much about grails, but I assume it has some sort of ssl-support. If it does not, you're probably better off doing it in javax.mail.
StartTLS is just a text-command you send to the smtp-server to explicitly start secure communications.
Properties properties = new Properties();
properties.put("mail.smtp.host", smtpHost);
properties.put("mail.smtp.port", smtpPort);
properties.put("mail.smtp.starttls.enable", "true");
properties.put("mail.user", userName);
properties.put("mail.password", password);
I'm using the javax.mail system, and having problems with "Invalid Address" exceptions. Here's the basics of the code:
// Get system properties
Properties props = System.getProperties();
// Setup mail server
props.put("mail.smtp.host", m_sending_host);
// Get session
Session session = Session.getDefaultInstance(props, new Authenticator(){
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(m_sending_user, m_sending_pass);
}
});
// Define message
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(m_sending_from));
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(vcea.get(i).emailaddr));
message.setSubject( replaceEnvVars(subject) );
message.setText(replaceEnvVars(body));
// Send message
try {
Transport.send(message);
} catch (Exception e){
Log.Error("Error sending e-mail to addr (%s): %s",
vcea.get(i).emailaddr, e.getLocalizedMessage() );
}
The issue is that the above code does work, sometimes. But for some e-mail addresses that I know to be valid (because I can send to them via a standard e-mail client), the above code will throw an "Invalid Address" exception when trying to send.
Any clues or hints would be greatly appreciated.
--Update: problem with authentication.
Ok, here's what I've discovered was going on. When receiving e-mail, the code above correctly sets up authentication and the Authenticator.getPasswordAuthentication() callback is actually invoked.
Not so when sending e-mail. You have to do a bit more. Add this:
// Setup mail server
props.put("mail.smtp.host", m_sending_host);
props.put("mail.smtp.auth", "true");
which will force the javax.mail API to do the login authentication. And then use an actual Transport instance instead of the static .send() method:
Transport t = session.getTransport(m_sending_protocol);
t.connect(m_sending_user, m_sending_pass);
...
// Send message
try {
t.sendMessage(message, message.getAllRecipients());
} catch (Exception e){
Without forcing the authentication, the mail server saw me as an unauthorized relay, and just shut me down. The difference between the addresses that "worked" and the addresses that didn't was that the ones that "worked" were all local to the mail server. Therefore, it simply accepted them. But for any non-local "relay" addresses, it would reject the message because my authentication information hadn't been presented by the javax.mail API when I thought it would have.
Thanks for the clues to prompt me to look at the mail server side of things as well.
--Update: problem with authentication.
Ok, here's what I've discovered was going on. When receiving e-mail, the code above correctly sets up authentication and the Authenticator.getPasswordAuthentication() callback is actually invoked.
Not so when sending e-mail. You have to do a bit more. Add this:
// Setup mail server
props.put("mail.smtp.host", m_sending_host);
props.put("mail.smtp.auth", "true");
which will force the javax.mail API to do the login authentication. And then use an actual Transport instance instead of the static .send() method:
Transport t = session.getTransport(m_sending_protocol);
t.connect(m_sending_user, m_sending_pass);
...
// Send message
try {
t.sendMessage(message, message.getAllRecipients());
} catch (Exception e){
Without forcing the authentication, the mail server saw me as an unauthorized relay, and just shut me down. The difference between the addresses that "worked" and the addresses that didn't was that the ones that "worked" were all local to the mail server. Therefore, it simply accepted them. But for any non-local "relay" addresses, it would reject the message because my authentication information hadn't been presented by the javax.mail API when I thought it would have.
Thanks for the clues to prompt me to look at the mail server side of things as well.
I would change the call to InternetAddress to use the "strict" interpretation and see if you get further errors on the addresses you are having trouble with.
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(vcea.get(i).emailaddr, true ));
// ^^^^ turns on strict interpretation
Javadoc for InternetAddress constructor
If this fails, it will throw an AddressException which has a method called getPos() which returns the position of the failure (Javadoc)
A good hint for those using the ssl encryption in the smtp configuration, you should enable it by specifying the property mail.smtp.ssl.enable, as shown below:
props.put("mail.smtp.ssl.enable", "true");
Otherwise it can lead to similar problems as described above.
Try this:
String to="stackoverflow#so.com";
String cc="one#mail.com,two#mail.com"; //The separator ',' works good
message.setRecipients(Message.RecipientType.TO,new InternetAddress[] {
new InternetAddress(to) }); // This is only one mail
InternetAddress[] addr = parseAddressList(cc); //Here add all the rest of the mails
message.setRecipients(Message.RecipientType.CC,addr);
Sorry for my english. It's not good.
This seems to me like a problem that happened at my work.
If the code you are showing is being concurrent, then using directly System.getProperties
could be a problem, because the host value you put on them can be overwritten by next request, while still keeping the user and password from the overwritten host.
In our case, we solved that using a clone of the System.getProperties() hashtable.
Hope that helps (this problem was really hard to track).