Firstly, I have reviewed multiple SO questions relating to similar issues and the Google Picker API docs but cannot find a solution. Links at the bottom of this post.
Goal
After using the Google auth2 JavaScript library to complete a ux_mode='popup' Sign In process for a user to obtain an access_code, I wish to open a Google Picker window using the picker JavaScript library and the obtained access_code.
Expected
After checking the access_code is still valid, when the google.picker.PickerBuilder() object is set to visible via picker.setVisible(true) the user is ALWAYS able to select files as per the configuration set for the picker object.
Actual
On a fresh browser, encountering this flow results in the Google Picker window asking the user to sign in again. If the user chooses to do so an additional popup will be triggered that automatically executes a login flow again and the user is then informed that "The API developer key is invalid."
What is truly unexpected about this is that if the user refreshes the page and repeats the exact same actions the Google Picker works exactly as expected. The only way to replicate the anomalous behaviour is to clear all the browser cache, cookies and any other site related settings.

On the JavaScript console there are the common errors of:
Failed to execute ‘postMessage’ on ‘DOMWindow’: The target origin provided (‘https://docs.google.com’) does not match the recipient window’s origin (‘http://localhost’).
Invalid X-Frame-Options header was found when loading “https://docs.google.com/picker?protocol=gadgets&origin=http%3A%2F%2Flocalhost&navHidden=true&multiselectEnabled=true&oauth_token=.............: “ALLOW-FROM http://localhost” is not a valid directive.
But otherwise, no other indication of error in the console, and the exact same errors are reported when the Picker works exactly as expected.
List of things I have confirmed
- I have added the Google Picker API to the associated project in the Google developer console
- I am working with a validated OAuth application configured in the OAuth Consent Screen
- I have tried working with both localhost and an https:// top level domain with valid cert and registered with the Google console as verified
- I have generated an API Key that is explicitly associated with the Picker API and the relevant URLs
- The API key is set as the .setDeveloperKey(APIKey)
- The API key does show usage in the GCP console
- The clientId is correct and the appId is correct for the GCP project
- I have tried with scopes of ['https://www.googleapis.com/auth/drive.file'] and with scopes of ['openid', 'email', 'profile', 'https://www.googleapis.com/auth/drive.file', 'https://www.googleapis.com/auth/documents']
Attempts to Resolve
I can replicate this behaviour with the bare minimum example provided in the docs Google Picker Example used as a direct cut and paste, only replacing the required credential strings.
Right before invoking the picker = new google.picker.PickerBuilder() I have validated the access_token by executing a GET fetch to https://www.googleapis.com/oauth2/v1/tokeninfo and the signin behavior still results when this returns a successful validation of the token.
I check the token using this simple function:
function checkToken(access_token) {
fetch("https://www.googleapis.com/oauth2/v1/tokeninfo", {
method: "GET",
headers: {
'Authorization': 'Bearer ' + access_token
}
}).then(response => {
if (response.status === 200) {
return response.json();
} else {
console.log('User Token Expired');
resetLoginCache();
}
}).then(data => {
console.log('User Token Still Valid');
}).catch((error) => {
console.error('User Token Check Error:', error);
})
}
The JavaScript API's are initialized with:
<meta name="google-signin-client_id" content="<clientId>">
<script type="text/javascript" src="https://apis.google.com/js/api.js"></script>
function initApi() {
gapi.load('signin2:auth2:picker', () => {
window.gapi.auth2.init(
{
'client_id': clientId,
'scope': scope.join(" ")
});
});
};
In my application code I've (poorly) implemented a simple generalization of a picker builder:
// Use the Google API Loader script to load the google.Picker script.
function loadPicker( mimeTypes = null, callback = pickerCallback ) {
gapi.load('picker', {
'callback': () => {
console.log("Gonna build you a new picker...")
createPicker( mimeTypes, callback );
}
});
}
// Create and render a Picker object for searching images.
function createPicker( mimeTypes, callback ) {
let gAuthTop = gapi.auth2.getAuthInstance();
let gUser = gAuthTop.currentUser.get();
let gAuthResp = gUser.getAuthResponse(true);
console.log(gAuthResp.access_token);
checkToken(gAuthResp.access_token)
if (pickerApiLoaded && oauthToken) {
// based on MIME type:
// FOLDER => google.picker.DocsView(google.picker.ViewId.FOLDERS)
// Cannot set mimeTypes to filter view
// DOCS => google.picker.View(google.picker.ViewId.DOCS)
// Can set MIME types to match
let selectView = new google.picker.View(google.picker.ViewId.DOCS);
if (mimeTypes) {
if (mimeTypes.includes(gdriveFolderMIME)) {
selectView = new google.picker.DocsView(google.picker.ViewId.FOLDERS);
selectView.setIncludeFolders(true);
selectView.setSelectFolderEnabled(true);
selectView.setParent('root');
} else {
selectView.setMimeTypes(mimeTypes);
}
}
let picker = new google.picker.PickerBuilder()
.enableFeature(google.picker.Feature.NAV_HIDDEN)
.enableFeature(google.picker.Feature.MINE_ONLY)
.setAppId(appId)
.setMaxItems(1)
.setOAuthToken(gAuthResp.access_token)
.addView(selectView)
.setSelectableMimeTypes(mimeTypes)
.setDeveloperKey(developerKey)
.setOrigin(window.location.protocol + '//' + window.location.host)
.setCallback(callback)
.setTitle('Application Title')
.build();
console.log('Origin was set to: ', window.location.protocol + '//' + window.location.host)
picker.setVisible(true);
}
}
I've even tried to dig into the minified code loaded by the Picker but I'm not that good at JavaScript and the Firefox debugger wasn't able to help me understand what might be triggering this. However, once the "error" has been passed once, it will not appear on the same browser again for any user account and within Firefox using Private Mode will also no longer show the sign in error until all history, cookies and cache are cleared.
As proof I have done some reasonable research, similar SO questions that I have reviewed and tried working with are:
- Google picker asking to sign in even after providing access token
- The API developer key is invalid when viewing file in google picker
- Is it possible to open google picker with access token which is fetched from server side Oauth2?
- How do I use Google Picker to access files using the “drive.file” scope?
- Google Picker with AccessToken not working
- Google Picker API sign in
- Picker API - manually set access_token
- Google Picker API - how to load a picker on demand
As well as the following documentation: