For the love of Bob, someone please help me out...
I'm trying to use the Google Analytics API (Javascript Library) to get some Analytics info. I've registered the my app and set up the oauth2 stuff. I can return the access token jsut fine, but when i try to send a request to actually grab Analytics info, it returns a 403 forbidden error. Here's my code:
function auth() {
var config = {
'client_id': '[my_client_id]',
'scope': 'https://www.googleapis.com/auth/analytics.readonly'
};
gapi.auth.authorize(config, function() {
var retObj = gapi.auth.getToken();
makeRequest(retObj.access_token);
});
}
function makeRequest(accessToken) {
var restRequest = gapi.client.request({
'path': '/analytics/v3/data/ga',
'params': {
'access_token': accessToken,
'ids': 'ga:[table_number]',
'metrics': 'ga:pageviews,ga:uniquePageviews',
'start-date': '2011-11-01',
'end-date' : '2011-12-01'
}
});
restRequest.execute(function(resp) { console.log(resp); });
}
The auth() function is executed via a button click and like I said, getting the access token is not the issue. It's when I execute the makeRequest function that I get the 403 error. Anyone have any clue as to what the deal is here?
Thanks to anyone who answers in advance!!
In my case I was getting 403 Forbidden because in my browser I was logged into Google with an account that didn't have permission to the GA profile I was trying to access. Before discovering that issue, I was having trouble with the tableID for which Aksival posted the solution above.
Here's my working code for your reference:
<script type="text/javascript">
//GET THESE HERE https://code.google.com/apis/console/
var clientId = 'YOURCLIENTIDHERE';
var apiKey = 'YOURAPIKEYHERE';
//GET THIS HERE http://code.google.com/apis/analytics/docs/gdata/v3/gdataAuthorization.html
var scopes = 'https://www.googleapis.com/auth/analytics.readonly';
//INITIALIZE
function handleClientLoad() {
gapi.client.setApiKey(apiKey);
window.setTimeout(checkAuth,1);
}
function checkAuth() {
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: true}, handleAuthResult);
}
function handleAuthResult(authResult) {
if (authResult) {
makeApiCall();
} else {
requestAuth();
}
}
function requestAuth() {
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false}, handleAuthResult);
}
function makeApiCall() {
gapi.client.load('analytics', 'v3', function() {
var request = gapi.client.analytics.data.ga.get({
'ids':'ga:YOURTABLEIDHERE', 'start-date':'2012-01-01', 'end-date':'2012-02-01', 'metrics':'ga:visits', 'metrics':'ga:visits', 'start-index':1, 'max-results':1000
});
request.execute(function(resp) { console.log(resp.totalsForAllResults); });
});
}
</script>
<script src="https://apis.google.com/js/client.js?onload=handleClientLoad"></script>
I had the same issue. Turns out I was passing in the wrong [table_number].
You need to query
accounts/[account-id]/webproperties/[webproperties-id]/profiles
and use the 'id' field of the appropriate property. (I was using the internalWebPropertyId from the webproperties query at first, which is why it was failing.)
Works like a charm now.
Related
I'm so frustrated by the google auth docs. They seem to be inconsistent.
It says here:
https://developers.google.com/identity/sign-in/web/reference#gapiauth2initparams
I can call
gapi.auth2.init(params)
In my case, I want to pass in the hosted_domain param to restrict who can sign in to this app. Under the parameters list it explicitly states that
"You must request the email scope when using the hosted_domain parameter alongside fetch_basic_profile: false."
Great so I come up with:
var apiKey = 'my_key';
var scopes = 'email name';
var GoogleAuth = gapi.auth2.init({
apiKey: apiKey,
fetch_basic_profile: false,
scope: scopes,
hosted_domain: "my_domain"
});
GoogleAuth.signIn()
.then(function(response) {
// Do stuff
})
.catch(function(err) {
console.log(err);
});
When I test this out I get an error in my console:
gapi.auth2 has been initialized with different options. Consider calling gapi.auth2.getAuthInstance() instead of gapi.auth2.init().
But the docs clearly state that I should send the params to the init() method.
So I go to the getAuthInstance() documentation:
https://developers.google.com/identity/sign-in/web/reference#gapiauth2getauthinstance
Where is explicitly states that
You must initialize the GoogleAuth object with gapi.auth2.init() before calling this method.
Um, that's exactly what I thought I did, so why the error telling me to go straight for the getAuthInstance()? This is making me molt.
So I give it a try:
var apiKey = 'my_key';
var scopes = 'email name';
var GoogleAuth = gapi.auth2.getAuthInstance({
apiKey: apiKey,
fetch_basic_profile: false,
scope: scopes,
hosted_domain: "my_domain"
});
GoogleAuth.signIn()
.then(function(response) {
// Do stuff
})
.catch(function(err) {
console.log(err);
});
And the result is not as expected. It essentially proceeds to ignore the hosted_domain allowing anybody to sign in and seems to retrieve the full profile rather than just the email and name. What am I missing here?
Thanks
I think you may be misunderstanding the note for the hosted_domain param. You don't need to set fetch_basic_profile to false but if you do, you need to add "email" to the scope param.
As far as I can tell, this is all you should need...
const apiKey = 'my_key';
gapi.load('auth2', () => {
gapi.auth2.init({
client_id: apiKey, // note "client_id", not "apiKey"
hosted_domain: 'my_domain'
}).then(auth2 => { // wait for initialisation
if (!auth2.isSignedIn.get()) { // check if already signed in
auth2.signIn().then(...)
}
})
})
As for your error...
gapi.auth2 has been initialized with different options. Consider calling gapi.auth2.getAuthInstance() instead of gapi.auth2.init().
Sounds like you may be calling gapi.auth2.init() in multiple places. There should only be one instance of this.
I am using the Google API {grant} function to request the calendar permission. The function never completes the network call and throws this error:
TypeError: Cannot read property '$k' of undefined
at rl (cb=gapi.loaded_0:183)
at pP (cb=gapi.loaded_0:215)
at qY (cb=gapi.loaded_0:211)
at WE.<anonymous> (cb=gapi.loaded_0:195)
at new _.C (cb=gapi.loaded_0:104)
at WE.W2 (cb=gapi.loaded_0:195)
at WE._.k.sY (cb=gapi.loaded_0:195)
at base.js:126
at h.r2 (cb=gapi.loaded_0:107)
at xs (cb=gapi.loaded_0:110)
The code seemed to work until a few days ago and nothing has really changed. Here is a minimal example creating the error:
function scheduleCalendar() {
console.log("Schedule calendar");
gapi.load('auth2', function () {
gapi.auth2.init({
discoveryDocs: DISCOVERY_DOCS,
clientId: CLIENT_ID,
scope: 'email'
}).then(function (googleAuth) {
console.log(googleAuth);
googleAuth.signIn().then(function () {
const googleUser = googleAuth.currentUser.get();
console.log(googleUser);
const options = new gapi.auth2.SigninOptionsBuilder(
{'scope': 'https://www.googleapis.com/auth/calendar'}
);
googleUser.grant(options).then(
function(success){
console.log(success);
},
function(fail){
console.log(fail);
}
);
});
});
});
}
It turns out this was a regression in the Google API library. The problem seems to be fixed.
The project is on Google Appengine cloud endpoints framework.
Python in the backend.
I'm also using endpoints_proto_datastore (not sure if that makes a difference)
Here is my html file -
<html>
<body>
<script>
clientId = 'myclientid-something-something-something.apps.googleusercontent.com'
loginScope = 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/admin.directory.user.readonly https://www.googleapis.com/auth/admin.directory.customer.readonly https://www.googleapis.com/auth/gmail.settings.basic';
apiKey = 'my-api-key-from-cloud-console';
doSignIn = function() {
console.log("calling doSignIn");
gapi.client.init({apiKey: apiKey, clientId: clientId,scope: loginScope}).then(renderSignIn);
}
renderSignIn = function(){
gapi.signin2.render('my-signin', {
'scope': loginScope,
'width': 'inherit',
'height': 50,
'longtitle': true,
'theme': 'light',
'onsuccess': getOfflineAccess,
'onfailure': function(){console.log("error")}
});
};
getOfflineAccess =function(){
console.log("calling getOfflineAccess");
gapi.auth2.getAuthInstance().grantOfflineAccess({'redirect_uri': 'postmessage'}).then(getApiAuthorization);
}
getApiAuthorization = function(){
console.log("calling getApiAuthorization");
gapi.auth.authorize({client_id: clientId,scope: loginScope, immediate: false},singedInCallback);
};
singedInCallback = function(authResponse) {
console.log("calling signInCallback");
gapi.client.endpointsapp.userOfflineAccessCode.insert({'auth_code':authResponse.code})
.then( function(){console.log("success");},
function(){console.log("error");}
);
};
init = function() {
console.log("calling init");
var apisToLoad;
var callback = function() {
if (--apisToLoad == 0) {
doSignIn();
}
}
apisToLoad = 2;
gapi.client.load('endpointsapp','v1',callback,"https://endpointsapp.appspot.com/_ah/api"); //dummy name for app
gapi.load('client:auth2', callback);
};
</script>
<div id="my-signin"></div>
<script src="https://apis.google.com/js/api.js?onload=init"></script>
<script src="https://apis.google.com/js/client.js?onload=init"></script>
<script src="https://apis.google.com/js/platform.js"></script>
</body>
</html>
Everything goes smooth at first.
I get a google signing button.
I click on it and then all required permissions are granted.
When the actual API hit is made. That gives me a 401.
The response that I get from the API (gapi.client.endpointsapp.userOfflineAccessCode.insert) is :
{
"error": {
"code": 401,
"errors": [
{
"domain": "global",
"message": "Invalid token.",
"reason": "required"
}
],
"message": "Invalid token."
}
}
When I try the same api endpoint using the google api explorer, if I'm authenticated, everything works, without any issue.
I've been trying to debug this for an entire day but just can't figure out what I'm doing wrong.
Any help is highly appreciated.
ok found the issue. Very basic mistake.
According to https://cloud.google.com/endpoints/docs/frameworks/python/create_api allowed_client_ids is a required field if the API uses authentication. I was not providing this parameter and expecting the API to be available to all client_ids by default.
I have implemented javascript based google+ login in my application using the following code:
var isGPInitialzed = false;
function render() {
gapi.signin.render('loginWithGoogle', {
'callback': 'onSignIn',
'clientid': 'the client id',
'cookiepolicy': 'single_host_origin',
'requestvisibleactions': 'http://schema.org/AddAction',
'scope': 'https://www.googleapis.com/auth/plus.login'
});
isGPInitialzed = true;
}
//Google
function onSignIn(authResult) {
if (!isGPInitialzed) {
if (authResult['status']['signed_in']) { //get some user info
gapi.client.load('oauth2', 'v2', function () {
gapi.client.oauth2.userinfo.get().execute(function (response) {
console.log(response.email);
$.ajax({
url: '/Account/GLogin',
type: 'POST',
data: {
email: response.email,
name: response.name,
profilePicture: response.picture
},
dataType: 'json',
success: function (isUserLoggedIn) {
if (isUserLoggedIn) {
window.location.reload();
}
}
});
});
});
}
}
else {
isGPInitialzed = false;
}
};
It was working fine until I created a new application from another account and replaced the client id. On successful authentication, the api is not returning the user email in the response. I have checked in the google+ account settings for the Apps and there is not setting to give acces to the email. What can be the issue?
change the scope with
'scope': 'https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email',
For anyone who still looking for the answer, try to use this:
scope: 'openid profile email'
based on latest update from google developers, please change the scope,
https://www.googleapis.com/auth/plus.profile.emails.read
This scope requests that your app be given access to:
the user's Google account email address, as well as any public, verified email addresses in the user's Google+ profile. You access the email addresses by calling people.get, which returns the emails array.
the name of the Google Apps domain, if any, that the user belongs to.
If you're using Rails, the issue might be that you need to update omniauth-google-oauth2 to (0.6.0)
https://github.com/zquestz/omniauth-google-oauth2/issues/358
Google seems to have changed what gets returned by their API. The first comment in the above issue shows the structure of the hash has changed.
I have a simple code that Logs into to Google to use the Google Calendar API and creates and event for the user. The code works fine but every so often I get a 401 - Login Required error from Google. If I open the developer console in the browser and try again it works... which is very odd
var scopes = 'https://www.googleapis.com/auth/calendar';
var clientId = 'CLIENT_ID';
var apiKey = 'API_KEY';
function handleClientLoad() {
gapi.client.setApiKey(apiKey);
window.setTimeout(checkAuth,1);
}
function checkAuth() {
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: true}, handleAuthResult);
}
function handleAuthResult(authResult) {
var authorizeButton = $('#calendarAuth');
var calendarFrame = $('#calendarFrame');
if (authResult && !authResult.error) {
authorizeButton.remove();
calendarFrame.show();
calendarFrame.attr('src', calendarFrame.attr('src')); //Hack to reload the iframe
} else {
authorizeButton.show();
calendarFrame.hide();
}
}
function Authorize() {
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false}, handleAuthResult);
return false;
}
function GoogleScheduleFollowup(followup) {
gapi.client.load('calendar', 'v3', function () {
var request = gapi.client.calendar.events.insert({
"calendarId": "primary",
resource: {
"summary": followup.title,
"start": {
"dateTime": window.GetFullDateString(followup.date, followup.time)
},
"end": {
"dateTime": window.GetFullDateString(followup.date, followup.time)
}
}
});
request.execute(function (resp) {
console.log(resp);
});
}
Everything seems to be well configured in the API Console, and I'm sure we haven't reached the quota (either total or requests per second). As an example today I have made 133 requests of which 36 failed with this error.
I have tried to call gapi.auth.authorize every 10 minutes to see if the problem was a session timeout, and as I read in another question I tried removing this line gapi.client.setApiKey(apiKey);, both without success