Reputation: 19284
I have a Google Apps account. I'm trying to simply send an email on behalf of a user using a Service Account.
I've scoured the internet and nothing is working and i'm nearly at a loss.
I've followed the Java guides and i still keep getting com.google.api.client.auth.oauth2.TokenResponseException: 401 Unauthorized
Why does this code snippet give me 401 Unauthorized?
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId("[email protected]")
.setServiceAccountPrivateKeyFromP12File(new File("path/to/file/myProject.p12"))
.setServiceAccountScopes(GmailScopes.all())
.setServiceAccountUser("[email protected]")
.build();
Gmail gmailService = new Gmail.Builder(httpTransport, JSON_FACTORY, credential)
.setApplicationName("My App") // DOES IT MATTER WHAT THIS IS SET TO?
.build();
MimeMessage mimeMessage = createEmail("[email protected]", "[email protected]", "Testing", "hey");
sendMessage(gmailService, "me", mimeMessage);
These methods are basically copy/paste from Googles documentation:
/**
* Create a MimeMessage using the parameters provided.
*
* @param to email address of the receiver
* @param from email address of the sender, the mailbox account
* @param subject subject of the email
* @param bodyText body text of the email
* @return the MimeMessage to be used to send email
* @throws MessagingException
*/
public static MimeMessage createEmail(String to,
String from,
String subject,
String bodyText)
throws MessagingException {
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);
MimeMessage email = new MimeMessage(session);
email.setFrom(new InternetAddress(from));
email.addRecipient(javax.mail.Message.RecipientType.TO,
new InternetAddress(to));
email.setSubject(subject);
email.setText(bodyText);
return email;
}
/**
* Send an email from the user's mailbox to its recipient.
*
* @param service Authorized Gmail API instance.
* @param userId User's email address. The special value "me"
* can be used to indicate the authenticated user.
* @param email Email to be sent.
* @throws MessagingException
* @throws IOException
*/
public static void sendMessage(Gmail service, String userId, MimeMessage email)
throws MessagingException, IOException {
Message message = createMessageWithEmail(email);
System.out.println("userId = " + userId);
message = service.users().messages().send(userId, message).execute();
System.out.println("Message id: " + message.getId());
System.out.println(message.toPrettyString());
}
/**
* Create a Message from an email
*
* @param email Email to be set to raw of message
* @return Message containing base64url encoded email.
* @throws IOException
* @throws MessagingException
*/
public static 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;
}
I just get this stacktrace:
com.google.api.client.auth.oauth2.TokenResponseException: 401 Unauthorized
at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:105)
at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:287)
at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307)
at com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:384)
at com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489)
at com.google.api.client.auth.oauth2.Credential.intercept(Credential.java:217)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:859)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
at com.my.services.NotificationServiceTest.testGmailCredential(NotificationServiceTest.java:96)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
I have my service account created and set to domain wide delegation and here's a screenshot of my Manage API client access scopes:
What am i missing that I keep get 401 Unauthorized?
Upvotes: 15
Views: 1482
Reputation: 19284
After a lengthy, yet helpful phone call with Google Support, we finally tracked down that if I changed GmailScopes.all()
to the explicit scopes below then it worked.
Collection<String> SCOPES
= Collections.unmodifiableCollection(
Arrays.asList(
new String[]{
GmailScopes.GMAIL_COMPOSE,
GmailScopes.GMAIL_SEND
}));
The support guy wasn't 100% sure, but he thought that maybe since my user didn't have access to ALL the gmail scopes and I was specifying GmailScopes.all()
that it was error'ing with 401 Unauthorized before even checking which scope I was trying to use.
Upvotes: 6
Reputation: 6505
look over here, seems you don't have the access token yet! add refeshToken to your code:
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId("[email protected]")
.setServiceAccountPrivateKeyFromP12File(new File("path/to/file/myProject.p12"))
.setServiceAccountScopes(GmailScopes.all())
.setServiceAccountUser("[email protected]")
.build();
credential.refreshToken();
Upvotes: 3
Reputation: 124
The most common error causes for when making API calls with an access token are:
Sometimes, more explanation exists in the response body of a HTTP 4xx. In the Java client, for example, you should log the error, because it will assist in troubleshooting:
try {
// Make your Google API call
} catch (GoogleJsonResponseException e) {
GoogleJsonError error = e.getDetails();
// Print out the message and errors
}
You could take your existing code and make an API call here whenever you get a HTTP 4xx and log that response. This’ll return some useful information.
If the token is invalid, you can follow this steps.
You can debug everything by folowwing this link. It even has a tutorial video!
Upvotes: 2