We are using the connection_scope
parameter in our system to request the following scopes from google social connection (setting connection = "google-oauth2"
):
https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/contacts.readonly
Is there a way to check if the user declines any of the scopes? Our app is a google calendar client and so we would like to be able to check right after login if the calendar scope was not granted since without it our app is useless.
According to google the there should be the list of granted scopes in the access token response. However, I can’t seem to find those anywhere in auth0, is there a way to retrieve those scopes? And ideally, propagate those to the auth0 access token response?
We may also not included the contacts scope for the first version of our app but instead request it in a future version requiring us to do incremental auth 1. So I have tried including the granted include_granted_scopes
parameter but that didn’t seem to help.
I have pasted the rule I am using currently as a workaround onto the end of this post. This rule uses the https://oauth2.googleapis.com/tokeninfo
endpoint to fetch the scopes after login to store the scopes into the metadata (we are also looking to providing multi-account support in the future by linking users which is we the scopes are stored as a dictionary with the key being the identity id)
Side Question: In a similar vein, there a way to make the refresh token provided by google persistent? Since google refresh, token id is only provided on the first authorization subsequent logins appear to erase the refresh token from the identities
array in the user
object. For this reason, the attached rule (along with fetching the scopes) also copies the refresh token from the identities array into the user_metadata
in order to make it persistent. I know this is not recommended 2 (at least for app_metadata
but I assume it is the same for user_metadata
), but I can’t think of another way to easily make that refresh token persistently available.
Any help with either of these questions would be greatly appreciated .
References (To get around the 3 link limit for new users ):
https://developers.google.com/identity/protocols/oauth2/web-server#incrementalAuth
https://auth0.com/docs/best-practices/metadata-best-practices#app-metadata-restrictions
Persist Granted Scopes and Refresh Token Rule:
function (user, context, callback) {
var getTokenInfoUrl = 'https://oauth2.googleapis.com/tokeninfo';
var rp = require('request-promise');
console.log("========== Persist Refresh Token and Scopes =========");
user.user_metadata = user.user_metadata || {};
user.user_metadata.refresh_tokens = user.user_metadata.refresh_tokens || {};
user.user_metadata.scopes = user.user_metadata.scopes || {};
var fetchScopesFromGoogle = function(accessToken) {
return new Promise((resolve, reject) => {
return rp({
uri: getTokenInfoUrl,
qs: {
access_token: accessToken
},
json: true
}).then(body => {
resolve(body.scope.split(" "));
}).catch(error => {
console.error('error:', error);
reject(error);
});
});
};
var persistMetaData = function() {
console.log(user);
return auth0.users.updateUserMetadata(
user.user_id,
user.user_metadata
).then(function() {
callback(null, user, context);
}).catch(function(err) {
callback(err);
});
};
var promises = Promise.all(user.identities.map((identity, idx) => {
if ('refresh_token' in identity) {
var identity_id = identity.provider + '|' + identity.user_id;
// Persist refresh token in the meta data since auth0 seems to drop the
// the refresh tokens from the identities array after subsequent logins,
// likely due to:
// https://stackoverflow.com/questions/10827920/not-receiving-google-oauth-refresh-token
user.user_metadata.refresh_tokens[identity_id] = identity.refresh_token;
// Request the granted scopes for the token and add to metadata so we can
// have a record of the scopes that were actually granted for this refresh token
return fetchScopesFromGoogle(identity.access_token).then(scopes => {
user.user_metadata.scopes[identity_id] = scopes;
});
}
})).then(persistMetaData, persistMetaData); // No finally in Node v8
}