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.
I solved the problem by modifying the grantOfflineAccess call to include the following...
...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).