How can I use google api in chrome extension content script? - javascript

I am trying to list labels using Gmail api. I want to use Gmail api in content script. Following is my manifest.json file and content script file:
{
"name": "Append Test Text",
"description": "Add test123 to body",
"version": "1.0",
"permissions": ["activeTab"],
"content_scripts": [
{
"matches": ["https://mail.google.com/*"],
"js": ["jquery-3.4.1.min.js", "gmail.js", "content-script.js"],
"all_frames": true
}
],
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"browser_action": {
"default_title": "Append Test Text"
},
"manifest_version": 2
}
content-script.js:
// Client ID and API key from the Developer Console
var CLIENT_ID = "<CLIENT_ID>";
var API_KEY = "<API_KEY>";
// Array of API discovery doc URLs for APIs used by the quickstart
var DISCOVERY_DOCS = [
"https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest"
];
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES = "https://www.googleapis.com/auth/gmail.readonly";
/**
* On load, called to load the auth2 library and API client library.
*/
function handleClientLoad() {
console.log("hello");
gapi.load("client:auth2", initClient);
}
/**
* Initializes the API client library and sets up sign-in state
* listeners.
*/
function initClient() {
gapi.client
.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES
})
.then(
function() {
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
// Handle the initial sign-in state.
updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
},
function(error) {
console.log(JSON.stringify(error, null, 2));
}
);
}
/**
* Called when the signed in status changes, to update the UI
* appropriately. After a sign-in, the API is called.
*/
function updateSigninStatus(isSignedIn) {
if (isSignedIn) {
listLabels();
} else {
gapi.auth2.getAuthInstance().signIn();
}
}
/**
* Print all Labels in the authorized user's inbox. If no labels
* are found an appropriate message is printed.
*/
function listLabels() {
gapi.client.gmail.users.labels
.list({
userId: "me"
})
.then(function(response) {
var labels = response.result.labels;
console.log("Labels:");
if (labels && labels.length > 0) {
for (i = 0; i < labels.length; i++) {
var label = labels[i];
console.log(label.name);
}
} else {
console.log("No Labels found.");
}
});
}
var script = $(
'<script async defer src="https://apis.google.com/js/api.js" onload="this.onload=function(){};handleClientLoad()" onreadystatechange="if (this.readyState === "complete") this.onload()"></script>'
);
$("body").append(script);
after running this there should at least be a "hello" in the console proving that the handleClientLoad() function is working. But nothing shows in the console.

Technically, Chrome won't allow you to do that. If you use content script to inject into a website then you can just send request only on that site, if you're trying to send a request into another site, Chrome will stop you due to CORS policy.
So to achieve it, you have to setup a background script which receive passed messages from your content script and then send request into Google API endpoint and then you can return the result into your content script via defined message channel. Here is how you can setup message passing in Chrome Extension.

Related

How to create events with google calendar api using a chrome extension?

