Following the official Oauth example using Google API Client Libraries here, I wrote my application that obtains user consent on startup, and then keeps sending notifications via email. It works for some hours, and then suddenly fails with
com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request POST https://oauth2.googleapis.com/token { "error": "invalid_grant", "error_description": "Token has been expired or revoked." }
I have also set offline access type and force approval prompt as mentioned here but it never made a difference.
One peculiarity about my usecase is that I have multiple applications sharing the same credential store. I did this so that the creds stored by one app can seamlesslessly be used by another app without re-prompting the user (and all this being code for purely personal use on my desktop). With this setup, even as one application is able to send emails, another application would sometimes give the error I mentioned about.
Here is my code.
private Gmail createGmailService() throws IOException, GeneralSecurityException {
NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = GsonFactory.getDefaultInstance();
Credential credentials = getCredentials(httpTransport, jsonFactory);
return new Gmail.Builder(httpTransport, jsonFactory, credentials)
.setApplicationName(gCloudApplicationName)
.build();
}
public Gmail getGmailService() {
return gmailService;
}
/**
* Creates an authorized Credential object.
*
* @param httpTransport The network HTTP Transport.
* @param jsonFactory JSON factory
* @return An authorized Credential object.
* @throws IOException If the credentials.json file cannot be found.
*/
private Credential getCredentials(final NetHttpTransport httpTransport, JsonFactory jsonFactory) throws IOException {
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(jsonFactory, new FileReader(credentialsFilePath));
List<String> scopes = List.of(GmailScopes.GMAIL_SEND);
// Build flow and trigger user authorization request.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
httpTransport, jsonFactory, clientSecrets, scopes)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(tokensDirectoryPath)))
.setAccessType("offline")
// https://stackoverflow.com/questions/13777842/how-to-get-offline-token-and-refresh-token-and-auto-refresh-access-to-google-api
.setApprovalPrompt("force")
.build();
LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
}
I can't make sense of what could be going on. Any help is much appreciated.