gapi grantOfflineAccess access_code to refresh_token

2.3k Views Asked by At

Up until at least the end of June this year (2017) (my users are teacher's so don't access our apps much over the summer...) I had the following work flow.

Users would login via the web using the gapi library. For new users, successful sign in would then call

gapi.auth2.getAuthInstance().currentUser.get().grantOfflineAccess()

to get a code that was then sent to our Java backend to exchanges the code for a refresh_token. We use the google-api-client library (v 1.22.0) to verify the user's id_token that is returned upon initial sign in and then a REST call to get the refresh_token returned.

HttpClient client   = new DefaultHttpClient();
HttpPost post       = new HttpPost("https://www.googleapis.com/oauth2/v4/token");
List<NameValuePair> pairs = new ArrayList<NameValuePair>();
pairs.add(new BasicNameValuePair("code", r.getRefreshCode()));
pairs.add(new BasicNameValuePair("redirect_uri", "postmessage"));
pairs.add(new BasicNameValuePair("client_id", clientInfo.getClientId()));
pairs.add(new BasicNameValuePair("client_secret", clientInfo.getClientSecret()));
pairs.add(new BasicNameValuePair("grant_type", "authorization_code"));
post.setEntity(new UrlEncodedFormEntity(pairs));
org.apache.http.HttpResponse response = client.execute(post);
String responseBody = EntityUtils.toString(response.getEntity());

Up until at least June, the REST call returned a JSON object in the following form

{
  access_token: '',
  token_type: '',
  expires_in: 3600,
  id_token: '',
  refresh_token: ''
}

...But now (within the last couple of weeks anyway), the refresh token is not included in the response from the REST call.

What has changed, or what do I need to do differently to get the refresh_token for our servers to use?

In the OAuth 2.0 Playground provided by Google, when I use the same scopes as my app and exchange the "code" for a refresh token, I get an object back with

{
  "access_token": "", 
  "token_type": "", 
  "expires_in": 3600, 
  "refresh_token": ""
}

So, obviously the Playground is doing something differently than I am. I've looked at the request send in the Chrome debugger and it looks similar to what I'm doing.

1

There are 1 best solutions below

3
On

I solved the problem by modifying the grantOfflineAccess call to include the following...

gapi.auth2.getAuthInstance().currentUser.get().grantOfflineAccess({
  access_type: 'offline',
  prompt:      'consent',
  scope:       SCOPES.join(' ')
})

...The important adding being the prompt. As far as I can tell, the window to provide consent for offline access was being closed automatically so the code returned wasn't valid to produce a refresh token on the Java side.

Also, I updated the Java code to use the Google client library instead of a REST call directly (though the JS change still resulted in retrieving a refresh token with the REST call, so this was just for consistency).

GoogleAuthorizationCodeTokenRequest request = 
  new GoogleAuthorizationCodeTokenRequest(new NetHttpTransport(), new JacksonFactory(),
                                  clientInfo.getClientId(), clientInfo.getClientSecret(),
                                  r.getRefreshCode(), "postmessage");
request.setGrantType("authorization_code");
GoogleTokenResponse response = request.execute();