I'm using Google Drive API java (see versions below) inside a Spring project. I'm aiming to monitor changes (this part is quite ok)
But I'm struggling to understand what's wrong with Google OAuth
First, I'm disapointed by the use of the class LocalServerReceiver
, as usually shown in quick starts like described here :
This class prepare a Jetty Server to listen to Oauth response as soon as you authorized your app using the provided url.
This class has an hardcoded http://
in it :
return "http://" + this.getHost() + ":" + this.port + this.callbackPath;
Which cannot be used in production : The OAuth consent screen allow production switching only if your credentials redirects use https://
So... How I'm supposed to listen to OAuth response ?
Also, this Jetty server creates its own port, along Spring App port. Is there a more elegant way to serve OAuth response using a standard Spring controller, without this Jetty server ?
I've successfully rewrite this class using the same implementation VerificationCodeReceiver
, but I've hardcoded https://
instead. Its works.
I can connect my app, I can track some changes.
I can renew the change tracking with a scheduler (it expires at some point - for testing purpose I'm doing this more often than necessary) using drive.changes().watch(...)
again (stopping the previous one before). It works a few times, but after less than 30 minutes, I can't use the API anymore, I receive the following error :
java.lang.RuntimeException: com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
GET
{
"code": 401,
"errors": [
{
"domain": "global",
"location": "Authorization",
"locationType": "header",
"message": "Invalid Credentials",
"reason": "authError"
}
],
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See .",
"status": "UNAUTHENTICATED"
}
at xxxx.WatchExpirationRunnableTask.run(WatchExpirationRunnableTask.java:28)
at .springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
Google Dependencies :
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client-jetty</artifactId>
<version>1.34.1</version>
</dependency>
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-drive</artifactId>
<version>v3-rev20220815-2.0.0</version>
</dependency>
Am I missing something here ?
Thanks for reading me.
I'm using Google Drive API java (see versions below) inside a Spring project. I'm aiming to monitor changes (this part is quite ok)
But I'm struggling to understand what's wrong with Google OAuth
First, I'm disapointed by the use of the class LocalServerReceiver
, as usually shown in quick starts like described here : https://developers.google/drive/api/quickstart/java
This class prepare a Jetty Server to listen to Oauth response as soon as you authorized your app using the provided url.
This class has an hardcoded http://
in it :
return "http://" + this.getHost() + ":" + this.port + this.callbackPath;
Which cannot be used in production : The OAuth consent screen allow production switching only if your credentials redirects use https://
So... How I'm supposed to listen to OAuth response ?
Also, this Jetty server creates its own port, along Spring App port. Is there a more elegant way to serve OAuth response using a standard Spring controller, without this Jetty server ?
I've successfully rewrite this class using the same implementation VerificationCodeReceiver
, but I've hardcoded https://
instead. Its works.
I can connect my app, I can track some changes.
I can renew the change tracking with a scheduler (it expires at some point - for testing purpose I'm doing this more often than necessary) using drive.changes().watch(...)
again (stopping the previous one before). It works a few times, but after less than 30 minutes, I can't use the API anymore, I receive the following error :
java.lang.RuntimeException: com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
GET https://www.googleapis/drive/v3/changes/startPageToken
{
"code": 401,
"errors": [
{
"domain": "global",
"location": "Authorization",
"locationType": "header",
"message": "Invalid Credentials",
"reason": "authError"
}
],
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
at xxxx.WatchExpirationRunnableTask.run(WatchExpirationRunnableTask.java:28)
at .springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
Google Dependencies :
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client-jetty</artifactId>
<version>1.34.1</version>
</dependency>
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-drive</artifactId>
<version>v3-rev20220815-2.0.0</version>
</dependency>
Am I missing something here ?
Thanks for reading me.
Share Improve this question asked Feb 1 at 10:02 Barium ScoeBarium Scoe 2,0285 gold badges31 silver badges49 bronze badges1 Answer
Reset to default 0I've looked for a solution for a long time.
I've concluded that the quickstart code is not a good base to run an app outside from a local test, as suggested by the class name LocalServerReceiver
Also the fact that this SDK starts a jetty server to handle the callback is not very handy when you're inside a Spring boot project with its own embebbed web server.
I've found this old post Setting context path in while authorizing application to access google apis using java
This answer is a quick (and quite dirty) implementation to use Google OAuth without using LocalServerReceiver
, like this :
flow = new GoogleAuthorizationCodeFlow.Builder(httpTransport, JSON_FACTORY, CLIENT_ID, CLIENT_SECRET, SCOPE)
.setAccessType("offline")
.setApprovalPrompt("force") //this is mandatory to retrieve refresh token later
.build();
String url = flow
.newAuthorizationUrl()
.setRedirectUri(REDIRECT_URI)
.build();
I've initiated a github with a working Spring Boot implentation here https://github/kemkem/google-drive-oauth-spring-boot