javaspringgmail-apigoogle-workspacegoogle-api-java-client

Sending email via Google API - Failed precondition


I'm trying to use my Google Workspace account to send emails in my Spring Boot application. I went to the Google Cloud Console and I:

  1. Created a new project and selected it (namely APPLICATION_NAME_FROM_GOOGLE_CONSOLE)
  2. Enabled the Gmail API
  3. Created a OAuth consent screen
  4. Navigated to "Credentials" and created a new "Service account credentials", which gave me a new email I will call it EMAIL_ADDRESS_CREATED_WHEN_CREATING_THE_SERVICE_ACCOUNT

Then I have this code which causes a FAILED PRECONDITION error:

    // invoking code
    Gmail service = buildEmailService();
    Message message = createMessage(fromEmailAddress, toEmailAddress, subject, text);
    _sendMessage(service, message);


    private Gmail buildEmailService() throws MessagingException {
        GoogleCredentials credentials = null;
        try {
            String serviceAccountJson = "{\n" +
                    "  \"type\": \"service_account\",\n" +
                    "  \"project_id\": \"lybro-355514\",\n" +
                    "  \"private_key_id\.........." 
            ;

            InputStream targetStream = new ByteArrayInputStream(serviceAccountJson.getBytes());
            credentials = ServiceAccountCredentials.fromStream(targetStream)
                    .createScoped(Collections.singletonList(GmailScopes.GMAIL_SEND))
               .createDelegated(EMAIL_ADDRESS_CREATED_WHEN_CREATING_THE_SERVICE_ACCOUNT);

        } catch (IOException e) {
            e.printStackTrace();
            throw new MessagingException(e.getMessage());
        }
        HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);

        Gmail service = new Gmail.Builder(new NetHttpTransport(),
                GsonFactory.getDefaultInstance(),
                requestInitializer)
                .setApplicationName(APPLICATION_NAME_FROM_GOOGLE_CONSOLE)
                .build();

        return service;
    }


    private void _sendMessage(Gmail service, Message message) throws MessagingException {
        try {
            // Create send message
            message = service.users().messages().send(EMAIL_ADDRESS_CREATED_WHEN_CREATING_THE_SERVICE_ACCOUNT, message).execute();
            System.out.println("Message id: " + message.getId());
            System.out.println(message.toPrettyString());
        } catch (GoogleJsonResponseException e) {
            // TODO(developer) - handle error appropriately
            GoogleJsonError error = e.getDetails();
            if (error != null && error.getCode() == 403) {
                System.err.println("Unable to send message: " + e.getDetails());
            } else {
                e.printStackTrace();
                throw new MessagingException(e.getMessage());
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new MessagingException(e.getMessage());
        }
    }

With this code, I get the following error:

javax.mail.MessagingException: 400 Bad Request POST https://gmail.googleapis.com/gmail/v1/users/lybro-512@lybro-355514.iam.gserviceaccount.com/messages/send {   "code": 400,   "errors": [
    {
      "domain": "global",
      "message": "Precondition check failed.",
      "reason": "failedPrecondition"
    }   ],   "message": "Precondition check failed.",   "status": "FAILED_PRECONDITION" }

Solution

  • createDelegated(EMAIL_ADDRESS_CREATED_WHEN_CREATING_THE_SERVICE_ACCOUNT)
    

    The email you should be setting this to is the email of the user on your workspace account that you have configured delegation for.

    createDelegated("no-reply@Yourdomain.com")
    

    Send should also be sent from the user on your domain. The way service accounts you preconfigure them to work impersonate a user on your domain. In this way the service account is pretending to be that user.

    If you have not already read this: Perform Google Workspace Domain-Wide Delegation of Authority I can highly recommend doing so.