My goal is to have some kind of long lived access token so that my Android app can read the events of a user's Google Calendar for the day without requiring user approval every time.
I am able to generate -- what I think is -- a one-time authorization code; however, when I send this to my server side, app engine, I get the following error response:
400 OK
{
"error" : "invalid_grant",
"error_description" : "Code was already redeemed."
}
That is the exception that is being thrown. I'm just catching it and sending it back to myself as a way of debugging.
The one-time code I get starts with 4/VUr so I assume it is a one-time code and not a regular access token.
Currently, on Android, I allow a user to sign in using Google+ so that I have their email address. From there I request a one-time authorization code with the following code:
try {
Bundle appActivities = new Bundle();
appActivities.putString(GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES, "http://schemas.google.com/AddActivity");
String scopes = "oauth2:server:client_id:" + Constants.SERVER_CLIENT_ID + ":api_scope:https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/calendar";
//Plus.SCOPE_PLUS_LOGIN + " " + CalendarScopes.CALENDAR_READONLY;
String acctName = "myGmail";
String token = GoogleAuthUtil.getToken(getApplicationContext(), acctName, scopes, appActivities);
} catch (UserRecoverableAuthException e) {
startActivityForResult(e.getIntent(), 257/*REQUEST_AUTHORIZATION/*/);
} catch (Exception e) {
e.printStackTrace();
}
This code is from here and it seems that this is what I must do.
I then send this code to my App Engine Endpoint. I use code from here to request a access and refresh token.
The following is the code that I use as a simple test:
HttpTransport transport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
//ArrayList<String> scopes = new ArrayList<>();
//scopes.add("https://www.googleapis.com/auth/plus.login");
//scopes.add("https://www.googleapis.com/auth/calendar");
GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(transport, jsonFactory,
SERVER_CLIENT_ID, SERVER_CLIENT_SECRET, code, "postmessage")/*.setScopes(scopes)*/.execute();
//urn:ietf:wg:oauth:2.0:oob
//postmessage
code = tokenResponse.getRefreshToken();
It is failing right when I instantiate GoogleAuthorizationCodeTokenRequest
To name a few I have seen
https://developers.google.com/accounts/docs/CrossClientAuth#offlineAccess
Google-api-php Refresh Token returns invalid_grant
getting Google oauth authorization token from Android- return with invalid_scope/ Unknown error
Setting the redirect uri differently did not work. I did fill out the consent screen for my app engine project. Both installed Android client id and web application client id are in the same project. I have the redirect uri for the web application set to xxxxxxxx.appspot.com for my app.
Gradle for my main app:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
// compile 'com.google.api-client:google-api-client:1.18.0-rc'
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'org.altbeacon:android-beacon-library:2.1.3'
compile 'com.google.apis:google-api-services-calendar:v3-rev118-1.19.1'
compile 'com.google.api-client:google-api-client-android:1.18.0-rc'
compile 'com.google.android.gms:play-services:6.5.87'
compile 'com.google.http-client:google-http-client-jackson:1.19.0'
compile project(path: ':beaconBackend', configuration: 'android-endpoints')
}
Gradle for my backend:
dependencies {
appengineSdk 'com.google.appengine:appengine-java-sdk:1.9.14'
compile 'com.google.appengine:appengine-endpoints:1.9.14'
compile 'com.google.appengine:appengine-endpoints-deps:1.9.14'
compile 'javax.servlet:servlet-api:2.5'
}
Any help would really be appreciated! Thanks!
Also, note that I have tried invalidating/revoking the current access token (or one-time code?).
I just need a way to have some kind of long living access token without user interaction after the first time.
It's been a while since I worked on this project -- it was no longer needed.
The problem was within the server side app. I just wasn't requesting it correctly. Once my server side app (App Engine Endpoint) received the refresh token from the Android app I did the following on the server side.