I have a webpage using PHP/JavaScript that can successfully do the Google Signin process on the client side. I would like to then forward the obtained token to the backend which uses Spring-Social to create a social connection and be able to call on the APIs to which the app has been authorized. I managed to do exactly this with both Facebook (OAUTH2) and Twitter (OAUTH1) both working flawlessly but for Google I always get a 401 response when trying to create the connection. The code in the server is like this:
AccessGrant accessGrant = new AccessGrant(accessToken);
connection = ((OAuth2ConnectionFactory<?>) connectionFactory).createConnection(accessGrant);
The code in the client something like this:
function onSignIn(googleUser) {
var profile = googleUser.getBasicProfile();
console.log('ID: ' + profile.getId());
console.log('Name: ' + profile.getName());
console.log('Email: ' + profile.getEmail());
var access_token = googleUser.getAuthResponse().id_token;
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://localhost/signin/google');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function() {
console.log('Signed in as: ' + xhr.responseText);
console.log('access_token=' + access_token);
};
xhr.send('access_token=' + access_token);
}
Most of the latter is copied from the Google documentation on how to authenticate with a backend server found here: https://developers.google.com/identity/sign-in/web/backend-auth
I've traced the calls inside spring-social-google and they lead to a REST call to https://www.googleapis.com/oauth2/v2/userinfo which is the one that replies 401 and the following JSON:
{
"error": {
"errors": [{
"domain": "global",
"reason": "authError",
"message": "Invalid Credentials",
"locationType": "header",
"location": "Authorization"
}],
"code": 401,
"message": "Invalid Credentials"
}
}
I've tried to call on this same REST API myself and I also get the same response, so it does not seem to be an issue of spring-social-google not sending the token properly as I've read on some other posts.
I believe that the issue is somehow related to the fact that the JavaScript API is giving me an "id_token" and the REST API is expecting an "access_token". However, I am not finding a way to obtain the access token as something separate from the id_token and the Google documentation does send the id_token to the backend. There is an "access_token" property alongside the used "id_token" property in googleUser.getAuthResponse() but it is coming back undefined. This doc is of course not aimed at spring-social so I am not saying it is incorrect, I am just left wondering what is the proper way to achieve this.
Is spring-social at fault by not being able to deal with the id_token?
Am I at fault not seeing some clear way to get the access_token?
Anyway, I feel like I am somehow close but still the solution seems well out of grasp.
Thank you!
Well, I found it. It seems that writing the whole thing down really tripped something in my brain...
The issue is in fact that the id_token cannot take the place of the access_token. To my taste, Google documentation could be a little more explicit about this in the example they show but they do end up explaining it in the actual client reference at https://developers.google.com/identity/sign-in/web/reference
The key is the boolean parameter to getAuthResponse() which is optional and defaults to false. From the doc:
includeAuthorizationData Optional: A boolean that specifies whether to always return an access token and scopes. By default, the access token and requested scopes are not returned when fetch_basic_profile is true (the default value) and no additional scopes are requested.
By setting this to true in my JS the access_token field which was previously undefined was populated and with this token one can access the /oauth2/v2/userinfo endpoint and spring-social-google can correctly create a connection just like the other providers.
I hope this helps someone else at least.