Javamail api in android using XOauth
Asked Answered
B

1

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.

Bath answered 19/9, 2012 at 21:38 Comment(2)
The SASL implementation in Javamail has non-android dependencies. See my answer here: https://mcmap.net/q/744634/-cannot-connect-to-gmail-imap-using-xoauth-on-androidJardine
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.Bath
S
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" />
Sudorific answered 10/10, 2012 at 14:19 Comment(15)
GAE: works for sending email with GAE+SMTP+OAuth+GMail - needs billing enabled, but that is free to do.Woolsack
This answer gives me a NPE when sendMessage() is called, any ideas? :)Suffocate
@alex, how would you read inbox using xoauth? I posted a separate question: #17681745 Could you answer the question if you can? Thanx.Kurtzig
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 #14682593 . The java mail in the zip has support for sasl authentication for imap (not smtp) but I only tested server authentication, nothing more.Sudorific
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 elseEndarch
I get the following error: {"status":"400","schemes":"Bearer","scope":"mail.google.com"} Have you seen this type of error before? Any idea what it means?Counterfeit
Never happened to me, if you are using smtp debug, post the complete errorSudorific
Android AccountManager stopped working for me. I had to use GoogleAuthUtil to get the token.Gravel
@Jakob were you able to get this working? I'm getting an error as I posted in this SO link: #22185926Tyson
Its working with ICS only and not GB or JB, any insights? Getting MessageExceptionCalomel
@Sudorific can you please give me the complete code because i cant able to send email successfully.Thanks in advanceHypocycloid
@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.Sudorific
@Sudorific 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 :)Hypocycloid
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.Nuri
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.Lough

© 2022 - 2024 — McMap. All rights reserved.