Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authenticate with Service Account to send Gmail #1270

Open
ybhwang opened this issue Feb 13, 2023 · 2 comments
Open

Authenticate with Service Account to send Gmail #1270

ybhwang opened this issue Feb 13, 2023 · 2 comments
Assignees
Labels
api: gmail Issues related to the Gmail API API. priority: p3 Desirable enhancement or fix. May not be included in next release.

Comments

@ybhwang
Copy link

ybhwang commented Feb 13, 2023

Thanks for stopping by to let us know something could be better!

PLEASE READ: If you have a support contract with Google, please create an issue in the support console instead of filing on GitHub. This will ensure a timely response.

Please run down the following list and make sure you've tried the usual "quick fixes":

If you are still having issues, please include as much information as possible:

Environment details

  1. Specify the API at the beginning of the title: Gmail (Google Workspace)
  2. OS type and version: Spring Boot 2.3.7.RELEASE
  3. Java version: OpenJDK 11
  4. version(s):
    implementation 'com.google.api-client:google-api-client:2.0.0'
    implementation 'com.google.apis:google-api-services-gmail:v1-rev20230123-2.0.0'
    
    implementation 'org.springframework.boot:spring-boot-starter-mail' // javax.mail.*

Steps to reproduce

  1. Gmail API activated
  2. Create Service Account
  3. Set Domain Wide Delegation (expect to send email with any addresses in domains / Not for a specific user's address)
  4. RESTful API with Springboot
  5. Test API
    • POST /send-gmail (application/json)
  6. Error Occurred

Code example

...
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.google.api.services.gmail.Gmail;
import javax.mail.internet.MimeMessage;

@RestController
public class GmailController {

	Gmail service;

	@Autowired
	private GmailService gmailService;

	@PostMapping("/send-gmail")
	public ResponseEntity<String> sendEmail(@RequestBody Email email) {
		try {
			MimeMessage message = gmailHelper.createMessage(email.getTo(), email.getFrom(), email.getSubject(), email.getBody());
			gmailService.sendMessage("me", message);
			return ResponseEntity.ok().body("Email sent successfully!");
		} catch (Exception e) {
			return ResponseEntity.badRequest().body("Failed to send email: " + e.getMessage());
		}
	}

}
...
import org.springframework.stereotype.Service;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.http.HttpTransport;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.model.Message;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import org.apache.commons.codec.binary.Base64;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.InternetAddress;
import javax.mail.Session;
import javax.mail.MessagingException;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.Properties;

@Service
public class GmailService {

    private static final JsonFactory jsonFactory = GsonFactory.getDefaultInstance();
	private static HttpTransport httpTransport;

    public MimeMessage createMessage(String to, String from, String subject, String body) throws MessagingException, UnsupportedEncodingException {
        Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);

        MimeMessage message = new MimeMessage(session);
        message.setFrom(new InternetAddress(from));
        message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to));
        message.setSubject(subject);
        message.setText(body);

        return message;
    }

    public Message sendMessage(String userId, MimeMessage message) throws MessagingException, IOException, GeneralSecurityException {
        Gmail service = getGmailService();
        
        Message sendMessage = createMessageWithEmail(message);
        sendMessage = service.users().messages().send(userId, sendMessage).execute();

        return sendMessage;
    }

    public Message createMessageWithEmail(MimeMessage email) throws MessagingException, IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        email.writeTo(baos);
        
        String encodedEmail = Base64.encodeBase64URLSafeString(baos.toByteArray());
        Message message = new Message();
        message.setRaw(encodedEmail);

        return message;
    }

    public Gmail getGmailService() throws IOException, GeneralSecurityException {
        GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
        httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        Gmail service =  new Gmail.Builder(httpTransport, jsonFactory, new HttpCredentialsAdapter(credentials))
            .setApplicationName("APPLICATION NAME")
            .build();
    
        return service;
    }

}

Stack trace

{
    "error": {
        "code": 401,
        "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
        "errors": [
            {
                "message": "Login Required.",
                "domain": "global",
                "reason": "required",
                "location": "Authorization",
                "locationType": "header"
            }
        ],
        "status": "UNAUTHENTICATED",
        "details": [
            {
                "@type": "type.googleapis.com/google.rpc.ErrorInfo",
                "reason": "CREDENTIALS_MISSING",
                "domain": "googleapis.com",
                "metadata": {
                    "service": "gmail.googleapis.com",
                    "method": "caribou.api.proto.MailboxService.GetMessage"
                }
            }
        ]
    }
}

External references such as API reference guides

Any additional information below

Creating and authenticating the OAuth 2.0 Client is confirmed as an official document, but I remember that in the past, only Service Account was authenticated and normal service was possible.

If the OAuth 2.0 Client allows the service to be built through offline settings, the server should be able to authenticate with Service Account because it seems that there is a problem with continuing the normal Gmail delivery service when the first authenticated user information is discarded (deleted through Workspace Admin).

In addition, if Client Token is implemented randomly to configure the logic of continuing authentication, it is expected that unnecessary resources will be generated in the part where the token is issued again through the authentication when the server is re-run.

In conclusion, if you look inside the key of the Service Account, I would like you to provide a way to use Gmail only with Service Account in the latest 2.0.0 version of Google API Service, where all the values required for authentication exist, including Client ID.

@JoeWang1127 JoeWang1127 added priority: p2 Moderately-important priority. Fix may not be included in next release. priority: p3 Desirable enhancement or fix. May not be included in next release. api: gmail Issues related to the Gmail API API. and removed priority: p2 Moderately-important priority. Fix may not be included in next release. labels Feb 15, 2023
@meltsufin meltsufin transferred this issue from googleapis/google-api-java-client-services Aug 10, 2023
@meltsufin
Copy link
Member

@TimurSadykov Any thoughts on why this isn't working? The README suggests that it should work.

@TimurSadykov TimurSadykov self-assigned this Aug 30, 2023
@TimurSadykov
Copy link
Member

Looking

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: gmail Issues related to the Gmail API API. priority: p3 Desirable enhancement or fix. May not be included in next release.
Projects
None yet
Development

No branches or pull requests

4 participants