I'm trying to build a Chrome extension and I need to load/save some data from/to the https://address.com. My manifest.json is as below:
"permissions": [
"geolocation",
"unlimitedStorage",
"https://address.com/"
],
"content_security_policy": "script-src 'self'; object-src 'self';connect-src 'self' https://address.com/;"
But I'm running into the below issue when I'm trying to load, I'm not sure what is the issue and how to fix it.
Refused to connect to 'https://address.com/graphql' because it violates the following Content Security Policy directive: "connect-src 'self' blob: .....
Please suggest, thanks.
Fetch Code:
try {
fetch("https://address.com/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ query: "{ getList(page:1, limit: 10) { t } }" })
})
.then(res => res.json())
.then(res => console.log("getList: ", res.data));
} catch (error) {
console.error("getListError:", error);
}
I just noticed another thing, the code works fine on sites like stackoverflow.com but giving error on facebook, twitter ... any idea.
It is possible (though risky) to allow a webextension in the browser to load javascript from a remote url using the following directive in the manifest.json file - by including something like:
"content_security_policy": "script-src 'self' https://example.com; object-src 'self'"
as documented here.
My question is how do I then go ahead and insert the javascript source file remotely housed on https://example.com ?
I tried in Firefox by adding at the top of my extension code:
var script_insert='<script src="https://example.com/dict.js"></script>';
document.write( script_insert + '\n' );
where dict.jscontains:
window.dict_L1C = {
"a" : "hello",
"b" : "world"
}
but that fails with warnings:
Content Security Policy: Ignoring “'unsafe-inline'” within script-src: ‘strict-dynamic’ specified
Content Security Policy: Ignoring “https:” within script-src: ‘strict-dynamic’ specified
I guess I'm doing it the wrong way, or I am missing something important.
UPDATE:
I am now looking at the solutions provides here
This is how from within a webextension I managed to read a dictionary fromhttps://www.remote-dictionary.com/dict.json.
First create the manifest.js that contains a permissions section as follows:
{
"manifest_version": 2,
"name": "mywebextension",
"version": "1.0",
"description": "reads a dictionary from a remote website and uses it on another",
"background": { "scripts": [ "background.js" ] },
"content_scripts": [ { "matches": ["*://my-insertion-site.com/*"], "js": [ "content.js" ] } ],
"permissions": [ "storage", "https://www.remote-dictionary.com/*" ]
}
Next create the background.js file that accesses the remote dictionary and then stores it locally in the browser.
if (typeof chrome !== 'undefined') { browser = chrome}
function fetchDict() {
let req = new Request( "https://www.remote-dictionary.com/dict.json", {
method: 'GET',
headers: { 'Accept': 'application/json' },
redirect: 'follow',
referrer: 'client'
});
fetch(req).then(function(response) {
// .json returns another promise
return response.json();
}).then(function(data) {
browser.storage.local.set({data: data}); // set storage for content-script
}).catch(error => { console.log(error); });
}
fetchDict();
And the content.js script:
if (typeof chrome !== 'undefined') { browser = chrome }
browser.storage.local.get({data: ""},function(data){ // get from local storage
window.dict=data.data;
)
})
// later access the dictionary values:
window.dict[ "my_key"];
What is the correct way to pass the code from myscript.js to background.js?
I have
myscript.js
function fox2(){
{
var url = 'https://sub.domain.com'
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var parser = new DOMParser();
source_code = parser.parseFromString(xhr.responseText, "text/html");
var a = source_code;
console.log(a)
}
};
xhr.send();
}
}
fox2();
Background.js
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null, {file: "nuevo.js"});
});
Error: Cross-Origin Read Blocking (CORB) blocked cross-origin response with MIME type text/html
I know that the solution is to use the code in Background, But How should I do it?.
Thanks.
I can't get a user's id_token (https://developers.google.com/accounts/docs/CrossClientAuth) from the Chrome identity api (https://developer.chrome.com/apps/identity).
I can get an access_token using the chrome identity sample when the oauth section in the manifest is:
"oauth2": {
"client_id": "<chrome-app-client-id>.apps.googleusercontent.com",
"scopes": ["https://www.googleapis.com/auth/plus.login"]
}
But when I try to get the id_token the same way I get it on my android client a get the error:
"OAuth2 request failed: Service responded with error: 'invalid scope: {0}'"}
The manifest section is now:
"oauth2": {
"client_id": "<chrome-app-client-id>.apps.googleusercontent.com",
"scopes": ["audience:server:client_id:<app-engine-client-id>.apps.googleusercontent.com"]
}
On Android I get the id_token by passing the same scope string to android.gms.auth.GoogleAuthUtil.getToken(), but I can't get it to work with the chrome identity api.
Is it possible to get an id_token with Chrome App Indentity Api? If not, how can I get an id_token for my Chrome app?
Thanks for your help!
I've came to the same problem yesterday and since I've found a solution, I might as well share it, as it wasn't that obvious. As far as i know Google does not provide a direct and documented way to do this, but you can use the chrome.identity.launchWebAuthFlow() function.
First you should create an Web application credentials in google console and add the following url as a valid Authorized redirect URI: https://<EXTENSION_OR_APP_ID>.chromiumapp.org. The URI does not have to exist, chrome will just catch the redirect to this URL and call your callback function later.
manifest.json:
{
"manifest_version": 2,
"name": "name",
"description": "description",
"version": "0.0.0.1",
"background": {
"scripts": ["background.js"]
},
"permissions": [
"identity"
],
"oauth2": {
"client_id": "<CLIENT_ID>.apps.googleusercontent.com",
"scopes": [
"openid", "email", "profile"
]
}
}
background.js:
// Using chrome.identity
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');
var url = 'https://accounts.google.com/o/oauth2/auth' +
'?client_id=' + clientId +
'&response_type=id_token' +
'&access_type=offline' +
'&redirect_uri=' + redirectUri +
'&scope=' + scopes;
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];
// Example: id_token=<YOUR_BELOVED_ID_TOKEN>&authuser=0&hd=<SOME.DOMAIN.PL>&session_state=<SESSION_SATE>&prompt=<PROMPT>
console.log(response);
}
}
);
Google OAuth2 API (for OpenID Connect) documentation can be found here: https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters
PS: If you don't need the oauth2 section in your manifest. You can safely omit it, and provide the identifiers and scopes in code only.
EDIT:
For those interested, you don't need the identity API. You can even access the token using a little trick with tabs API. The code is a little longer, but you have better error messages and control. Keep in mind that in the following example, you need to create Chrome App credentials.
manifest.json:
{
"manifest_version": 2,
"name": "name",
"description": "description",
"version": "0.0.0.1",
"background": {
"scripts": ["background.js"]
},
"permissions": [
"tabs"
],
"oauth2": {
"client_id": "<CLIENT_ID>.apps.googleusercontent.com",
"scopes": [
"openid", "email", "profile"
]
}
}
background.js:
// Using chrome.tabs
var manifest = chrome.runtime.getManifest();
var clientId = encodeURIComponent(manifest.oauth2.client_id);
var scopes = encodeURIComponent(manifest.oauth2.scopes.join(' '));
var redirectUri = encodeURIComponent('urn:ietf:wg:oauth:2.0:oob:auto');
var url = 'https://accounts.google.com/o/oauth2/auth' +
'?client_id=' + clientId +
'&response_type=id_token' +
'&access_type=offline' +
'&redirect_uri=' + redirectUri +
'&scope=' + scopes;
var RESULT_PREFIX = ['Success', 'Denied', 'Error'];
chrome.tabs.create({'url': 'about:blank'}, function(authenticationTab) {
chrome.tabs.onUpdated.addListener(function googleAuthorizationHook(tabId, changeInfo, tab) {
if (tabId === authenticationTab.id) {
var titleParts = tab.title.split(' ', 2);
var result = titleParts[0];
if (titleParts.length == 2 && RESULT_PREFIX.indexOf(result) >= 0) {
chrome.tabs.onUpdated.removeListener(googleAuthorizationHook);
chrome.tabs.remove(tabId);
var response = titleParts[1];
switch (result) {
case 'Success':
// Example: id_token=<YOUR_BELOVED_ID_TOKEN>&authuser=0&hd=<SOME.DOMAIN.PL>&session_state=<SESSION_SATE>&prompt=<PROMPT>
console.log(response);
break;
case 'Denied':
// Example: error_subtype=access_denied&error=immediate_failed
console.log(response);
break;
case 'Error':
// Example: 400 (OAuth2 Error)!!1
console.log(response);
break;
}
}
}
});
chrome.tabs.update(authenticationTab.id, {'url': url});
});
First, I assume that in your manifest.json snippet you don't mean that your client_id is literally "<chrome-app-client-id>.apps.googleusercontent.com. It should be something like 9414861317621.apps.googleusercontent.com -- something you got from the Developer Console, or whatever Google site you used to register the app.
Assuming the above is OK, and you have the client_id right, and the scope right, you get what's called an "OAuth2 access token" with a call to chrome.identity.getAuthToken. As you don't show us any JavaScript code, I can't tell if this is what you're doing. The access token you get you need to save for subsequent use when you call an API function. For example:
var access_token;
chrome.identity.getAuthToken(
{
'interactive': true
},
function(token) {
access_token = token;
// do something if you like to indicate
// that the app is authorized
}
);
Then, when you make an API call, you supply that access token, like this:
var url = 'https://www.googleapis.com/' + method;
Ajax.ajaxSend(url, "json",
function (status, response) {
if (response && response.error && response.error.message)
errorCallback(response.error.message);
else if (status == 200)
successCallback(response);
else
errorCallback('Result code: ' + status);
},
function (e) {
if (errorCallback)
errorCallback('Communication error');
},
{
Authorization: 'Bearer ' + access_token
}
);
Ajax.ajaxSend is my own function:
var Ajax = (function () {
var api = {
ajaxSend: function (url, responseType, successCallback, errorCallback, headers) {
var req = new XMLHttpRequest();
req.onload = function (e) {
successCallback(req.status, req.response);
};
req.onerror = errorCallback;
req.responseType = responseType ? responseType : "text";
req.open("get", url);
if (headers)
for (var v in headers)
req.setRequestHeader(v, headers[v]);
req.send();
}
};
return api;
})();
The other undefined functions also are what you'd expect. The third argument to Ajax.ajaxSend is a header to be sent along. (Sorry, I don't have time to develop standalone code just for this answer.)
I hope the above is useful.
I guess it depends on why one would want token id, but in my case
access_token was enough to authorize user - by pulling user info from https://www.googleapis.com/oauth2/v2/userinfo?alt=json ( with Authorization header = access_token).
If I run this Code:
function sendRequest() {
var req = new XMLHttpRequest();
req.open("GET", "http://www.google.com/search?XXXX", true);
req.onreadystatechange = function() {
if (req.readyState == 4) {
if (req.status == 200) {
alert( req.responseText );
}
}
};
req.send();
}
I get this error:
XMLHttpRequest cannot load http://www.google.com/search?XXXX. Origin chrome-extension://loifkhcbcjakjhcmecadcbdgfldfjfce is not allowed by Access-Control-Allow-Origin.
why??
In manifest file I have:
"permissions": [
"<all_urls>"
]
Demi
That will work for a packaged Chrome app, but not a hosted one.