I am having trouble getting a one-time authorization code from Google. I am attempting to get the authorization code from an Android client so that I can send it to my Rails backend (web client).
In my Google Cloud Developer Console I have an application with two Client IDs:
Client ID for web application (for my rails backend)
Client ID for Android application (for my android client). The SHA1 used is from ~/.android/debug.keystore
Suppose the Web Application Client ID is 12345.apps.googleusercontent.com
Suppose the Android Client ID is 67890.apps.googleusercontent.com
This is some of my code:
private final static String WEB_CLIENT_ID = "12345.apps.googleusercontent.com";
private final static String GOOGLE_CALENDAR_API_SCOPE = "audience:server:client_id:" + WEB_CLIENT_ID;
private void getAndUseAuthToken(final String email) {
AsyncTask task = new AsyncTask<String, Void, String>() {
@Override
protected String doInBackground(String... emails) {
try {
return GoogleAuthUtil.getToken(AddExternalCalendarsActivity.this, emails[0], GOOGLE_CALENDAR_API_SCOPE);
} catch (UserRecoverableAuthException e) {
startActivityForResult(e.getIntent(), IntentConstants.REQUEST_GOOGLE_AUTHORIZATION);
} catch (IOException e) {
e.printStackTrace();
} catch (GoogleAuthException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(String authToken) {
if (authToken != null) {
saveTokenAndGetCalendars(email, authToken);
}
}
};
String[] emails = new String[1];
emails[0] = email;
task.execute(emails);
}
Some additional notes
I am hitting the GoogleAuthException and receiving "Unknown" as the message detail
I'm unable to add additional members in the permissions of the Google Cloud Console for this project - when adding a new member a popup appears with "Server Error. Whoops! Our bad.". I have sent feedback to Google twice.
I'm referring to this documentation. Notice the quote below. By "fixed", are they saying that I do not need to prepend audience:server:client_id in my GOOGLE_CALENDAR_API_SCOPE variable? I've tried both with and without and still getting the same GoogleAuthException.
In this situation, the Android app can call the GoogleAuthUtil.getToken() method on behalf of any of the Google accounts on the device, and with a scope argument value of audience:server:client_id:9414861317621.apps.googleusercontent.com. The prefix audience:server:client_id: is fixed, and the rest of the scope string is the client ID of the web component.
If I use this scope, I can authenticate with google from device. However, the documentation I've read suggests that I need to use the server web client id (which is in the same google console project as the android client id) in the scope in order for the server to hit the google api on behalf of the user who authorized it on the android client:
private final static String GOOGLE_CALENDAR_API_SCOPE = "oauth2:https://www.googleapis.com/auth/calendar";
UPDATE 1
I originally added in answer: The reason for my first problem - I am hitting the GoogleAuthException and receiving "Unknown" as the message detail - was a mistake I made when adding the android client id in the cloud console. The SHA1 was correct but I did not type the package name correctly. I used com.company.app when my android package is actually com.company.android.app. The code in the original question works fine. Just make sure you have all the necessary clients in your Google Cloud Console project.
But another problem still exists. When I send the one-time authorization token returned from GoogleAuthUtil.getToken() to the Rails backend, and then try to exchange it for an access_token and refresh_token, I get the follow:
Signet::AuthorizationError:
Authorization failed. Server message:
{
"error" : "invalid_grant"
}
This google documentation and several SO posts suggests that I need to set access_type=offline
. But I think that is when you are requesting the one-time authorization code and offline access from a Web Server. I'm trying to request the one-time authorization code from an Android client and send it to the web server.
Is this possible with GoogleAuthUtil.getToken()?
UPDATE 2
Google Plus login must be in the scope even if you're only trying to access the calendar:
private final static String GOOGLE_CALENDAR_API_SCOPE = "oauth2:server:client_id:" + WEB_CLIENT_ID + ":api_scope:https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/calendar";
This SO post was helpful. Also, Google's Cross Client identity documentation does state:
[Note: This policy in being rolled out gradually. For the moment, when access tokens are involved, it only applies when the requested scopes include https://www.googleapis.com/auth/plus.login.]
I'll summarize in an answer if the token exchange works on Rails backend.
Two things solved this for me:
Make sure the Android and Web Client IDs are setup in correctly in the same Google Cloud Console project.
Use the correct scope. Plus login is required even if you're only accessing the calendar api:
// the id of the web server that is exchanging the auth code for access and refresh tokens
private final static String WEB_CLIENT_ID = "12345.apps.googleusercontent.com"; private final static String GOOGLE_CALENDAR_API_SCOPE = "oauth2:server:client_id:" + WEB_CLIENT_ID + ":api_scope:https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/calendar";
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With