Goal: To create a google calendar event using a chrome extension.
I have been stuck for hours to find a solution to call the google calendar api from my popup script. However, I keep getting the following error:
Refused to load the script 'https://apis.google.com/js/client.js' because it violates the following Content Security Policy directive: "script-src 'self' https://apis.google.com/js/api.js". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
The calendar api works if I use the example given on the official docs (https://developers.google.com/calendar/api/quickstart/js). However, in this example the api script is loaded using the script tag in a html file and it does not encounter the above issue for reasons I do not fully understand.
My Manifest.json
{
"manifest_version": 2,
"name": "",
"description": "",
"version": "1.0",
"permissions": [
"identity","*://apis.google.com/*"
],
"content_scripts":[
{ "matches": ["<all_urls>"]
}
],
"browser_action":{
"default_icon":"icon.png",
"default_popup":"popup.html",
"default_title":"A popup will come here"
},
"content_security_policy": "script-src 'self' https://apis.google.com/js/api.js; object-src 'self'"
}
My popup.html
<!doctype html>
<html>
<head>
<title>GTmetrix Analyzer</title>
<script src="popup.js"></script>
</head>
<body>
<button id="authorize_button" style="display: none;">Authorize</button>
<button id="signout_button" style="display: none;">Sign Out</button>
</body>
</html>
My popup.js
// Client ID and API key from the Developer Console
var CLIENT_ID ="";
var API_KEY = "";
// Array of API discovery doc URLs for APIs used by the quickstart
var DISCOVERY_DOCS = [
"https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest",
];
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES =
"https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/calendar.events https://www.googleapis.com/auth/calendar.readonly";
var authorizeButton = document.getElementById("authorize_button");
var signoutButton = document.getElementById("signout_button");
function handleClientLoad() {
gapi.load("client:auth2", initClient);
}
function initClient() {
gapi.client
.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES,
})
.then(
function () {
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
// Handle the initial sign-in state.
updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
authorizeButton.onclick = handleAuthClick;
signoutButton.onclick = handleSignoutClick;
},
function (error) {
appendPre(JSON.stringify(error, null, 2));
}
);
}
/**
* Sign in the user upon button click.
*/
function handleAuthClick(event) {
gapi.auth2.getAuthInstance().signIn();
}
/**
* Sign out the user upon button click.
*/
function handleSignoutClick(event) {
gapi.auth2.getAuthInstance().signOut();
}
function updateSigninStatus(isSignedIn) {
if (isSignedIn) {
authorizeButton.style.display = "none";
signoutButton.style.display = "block";
createEvent();
} else {
authorizeButton.style.display = "block";
signoutButton.style.display = "none";
}
}
function createEvent() {
var event = {
summary: "Google I/O 2015",
location: "800 Howard St., San Francisco, CA 94103",
description: "A chance to hear more about Google's developer products.",
start: {
dateTime: "2021-08-22T09:00:00-07:00",
timeZone: "America/Los_Angeles",
},
end: {
dateTime: "2021-08-22T17:00:00-07:00",
timeZone: "America/Los_Angeles",
},
};
gapi.client.calendar.events
.insert({
calendarId: "primary",
resource: event,
})
.then(function (response) {
appendPre("Event created: " + response.result.htmlLink);
console.log(response);
});
}
let script = document.createElement('script');
chrome.tabs.getSelected(null,function(tab){
script.src = "https://apis.google.com/js/api.js";
document.body.append(script);
})
script.addEventListener('load',function(){
handleClientLoad();
})
Note: I would also be interested to know if there is a better way of making this type of chrome extension than what I am currently doing. Thanks!

Write Form Data from my Chrome Extension to Google Sheets

