17

I need to send an email through my app using say the javamail API (any other mailing service if available will also do). the problem is i do not want to ask the user his username and password.

1) Is it possible to use OAuth 2.0 with JavaMail API/ any other mail api

2) how to get OAuth Token ??

3) Is there a sample code available on the net

Thanks in advance.

PS: I have never ever worked with mailing services/SMTP requests.

Manan Sharma
  • 631
  • 2
  • 14
  • 28
SKen
  • 612
  • 1
  • 10
  • 20
  • The SASL implementation in Javamail has non-android dependencies. See my answer here: http://stackoverflow.com/a/11830319/50913 – Malachi Sep 28 '12 at 10:36
  • i got to that road block and have already seen your post which you have linked... finally ended doing it the good old way using user credentials. – SKen Sep 28 '12 at 12:01

1 Answers1

28

I researched this for some days and I found a solution that is working for me at the moment. I get the oauth2 token from the android AccountManager and then send the email via SMTP using JavaMail. The idea is based on the Java example here http://code.google.com/p/google-mail-oauth2-tools/wiki/JavaSampleCode and on this java Xoauth example here http://google-mail-xoauth-tools.googlecode.com/svn/trunk/java/com/google/code/samples/xoauth/XoauthAuthenticator.java

There's no working SASL implementation in JavaMail for Android and using asmack wasn't working so I didn't use SASL and I issued the command directly like in the Xoauth example above.

I get the token from the acount manager like this

AccountManager am = AccountManager.get(this);
Account me = ...; //You need to get a google account on the device, it changes if you have more than one
am.getAuthToken(me, "oauth2:https://mail.google.com/", null, this, new OnTokenAcquired(), null);

private class OnTokenAcquired implements AccountManagerCallback<Bundle>{
    @Override
    public void run(AccountManagerFuture<Bundle> result){
        try{
            Bundle bundle = result.getResult();
            token = bundle.getString(AccountManager.KEY_AUTHTOKEN);

        } catch (Exception e){
            Log.d("test", e.getMessage());
        }
    }
}

If it works, you have an oauth2 token in token. I use the token in this code

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Provider;
import java.security.Security;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.URLName;
import javax.mail.Message;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import android.util.Log;

import com.sun.mail.smtp.SMTPTransport;
import com.sun.mail.util.BASE64EncoderStream;

public class GMailOauthSender {
private Session session;


public SMTPTransport connectToSmtp(String host, int port, String userEmail,
        String oauthToken, boolean debug) throws Exception {

    Properties props = new Properties();
    props.put("mail.smtp.starttls.enable", "true");
    props.put("mail.smtp.starttls.required", "true");
    props.put("mail.smtp.sasl.enable", "false");
    session = Session.getInstance(props);
    session.setDebug(debug);


    final URLName unusedUrlName = null;
    SMTPTransport transport = new SMTPTransport(session, unusedUrlName);
    // If the password is non-null, SMTP tries to do AUTH LOGIN.
    final String emptyPassword = null;
    transport.connect(host, port, userEmail, emptyPassword);

            byte[] response = String.format("user=%s\1auth=Bearer %s\1\1", userEmail,
            oauthToken).getBytes();
    response = BASE64EncoderStream.encode(response);

    transport.issueCommand("AUTH XOAUTH2 " + new String(response),
            235);

    return transport;
}

public synchronized void sendMail(String subject, String body, String user,
        String oauthToken, String recipients) {
    try {

        SMTPTransport smtpTransport = connectToSmtp("smtp.gmail.com",
                587,
                user,
                oauthToken,
                true);

        MimeMessage message = new MimeMessage(session);
        DataHandler handler = new DataHandler(new ByteArrayDataSource(body.getBytes(), "text/plain"));   
                message.setSender(new InternetAddress(user));   
                message.setSubject(subject);   
                message.setDataHandler(handler);   
        if (recipients.indexOf(',') > 0)   
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipients));   
        else  
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(recipients));   
        smtpTransport.sendMessage(message, message.getAllRecipients());   


    } catch (Exception e) {
        Log.d("test", e.getMessage());
    }

}

I'm not at all an expert in this and I didn't use any Security provider like in the examples above, not sure how it will affect this but it's working for me. Hope this helps and that someone can tell me if there's something wrong with this too :p It's my first answer here so sorry if I did something wrong!

Ops, forgot some other documentation I used: https://developers.google.com/google-apps/gmail/xoauth2_protocol and http://developer.android.com/training/id-auth/authenticate.html

ops again! You also need these permissions in the manifest

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
alex
  • 3,412
  • 2
  • 28
  • 36
  • GAE: works for sending email with GAE+SMTP+OAuth+GMail - needs billing enabled, but that is free to do. – eddyparkinson Apr 15 '13 at 03:31
  • This answer gives me a NPE when `sendMessage()` is called, any ideas? :) – whitfin Jun 15 '13 at 16:57
  • @alex, how would you read inbox using xoauth? I posted a separate question: http://stackoverflow.com/questions/17681245/android-javamail-api-using-xoauth-to-read-inbox Could you answer the question if you can? Thanx. – jclova Jul 16 '13 at 15:54
  • I was unable to send auth commands directly for imap (I don't know how to do it or if it's possible) so I added sasl support to java mail for imap. You can find a zipped test project in this answer here http://stackoverflow.com/questions/14682093/access-gmail-using-imap-with-accountmanager-token/14691708#14691708 . The java mail in the zip has support for sasl authentication for imap (not smtp) but I only tested server authentication, nothing more. – alex Jul 16 '13 at 17:56
  • Unbelievable! I went through dozens of sample online and this is the ONLY one that actually works. seems like the scope for `getAuthToken` is the key difference from other samples that use "mail", "ah" or anything else – Muzikant Jul 18 '13 at 13:34
  • I get the following error: {"status":"400","schemes":"Bearer","scope":"https://mail.google.com/"} Have you seen this type of error before? Any idea what it means? – Camille Sévigny Sep 16 '13 at 18:52
  • Never happened to me, if you are using smtp debug, post the complete error – alex Sep 16 '13 at 20:59
  • Android AccountManager stopped working for me. I had to use GoogleAuthUtil to get the token. – RobB Sep 28 '13 at 14:16
  • @Jakob were you able to get this working? I'm getting an error as I posted in this SO link: http://stackoverflow.com/questions/22185426/unable-to-send-gmail-e-mail-using-javamail-and-account-manager-error-400/22186525?noredirect=1#22186525 – Rohan Mar 06 '14 at 21:25
  • Its working with ICS only and not GB or JB, any insights? Getting MessageException – Ayush Goyal Mar 17 '14 at 18:54
  • @alex can you please give me the complete code because i cant able to send email successfully.Thanks in advance – Talha Q Jul 14 '14 at 03:30
  • @TalhaQ What error do you get? The complete code is specific to the application I was developing but the core is the same as I posted in the answer here. – alex Jul 15 '14 at 13:25
  • @alex i cant able to get the oauth token and obviously can't able to send email.There are many posts available which takes username and password from user and then send and email.I want any working example how can i able to authenticate using oauth and sending and email.Thanks for commenting :) – Talha Q Jul 15 '14 at 17:18
  • Finally got this working. Thanks so much! Be sure the "From:" address is the same as the authorized gmail account. Also watch out for race conditions. – mdaddy Sep 28 '14 at 13:10
  • This worked for me as well, although it was a bit struggle I finally got it to work on Java 1.5.5 API. This answer has scope of cleanup and improvement, hopefully I will do that when I have everything working. But thanks for putting all this together. – MG Developer Sep 21 '18 at 02:15