GMail API access from chrome extension? 403 Forbidden - javascript

I have an application that accesses Google APIs out of a Chrome extension via the workflow outlined here.
Chrome Extensions OAuth Tutorial
The basics of the workflow are to initialize the OAuth flow
var oauth = ChromeExOAuth.initBackgroundPage({
'request_url': 'https://www.google.com/accounts/OAuthGetRequestToken',
'authorize_url': 'https://www.google.com/accounts/OAuthAuthorizeToken',
'access_url': 'https://www.google.com/accounts/OAuthGetAccessToken',
'consumer_key': '{MY_CLIENT_ID}',
'consumer_secret': '{MY_CLIENT_SECRET}',
'scope': 'https://www.google.com/m8/feeds/ https://apps-apis.google.com/a/feeds/emailsettings/2.0/ https://mail.google.com/',
'app_name': 'Gmail Plugin',
'callback_page': 'src/google-oauth/chrome_ex_oauth.html'
});
Upon installing the extension, the user is taken to dialog page to authenticate and agree to the scopes I ask for. From here I infer that my consumer key and secret are OK. I have allowed access to the GMail, Contacts, and Admin SDK in the Google Developers console.
Prior to this I had requests working with the Contacts API and Admin SDK API. I'm now trying to add some features that utilize the Gmail REST API.
The next step in setting up a request is to make a request from the background page.
function getSentEmails() {
var emailCollection;
var url = "https://www.googleapis.com/gmail/v1/users/me/messages";
var request = {
'method': 'GET',
'parameters': {
'labelIds': 'SENT'
}
};
var callback = function(response, xhr) {
emailCollection = JSON.parse(response);
console.dir(emailCollection);
}
oauth.sendSignedRequest(url, callback, request);
};
The way the signed requests work is to call a method to complete the next step of the OAuth dance,
oauth.authorize(function() {
getSentEmails();
});
This results in a 403 Forbidden every time. I seem to have no issue accessing the other APIs I mentioned though this OAuth flow. I've allowed the scope in my manifest.json
manifest.json
"permissions": [
"tabs",
"storage",
"https://mail.google.com/*",
"https://www.google.com/m8/feeds/*",
"https://apps-apis.google.com/a/feeds/emailsettings/2.0/*",
"https://www.googleapis.com/gmail/v1/users/*",
"https://www.googleapis.com/auth/gmail.modify/*",
"https://www.googleapis.com/auth/gmail.compose/*",
"https://www.googleapis.com/auth/gmail.readonly/*",
"https://www.google.com/accounts/OAuthGetRequestToken",
"https://www.google.com/accounts/OAuthAuthorizeToken",
"https://www.google.com/accounts/OAuthGetAccessToken"
]
I tried an alternate method of building the HTTP request as outlined in the link above.
function stringify(parameters) {
var params = [];
for(var p in parameters) {
params.push(encodeURIComponent(p) + '=' +
encodeURIComponent(parameters[p]));
}
return params.join('&');
};
function xhrGetSentEmails() {
var method = 'GET';
var url = 'https://www.googleapis.com/gmail/v1/users/me/messages';
var params = {'labelIds': 'SENT'};
var callback = function(resp, xhr) {
console.log(resp);
}
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(data) {
callback(xhr, data);
};
xhr.open(method, url + '?' + stringify(params), true);
xhr.setRequestHeader('Authorization', oauth.getAuthorizationHeader(url, method, params));
xhr.send();
}
I get the same 403 doing this.
I believe I'm authenticating properly though, because if I change
xhr.setRequestHeader('Authorization', oauth.getAuthorizationHeader(url, method, params));
to
xhr.setRequestHeader('Authorization','foo' + oauth.getAuthorizationHeader(url, method, params));
I get a 401 Unauthorized instead.
Again, no trouble accessing the other APIs I mentioned.
Any input would be much appreciated.