Updated with snippets and today's progress:
I am writing a Chrome Extension that is essentially a popup with a form, and I would like to write data entered into that form into Google Sheets. Currently, my extension consists of a manifest.json and a popup script, and a background script.
manifest.json (relevant pieces):
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [{ "js": ["content.js"], "matches": ["<all_urls>"] }],
"permissions": [
"tabs",
"storage",
"<all_urls>",
"identity",
"https://*.googleapis.com/*"
]
popup.js (note: this is an extension to track MS symptoms)
const app = {
symptoms: [],
init: function () {
//cache some element references
let formEl = document.getElementById("symptoms-form");
let fatigue = document.getElementById("fatigue");
let tingling = document.getElementById("tingling");
let weakness = document.getElementById("weakness");
let vision = document.getElementById("vision");
let dizzy = document.getElementById("dizzy");
let cognition = document.getElementById("cognition");
let depression = document.getElementById("depression");
let balance = document.getElementById("balance");
//upon submit, update symptoms obj and send to background
formEl.addEventListener("submit", ev => {
ev.preventDefault();
console.log('button click')
this.symptoms.push({fatigue: fatigue.value})
this.symptoms.push({tingling: tingling.value})
this.symptoms.push({weakness: weakness.value})
this.symptoms.push({vision: vision.value})
this.symptoms.push({dizzy: dizzy.value})
this.symptoms.push({cognition: cognition.value})
this.symptoms.push({depression: depression.value})
this.symptoms.push({balance: balance.value})
// chrome.runtime.sendMessage({fn: 'getSymptoms'}, function(response) {
// console.log('popup got response', response)
// })
chrome.runtime.sendMessage({fn: 'setSymptoms', symptoms: this.symptoms})
});
}
}
document.addEventListener('DOMContentLoaded', () => {
app.init();
})
background.js - note: my current workaround is to load the data into Firebase, which you will see below:
console.log("Background running");
const background = {
symptoms: [],
init: function() {
//listen for any messages and route them to functions
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.fn in background) {
background[request.fn](request, sender, sendResponse);
}
const jsonObj = {}
jsonObj['symptoms'] = request.symptoms
console.log("message received", jsonObj);
this.postSymptoms(jsonObj)
});
},
postSymptoms: function(msg) {
const xhr = new XMLHttpRequest();
xhr.open("POST", "https://ms-mysymptoms-1541705437963.firebaseio.com/symptoms.json", true);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.send(msg);
}
};
background.init();
I have set up a new project in the Google Developers console, enabled the Google Sheets API, and set up my credentials and API token. I tested in the Google API explorer that the authentication is set up properly and I can, indeed, write a row to my sheet. This is great news!
I am blocked right now on how to do this (write the data), directly from my Chrome extension. So far, I have saved all my credentials, set up a config file, and wrote my append method in a separate file locally.
sheets.js:
const {authorize, google} = require('./config')
const fs = require('fs')
const spreadsheetId = '---removed for this post--'
const append = (range, values) => {
fs.readFile('client_secret.json', (err, content) => {
if (err) return console.log('Error loading client secret file:', err);
// Authorize a client with credentials, then call the Google Sheets API.
authorize(JSON.parse(content), (auth) => {
const sheets = google.sheets({
version: 'v4',
auth
});
const valueInputOption = 'USER_ENTERED';
const resource = {
values
};
sheets.spreadsheets.values.append({
spreadsheetId,
range,
valueInputOption,
resource
}, (err, result) => {
if (err) {
console.log(err);
} else {
console.log("Success!");
}
});
});
});
}
// module.exports = {
// append
// };
When I try to integrate this code into my popup script, however, I encounter an error because in order to reference that config data and that append method, I have to use require in my popup script. Since the popup script is running in the browser, I can't use require (without webpack, that is).
I'm sure I'm going about this all wrong, so I could use a push in the right direction as to how to authenticate and append to Sheets from the browser if my configuration and authentication are stored in local files on my computer.
Solutions I've considered:
1 - spin up a REST API, post the data from the form to that endpoint, and have it act as a proxy to the Google Sheets API - this is not ideal.
2 - use webpack so that I can use require in my popup file
What would be the recommended way to do this? How should I integrate authentication and working with the Google Sheet into this extension?
Writing to a spreadsheet with Google's API is a PUT not a POST.
https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/update
I had success with this using chrome.identity.getAuthToken, then running a fetch with the following:
chrome.identity.getAuthToken({interactive: true}, function(token) {
var params = {
'values': [
['Row 1 Col A','Row 1 Col B'],
['Row 2 Col A','Row 2 Col B'],
]
};
let init = {
method: 'PUT',
async: true,
body: JSON.stringify(params),
headers: {
Authorization: 'Bearer ' + token,
Content-Type': 'application/json'
},
contentType: 'json',
};
fetch('https://sheets.googleapis.com/v4/spreadsheets/***YOUR SHEET ID****/values/****YOUR RANGE*****?valueInputOption=USER_ENTERED&key=***YOUR API KEY***', init)
.then((response) => response.json())
.then(function(data) {
//console.log(data);
//Returns spreadsheet ID, update tange, cols and rows
});
})
});
That's all in the background script, where I've put Row 1 Col A etc as the values, that'll be the first cell of your range.
Hope that helps.
Careful! If you want to append data, the ? query parameter comes after the :append.
fetch(`https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${range}:append?valueInputOption=${valueInputOption}`, init)

OAuth for GAPI - Avoid Authentication and Authorization after initial sign in for Javascript

I have created a chrome extension that reads email, does something and create tasks using google client API for javascript.
I am using chrome identity for authentication and authorization.
The extension works as expected. However, it keeps asking for sign every once in a while. What I want is to authorize the user in the background script so that they don't need to do it over and over again, after the initial authentication and authorization.
What I have done so far:
I read that I need a refresh token to avoid this. However, refresh tokens are expected to be exchanged and stored on the server side and not client side (which wouldn't work because the background script is doing the job here which is client side)
Using gapi.auth.authorize with immediate true. That gives error regarding external visibility. When I read else, they suggested using it inside a server. I am not sure how can I do that in a chrome extension.
Turn interactive to false in getAuthToken, which starts giving error 401 due to authentication problem after the access token expires.
Following is the code I am using for authentication and authorization, with function onGoogleLibraryLoaded being called after loading the google api's client js file.
var signin = function (callback) {
chrome.identity.getAuthToken({interactive: true}, callback);
};
function onGoogleLibraryLoaded() {
signin(authorizationCallback);
}
var authorizationCallback = function (data) {
gapi.auth.setToken({access_token: data});
gapi.client.load('tasks', 'v1')
gapi.client.load('gmail', 'v1', function () {
console.log("Doing my stuff after this ..")
});
};
UPDATE:
As per the suggestion in the answer, I made some changes to the code. However, I am still facing the same issue. Following is the updated code snippet
jQuery.loadScript = function (url, callback) {
jQuery.ajax({
url: url,
dataType: 'script',
success: callback,
async: false
});
}
//This is the first thing that happens. i.e. loading the gapi client
if (typeof someObject == 'undefined') $.loadScript('https://apis.google.com/js/client.js',
function(){
console.log("gapi script loaded...")
});
//Every 20 seconds this function runs with internally loads the tasks and gmail
// Once the gmail module is loaded it calls the function getLatestHistoryId()
setInterval(function() {
gapi.client.load('tasks', 'v1')
gapi.client.load('gmail', 'v1', function(){
getLatestHistoryId()
})
// your code goes here...
}, 20 * 1000); // 60 * 1000 milsec
// This is the function that will get user's profile and when the response is received
// it'll check for the error i.e. error 401 through method checkForError
function getLatestHistoryId(){
prevEmailData = []
var request = gapi.client.gmail.users.getProfile({
'userId': 'me'
});
request.execute(function(response){
console.log("User profile response...")
console.log(response)
if(checkForError(response)){
return
}
})
}
// Now here I check for the 401 error. If there's a 401 error
// It will call the signin method to get the token again.
// Before calling signin it'll remove the saved token from cache through removeCachedAuthToken
// I have also tried doing it without the removeCachedAuthToken part. However the results were the same.
// I have left console statements which are self-explanatory
function checkForError(response){
if("code" in response && (response["code"] == 401)){
console.log(" 401 found will do the authentication again ...")
oooAccessToken = localStorage.getItem("oooAccessTokenTG")
console.log("access token ...")
console.log(oooAccessToken)
alert("401 Found Going to sign in ...")
if(oooAccessToken){
chrome.identity.removeCachedAuthToken({token: oooAccessToken}, function(){
console.log("Removed access token")
signin()
})
}
else{
console.log("No access token found to be remove ...")
signin()
}
return true
}
else{
console.log("Returning false from check error")
return false
}
}
// So finally when there is 401 it returns back here and calls
// getAuthToken with interactive true
// What happens here is that everytime this function is called
// there is a signin popup i.e. the one that asks you to select the account and allow permissions
// That's what is bothering me.
// I have also created a test chrome extension and uploaded it to chrome web store.
// I'll share the link for it separately.
var signin = function (callback) {
console.log(" In sign in ...")
chrome.identity.getAuthToken({interactive: true}, function(data){
console.log("getting access token without interactive ...")
console.log(data)
gapi.auth.setToken({access_token: data});
localStorage.setItem("oooAccessTokenTG", data)
getLatestHistoryId()
})
};
Manifest goes like this:
{
"manifest_version": 2,
"name": "Sign in Test Extension ",
"description": "",
"version": "0.0.0.8",
"icons": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
},
"content_security_policy": "script-src 'self' 'unsafe-eval' https://apis.google.com; object-src 'self'",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"identity",
"storage"
],
"oauth2": {
"client_id": "1234.apps.googleusercontent.com",
"scopes": [
"https://www.googleapis.com/auth/gmail.readonly"
]
},
"background":{
"scripts" : ["dependencies/jquery.min.js", "background.js"]
}
}
Anyone else facing the same issue?
I am also using the identity API for google authorization in my chrome extension. I used to get the 401 status when my google token expired. So I added a check that if I am getting 401 status response of my request, then I will again authorize and get the token (it will happen in background) and continue my work.
Here is an example from my background.js
var authorizeWithGoogle = function() {
return new Promise(function(resolve, reject) {
chrome.identity.getAuthToken({ 'interactive': true }, function(result) {
if (chrome.runtime.lastError) {
alert(chrome.runtime.lastError.message);
return;
}
if (result) {
chrome.storage.local.set({'token': result}, function() {
resolve("success");
});
} else {
reject("error");
}
});
});
}
function getEmail(emailId) {
if (chrome.runtime.lastError) {
alert(chrome.runtime.lastError.message);
return;
}
chrome.storage.local.get(["token"], function(data){
var url = 'https://www.googleapis.com/gmail/v1/users/me/messages/id?alt=json&access_token=' + data.token;
url = url.replace("id", emailId);
doGoogleRequest('GET', url, true).then(result => {
if (200 === result.status) {
//Do whatever from the result
} else if (401 === result.status) {
/*If the status is 401, this means that request is unauthorized (token expired in this case). Therefore refresh the token and get the email*/
refreshTokenAndGetEmail(emailId);
}
});
});
}
function refreshTokenAndGetEmail(emailId) {
authorizeWithGoogle().then(getEmail(emailId));
}
I don't need to log in again and again manually. The google token is refreshed automatically in the background.
So this is what I believe would be the answer to my question.
Few important things to know
Chrome sign in is not same as gmail sign in. You could have UserA signed into chrome, while you plan to use the chrome extension with UserB. chrome.identity.getAuthToken won't work in that case, because it looking for the user signed into chrome.
For using other google accounts i.e. the one not signed into chrome, you would need to use chrome.identity.launchWebAuthFlow. Following are the steps you can use. I am referring the example given here (Is it possible to get an Id token with Chrome App Indentity Api?)
Go to google console, create your own project > Credentials > Create Credentials > OAuthClientID > Web Application. On that page in the field Authorized redirect URIs, enter the redirect url in the format https://.chromiumapp.org. If you don't know what chrome extension ID is, refer this (Chrome extension id - how to find it)
This would generate a client id that would go into your manifest file. Forget about any previous client id you might have created. Let's say in our example the client id is 9999.apps.googleusercontent.com
Manifest file:
{
"manifest_version": 2,
"name": "Test gmail extension 1",
"description": "description",
"version": "0.0.0.1",
"content_security_policy": "script-src 'self' 'unsafe-eval' https://apis.google.com; object-src 'self'",
"background": {
"scripts": ["dependencies/jquery.min.js", "background.js"]
},
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"identity",
"storage"
],
"oauth2": {
"client_id": "9999.apps.googleusercontent.com",
"scopes": [
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/tasks"
]
}
}
Sample code for getting user's info in background.js
jQuery.loadScript = function (url, callback) {
jQuery.ajax({
url: url,
dataType: 'script',
success: callback,
async: false
});
}
// This is the first thing that happens. i.e. loading the gapi client
if (typeof someObject == 'undefined') $.loadScript('https://apis.google.com/js/client.js',
function(){
console.log("gapi script loaded...")
});
// Every xx seconds this function runs with internally loads the tasks and gmail
// Once the gmail module is loaded it calls the function getLatestHistoryId()
setInterval(function() {
gapi.client.load('tasks', 'v1')
gapi.client.load('gmail', 'v1', function(){
getLatestHistoryId()
})
// your code goes here...
}, 10 * 1000); // xx * 1000 milsec
// This is the function that will get user's profile and when the response is received
// it'll check for the error i.e. error 401 through method checkForError
// If there is no error i.e. the response is received successfully
// It'll save the user's email address in localstorage, which would later be used as a hint
function getLatestHistoryId(){
var request = gapi.client.gmail.users.getProfile({
'userId': 'me'
});
request.execute(function(response){
console.log("User profile response...")
console.log(response)
if(checkForError(response)){
return
}
userEmail = response["emailAddress"]
localStorage.setItem("oooEmailAddress", userEmail);
})
}
// Now here check for the 401 error. If there's a 401 error
// It will call the signin method to get the token again.
// Before calling the signinWebFlow it will check if there is any email address
// stored in the localstorage. If yes, it would be used as a login hint.
// This would avoid creation of sign in popup in case if you use multiple gmail accounts i.e. login hint tells oauth which account's token are you exactly looking for
// The interaction popup would only come the first time the user uses your chrome app/extension
// I have left console statements which are self-explanatory
// Refer the documentation on https://developers.google.com/identity/protocols/OAuth2UserAgent >
// Obtaining OAuth 2.0 access tokens > OAUTH 2.0 ENDPOINTS for details regarding the param options
function checkForError(response){
if("code" in response && (response["code"] == 401)){
console.log(" 401 found will do the authentication again ...")
// Reading the data from the manifest file ...
var manifest = chrome.runtime.getManifest();
var clientId = encodeURIComponent(manifest.oauth2.client_id);
var scopes = encodeURIComponent(manifest.oauth2.scopes.join(' '));
var redirectUri = encodeURIComponent('https://' + chrome.runtime.id + '.chromiumapp.org');
// response_type should be token for access token
var url = 'https://accounts.google.com/o/oauth2/v2/auth' +
'?client_id=' + clientId +
'&response_type=token' +
'&redirect_uri=' + redirectUri +
'&scope=' + scopes
userEmail = localStorage.getItem("oooEmailAddress")
if(userEmail){
url += '&login_hint=' + userEmail
}
signinWebFlow(url)
return true
}
else{
console.log("Returning false from check error")
return false
}
}
// Once you get 401 this would be called
// This would get the access token for user.
// and than call the method getLatestHistoryId again
async function signinWebFlow(url){
console.log("THE URL ...")
console.log(url)
await chrome.identity.launchWebAuthFlow(
{
'url': url,
'interactive':true
},
function(redirectedTo) {
if (chrome.runtime.lastError) {
// Example: Authorization page could not be loaded.
console.log(chrome.runtime.lastError.message);
}
else {
var response = redirectedTo.split('#', 2)[1];
console.log(response);
access_token = getJsonFromUrl(response)["access_token"]
console.log(access_token)
gapi.auth.setToken({access_token: access_token});
getLatestHistoryId()
}
}
);
}
// This is to parse the get response
// referred from https://stackoverflow.com/questions/8486099/how-do-i-parse-a-url-query-parameters-in-javascript
function getJsonFromUrl(query) {
// var query = location.search.substr(1);
var result = {};
query.split("&").forEach(function(part) {
var item = part.split("=");
result[item[0]] = decodeURIComponent(item[1]);
});
return result;
}
Feel free to get in touch with me if you have any questions. I have spent quite a few days joining these dots. I wouldn't want someone else to do the same.