This question is probably fairly obscure, so I'll share how I ended up resolving it.
I moved my chrome extensions OAuth 2.0 workflow to the newer (since Chrome 29) chrome.identity setup for apps and extensions.
Detailed instructions for setting up OAuth 2.0 for an extension are here.
Chrome Identity API User Authentication
Now I can use
chrome.identity.getAuthToken(function(token) {
// Do HTTP API call with token
});
And none of my HTTP requests come up forbidden (403) anymore.
Hope this is helpful to extension builders out there!

Related

Javascript Fetch - How to Fix Failed to Fetch Error (probably from making HTTP request on HTTPS site?)

This is the React app (and code). It generates 3 random objects (from list of 500+ objects stored locally). I want to call IconFinder API when the random objects are chosen and just display the icons then (instead of finding 500 icons beforehand).
I'm running it on Windows 10 with Google Chrome Version 84.0.4147.89 (Official Build) (64-bit). The code is hosted on Codesandbox. You can take a look at all the code here or just the output and Chrome dev tools here
Demo XHR GET request that they show in their Docs
var data = null;
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function () {
if (this.readyState === this.DONE) {
console.log(this.responseText);
}
});
xhr.open("GET", "https://api.iconfinder.com/v4/icons/search?query=arrow&count=10");
xhr.setRequestHeader("authorization", "Bearer <API KEY HERE>");
xhr.send(data);
My Fetch GET request
let url = "https://api.iconfinder.com/v4/icons/search?query=arrow&count=1&premium=0";
let h = new Headers();
h.append("Accept", "application/json");
h.append(
"authorization",
"Bearer <API KEY HERE>"
);
let req = new Request(url, {
method: "GET",
headers: h,
credentials: "include"
});
fetch(req)
.then(response => {
if (response.ok) {
return response;
} else {
throw Error(`Request rejected with status ${response.status}`);
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.error("Error: ", err.message));
Output: Console reads Error: Failed to fetch
In the Chrome Dev Tools, Network Tab (Screenshot 1) I see a file of type fetch (Screenshot 2) and file of type json (Screenshot 3 and 4) have something wrong with them.
What I've tried
Fetch GET requests work for Codesandbox with the Star Wars API (no authentication required), but not for MovieDB API (authentication in url not headers - ex. https://api.themoviedb.org/3/search/movi?api_key=<INSERT KEY>?<other parameters here> ). It threw an error because response.ok wasn't true.
I tried fetching data from the IconFinder API and the MovieDB API on other online IDEs (like Scrimba.com and repl.it). Those also didn't work.
I've tried using XHR requests and fetch await requests instead of fetch requests chained with then promises on different platforms. No luck.
I've double-checked how to set the authentication headers with the IconFinder API documentation.
I've also double-checked that my API key is registered to the url I'm calling it from and not restricted to any domains (Screenshot 5)
Looked through these Forums: it looks like I have something going wrong with making fetch requests from a HTTPS page, but I'm not sure what I need to change in my code based on that information. I've tried reloading the page on Codesandbox as http, but it redirects me to https automatically (Source 1, Source 2, Source 3, Source 4)
This is my first time making API calls so I don't really know what the possibilities are for what could go wrong and how to fix it. I've tried looking at some other StackOverflow threads but I'm having a hard time understanding them. Next, I'm going to try transferring the React App to a local file and seeing if I can make fetch requests there. Anyone have any thoughts on what else I should try?
The issue was based on a CORS error. A friend of mine told me to test the api call to:
https://api.iconfinder.com/v4/icons/search?query=arrow&count=1&premium=0
via https://reqbin.com/. It worked on the testing website.
Then, he told me to just make the request to that url with this Cors Anywhere tool. Like this:
https://cors-anywhere.herokuapp.com/https://api.iconfinder.com/v4/icons/search?query=arrow&count=1&premium=0
That worked on my local development environment. Still not sure exactly HOW it works.

Why does this email sending function not work?

Heres my email sending function:
function send() {
var key = "dJdJekCVAFIqvUJ13DEczZjgIh_4MyeIGEHz2GBYKFe";
var message_name = "defender_send_message";
var data = {};
data.value1 = document.getElementById('textBox').value;
data.value2 = localStorage.getItem("AdminsEmail");
var url = "https://maker.ifttt.com/trigger/" + message_name + "/with/key/" + key;
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (xmlhttp.status == 200) {
console.log("Message Sent");
}
}
}
xmlhttp.open('POST', url, true);
xmlhttp.responseType = 'json';
xmlhttp.send(new FormData(data));
}
I wanted to create an email sending function with only pure js, not jquery or anything. I get the following errors when i click send:
(ignore the first error i fixed that already)
I had a jquery function that worked (but i had to get rid of it):
var message = localStorage.getItem("Message");
console.log(message + localStorage.getItem("AdminsEmail"));
var key = "dJdJekCVAFIqvUJ13DEczZjgIh_4MyeIGEHz2GBYKFe"; // << YOUR KEY HERE
var message_name = "defender_send_message"; // << YOUR MESSAGE NAME HERE
var url = "https://maker.ifttt.com/trigger/" + message_name + "/with/key/" + key;
$.ajax({
url: url,
data: {value1: message,
value2: localStorage.getItem("AdminsEmail")},
dataType: "jsonp",
complete: function(jqXHR, textStatus) {
console.log("Message Sent");
}
});
why would this work and my other function not?
EDIT 2 : Since it seems the endpoint doesn't actually return JSON, I think your original jQuery code wasn't correct either. You need to do more research into this iftt.com platform and how to use it. From what I can tell, it's meant to be used in a mobile app, not in the browser- it would be a normal POST XHR then, and CORS doesn't apply to mobile apps. They have this page for testing the endpoint- notice that it gives you an example using curl, a command-line tool, where again CORS doesn't apply. So I think you need to rethink things, this service is not designed to be used from a browser, like you are trying to do.
EDIT: since it turns out you are actually trying to use JSONP and not a plain XHR, all you need to do is implement that without jQuery- create a script tag with the server's URL and add a URL parameter to define your callback function to handle the data. This answer should give you the solution.
In your case the code might look like this :
http://www.codeply.com/go/bp/VRCwId81Vr
function foo(data)
{
// do stuff with JSON
console.log(data)
}
var script = document.createElement('script');
script.src = "https://maker.ifttt.com/trigger/defender_send_message/with/key/"+
"dJdJekCVAFIqvUJ13DEczZjgIh_4MyeIGEHz2GBYKFe?callback=foo";
document.getElementsByTagName('head')[0].appendChild(script);
Note that this doesn't work for me(but with your code, you would get Message sent printed to the console, so maybe you thought it was working?)- the response isn't JSON. Most likely the endpoint isn't actually meant to be used for JSONP?
My answer below only applies if you are trying to do a regular XHR in a browser without JSONP.
This happens because of the Cross Origin Resource Sharing policy of your browser. Your code is hosted at localhost, and it is trying to access a resource hosted at maker.ifttt.com through an XmlHttpRequest. In order to allow this to happen, the server at maker.ifttt.com would need to be configured to allow access from the localhost origin. Presumably you can not make that change as you don't control that server.
In your case, the best solution would be to make the request to maker.ifttt.com through your own server- CORS doesn't apply for server-to-server requests. Send the XmlHttpRequest to your server, take the data regarding the email from the request URL parameters, and then make the request to maker.ifttt.com using that data.

Tumblr API OAuth with local test server

I'm trying to get posts from my tumblr blog and put them on a separate website page. To do this I registered an app on their OAuth page, but I'm having some issues when I try to actually request the authorization. My console spits out this message—
XMLHttpRequest cannot load https://api.tumblr.com/v2/blog/myblog.tumblr.com/posts?api_key=(MY_KEY).
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://127.0.0.1:63342' is therefore not allowed access.
(I've omitted the key value here for obvious reasons).
Now, my site isn't actually live yet, and I have a test server running at localhost:63342 but on their OAuth app settings page I have these options that I must fill out—
Is there a way to get this to work with my local test server? Here's the code that I'm calling to request access.
var request = new XMLHttpRequest();
request.open('GET', 'https://api.tumblr.com/v2/blog/myblog.tumblr.com/posts?api_key=(API_KEY)', true);
request.onload = function() {
if (request.status >= 200 && request.status < 400) {
// Success!
var data = JSON.parse(request.responseText);
console.log(data);
} else {
// We reached our target server, but it returned an error
console.log('server error');
}
};
request.onerror = function() {
// There was a connection error of some sort
console.log("ERROR!!!");
};
request.send();
Any help would be appreciated! Thanks!
Turn out my issue was using JSON instead of JSONP, which bypasses the Access-Control-Allow-Origin issue. I downloaded this JSONP library for Javascript ( I am not using JQuery in my project ) and was able to access the api by writing this:
JSONP('https://api.tumblr.com/v2/blog/myblog.tumblr.com/posts?api_key=(API_KEY)'
, function(data) {
console.log(data);
});
Which returns a JSON Object which I can then data from using something like data.response or whatever objects are in the array.
Again, my issue was not Tumblr not authorizing my test server. I was able to get this to work using 127.0.0.1:port as my application website & callback url.

Office App Ajax with auth header, connecting to secured web api 2 backend with cors enabled

I'm building a content panel app for Office.
Part of that requires authenticating an account against a server. I've implemented my own AuthorizationFilterAttribute on my web api 2 controllers, and called config.EnableCors(new EnableCorsAttribute("*", "*", "*")); on my web api registration.
I built a test script that fires a simple get at an endpoint and I can get it to work in chrome and ie.
var token = btoa(username + ':' + password);
$.ajax({
url: 'http://www.someendpoint/',
type: 'GET',
beforeSend: function (request) {
request.setRequestHeader("Authorization", "Basic " + token);
},
success: function (data) {
return true;
},
error: function (data) {
return false;
}
});
Problem is that it doesn't work in the app.
The error: function is being hit and the data contains "Error: Access is denied.\r\n"
I've attempted to use $.support.cors = true; in addition to ensuring the domain name is given permission in the App Manifest.
Anyone have any ideas on what else to try?
So it turns out that in the project properties, office apps enforce SSL by default, which is great to be honest, but not ideal when testing against a non SSL domain.
The answer to this is to adjust the project settings as shown below.

Google Contacts API with Google JavaScript Client Lib

I am trying to work with the Google Contacts API v3.
Because of the OAuth2 authentication and authorization I'm started with the Google APIs Client Library for JavaScript.
I have no problems with that part of the API access.
But after doing the auth part I don't know what to do next. Can I use the google-api-javascript-client for the Google Contacts API v3? In the list of the supported Google APIs by the javascript-client the contacts API does not appear. But I have full access with the OAuth 2.0 Playground tool.
I am really confused which client is working best with the latest Google Contacts API. What is about the gdata-javascript-client?
To use the v3 contacts api with the authentication token provided by gapi (Google JS client lib) this one is helpful, using alt=&json
$.getJSON('https://www.google.com/m8/feeds/contacts/default/full/?access_token=' +
authResult.access_token + "&alt=json&callback=?", function(result){
console.log(JSON.stringify(result));
});
I know it's an old question but this question shows up when looking on how to read the contacts information from a Google account.
If you only need to access the contact to import it or show the email, phone numbers, or other information and you don't need to modify it, you can use the People API (https://developers.google.com/people/). For javascript you can check the samples tab.
I created a gist, which is almost the sample from Google but adding the requestField parameters.
https://gist.github.com/ddbb1977/7a4b408ed17c7249252a
The problem you are encountering is that the Contacts API v3 is an older API that works with the deprecated GData Client Library. Therefore it is incompatible with the newer Google APIs JavaScript Client.
For now you will need to load and use the GData Client library. For further information on the difference between the GData library and the Google APIs client, please refer to this recent SO question: gapi.client.load versus google.load
Unfortunate Google Contacts API does not work with the new Javascript Client Library. It only works with GData Client Library. I tried working GData Client Library, but it is difficult as you get warnings in the documentation at every juncture that the library has been deprecated.
Therefore, I used a hydrid,
using the new Client Library, to get an authentication.
Use a URL to get the contacts
Unfortunately, because of cross domain restrictions you need to use JSONP, otherwise the browser fails.
<script src="https://apis.google.com/js/client.js"></script>
.....
function contactsInit() {
var clientId = 'YOURCLIENTID.apps.googleusercontent.com';
var scopes = 'https://www.google.com/m8/feeds';
gapi.auth.authorize({
client_id: clientId, scope: scopes, immediate: false},
handleAuthResult);
function handleAuthResult(authResult) {
if (authResult && !authResult.error) {
var url =
"https://www.google.com/m8/feeds/contacts/default/" +
"full?alt=json-in-script&access_token=" +
authResult.access_token +
"&max-results=7000&v=3.0";
var myJSONP = new Request.JSONP({
url: url,
callbackKey: 'jsoncallback',
data: {
},
onRequest: function(url){
// a script tag is created with a src equal to url
},
onComplete: function(data){
// the request was completed.
}
}).send();
}
}
}
function Skeleton() {}
if (!gdata) {
var gdata = new Skeleton();
gdata.io = new Skeleton();
gdata.io.handleScriptLoaded = function(data) {
processContacts(data);
}
}
Notes:
I use Mootools for JSONP but you could also use jquery or vanilla js with How to make a JSONP request from Javascript without JQuery?
You need to provide your own YOURCLIENTID, and define the processContacts function.
The gdata.io.handleScriptLoaded(data) is necessary since this what the url expects during callback.
I use a limit of 7000, but I don't think it is necessary.
If you don't want to use JSONP you could forward the access_token to your webserver, and process the URL there, e.g. with cURL or with Node.js just replace json-in-script with json.
In json-in-script is important on a browser since otherwise the browser croaks.
Thanks to the other answers on this page, that pointed me in the right direction.
I hope that Google will make the Contacts API capable with the new Javascript Client Library. I hope that others will other be able to use this solution in the meantime.
For fetching list of contacts using Google plus use this :-
<script src="https://apis.google.com/js/client.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script>
function auth() {
var config = {
'client_id': 'OAUTH_CLIENT_ID',
'scope': 'https://www.google.com/m8/feeds'
};
gapi.auth.authorize(config, function() {
fetch(gapi.auth.getToken());
});
}
function fetch(token) {
$.ajax({
url: "https://www.google.com/m8/feeds/contacts/default/full?access_token=" + token.access_token + "&alt=json",
dataType: "jsonp",
success:function(data) {
console.log(JSON.stringify(data));
}
});
}
In the HTML Body :-
<button onclick="auth();">GET CONTACTS FEED</button>
The output will have as a field with the contact containing the phone number.
Make sure to get the client id from google developer console with proper redirect uri.
This is what we found to work to get individual data:
var data = (JSON.stringify(data.feed.entry[0].gd$email, null, 4));
console.log(data);
If you run JSON.stringify(data) you can see all of the headers that you can call on.
google contact v3 is your best friend for this job
here you can find all posible request endpoint
https://developers.google.com/google-apps/contacts/v3/
like for all contact list this is the end point
https://developers.google.com/google-apps/contacts/v3/#retrieving_all_contacts
After get authentication you can do get request to this url to get all contact list
this is an example in python http://code.google.com/p/gdata-python-client/source/browse/samples/contacts/contacts_example.py

Categories

Resources