API key not valid error when trying to get profile data

I'm trying to test Google login API. I want to retrieve some basic data after the client logs.
I created a Google API Console project and client ID (https://developers.google.com/identity/sign-in/web/devconsole-project)
Here is google official code (https://developers.google.com/api-client-library/javascript/samples/samples#LoadinganAPIandMakingaRequest):
<html>
<head>
</head>
<body>
<script type="text/javascript">
function handleClientLoad() {
// Loads the client library and the auth2 library together for efficiency.
// Loading the auth2 library is optional here since `gapi.client.init` function will load
// it if not already loaded. Loading it upfront can save one network request.
gapi.load('client:auth2', initClient);
}
function initClient() {
// Initialize the client with API key and People API, and initialize OAuth with an
// OAuth 2.0 client ID and scopes (space delimited string) to request access.
gapi.client.init({
apiKey: '5v2RzP7-xyQGNjxrD5suoPL9',
discoveryDocs: ["https://people.googleapis.com/$discovery/rest?version=v1"],
clientId: '298062822261-e5c09q8191mkho0o7n3n3obiq2eq2p3f.apps.googleusercontent.com',
scope: 'profile'
}).then(function () {
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
// Handle the initial sign-in state.
updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
});
}
function updateSigninStatus(isSignedIn) {
// When signin status changes, this function is called.
// If the signin status is changed to signedIn, we make an API call.
if (isSignedIn) {
makeApiCall();
}
}
function handleSignInClick(event) {
// Ideally the button should only show up after gapi.client.init finishes, so that this
// handler won't be called before OAuth is initialized.
gapi.auth2.getAuthInstance().signIn();
}
function handleSignOutClick(event) {
gapi.auth2.getAuthInstance().signOut();
}
function makeApiCall() {
// Make an API call to the People API, and print the user's given name.
gapi.client.people.people.get({
resourceName: 'people/me'
}).then(function(response) {
console.log('Hello, ' + response.result.names[0].givenName);
}, function(reason) {
console.log('Error: ' + reason.result.error.message);
});
}
</script>
<script async defer src="https://apis.google.com/js/api.js"
onload="this.onload=function(){};handleClientLoad()"
onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>
<button id="signin-button" onclick="handleSignInClick()">Sign In</button>
<button id="signout-button" onclick="handleSignOutClick()">Sign Out</button>
</body>
</html>
ClientId and apikey are from my google api.
I receive error 400
{
"error": {
"code": 400,
"message": "API key not valid. Please pass a valid API key.",
"status": "INVALID_ARGUMENT",
"details": [
{
"#type": "type.googleapis.com/google.rpc.Help",
"links": [
{
"description": "Google developers console",
"url": "https://console.developers.google.com"
}
]
}
]
}
}
I don't understand what's wrong whit my api key. I followed all the steps from google documentation.
It seems you are using the client secret as API key. Isn't it?
To obtain an API key you must create it.
Under credentials (dev console) click on create credential, and then 'API Key'

#me called by anonymous error when using google plus api inside a gwt project

I'm using the google api javascript client to get information about the user profile inside a gwt project hosted in google app engine.
In localhost, the data is being retrieved correctly. I get a json with the google plus profile. When I deploy to appengine, the response is 401, "#me called by anonymous".
Here is my Code:
<script src="https://apis.google.com/js/client.js"></script>
<script type="text/javascript">
$(function() {
auth();
});
var API_KEY = "***************************************";
var CLIENT_ID = "************.apps.googleusercontent.com";
var scopes = 'https://www.googleapis.com/auth/plus.me';
function auth() {
var config = {
'client_id' : CLIENT_ID,
'scope' : scopes,
'key' : API_KEY,
};
gapi.client.load('plus', 'v1', function() {
api.client.setApiKey(API_KEY);
gapi.auth.authorize(config, function() {
var request = gapi.client.plus.people.get({
'userId' : 'me',
});
request.execute(function(resp) {
console.log(resp);
});
});
});
}
</script>
What i tried:
call to api.client.setApiKey at the begining.
create a new google api access with the google api console
update:
This is the complete response error message:
{
"error": {
"code": 401,
"message": "me called by anonymous",
"data": [
{
"domain": "global",
"reason": "authError",
"message": "me called by anonymous",
"locationType": "header",
"location": "Authorization"
}
]
},
"id": "gapiRpc"
}
There are other messages that may be related:
This is one of them:
Skipping duplicate osapi method definition chili.people.list on transport googleapis; others may exist, but suppressing warnings cb=gapi.loaded1 (línea 119)
Skipping duplicate osapi method definition pos.plusones.list on transport googleapis; others may exist, but suppressing warnings cb=gapi.loaded1 (línea 119)
Skipping duplicate osapi method definition chili.activities.list on transport googleapis; others may exist, but suppressing warnings cb=gapi.loaded1 (línea 119)
Skipping duplicate osapi method definition googleapis.newHttpRequest on transport googleapis; others may exist, but suppressing warnings
this is the other:
Invalid auth token. 1025***** vs 140186****
I could finally resolve the issue with the following settings or steps:
1) In the google apis console, I left the Redirect URIs section empty and completed the JavaScript origins section with the url of my site, repeating it with the https protocol:
JavaScript origins:
http://example.com
https://example.com
I put the script that loads the api before the end body tag:
<script src="https://apis.google.com/js/client.js"></script>
This script comes inside the body, before the api script:
<script type="text/javascript">
var API_KEY = "***************************************";
var CLIENT_ID = "************.apps.googleusercontent.com";
var scopes = 'https://www.googleapis.com/auth/plus.me';
function auth() {
var scopes = 'https://www.googleapis.com/auth/plus.me';
gapi.client.setApiKey(API_KEY);
window.setTimeout(checkAuth, 1000);
function checkAuth() {
gapi.auth.authorize({
client_id : CLIENT_ID,
scope : scopes,
immediate : false
}, handleAuthResult);
}
function handleAuthResult(authResult) {
if (authResult) {
makeApiCall();
} else {
checkAuth();
}
}
function makeApiCall() {
gapi.client.load('plus', 'v1', function() {
var request = gapi.client.plus.people.get({
'userId' : 'me'
});
request.execute(function(resp) {
$("#image").attr("src", resp.image.url);
});
});
}
}
</script>
Then I call the function auth() when the user clicks to see his picture.

Categories

Resources