Is it possible to add google analytics into a chrome extension using manifest v3 ? How can i do that ?
I found this post from stackoverflow : Add Google Analytics to a Chrome Extension so i tried the code into the accepted answer, with
"content_security_policy": {
"extension_pages": "script-src 'self' https://ssl.google-analytics.com; object-src 'self'"
}
into my manifest.json, but when i load my extension i got this error : 'content_security_policy.extension_pages': Insecure CSP value "https://ssl.google-analytics.com" in directive 'script-src'.
I feel like it's not possible to use google analytics with chrome extension right now, but it's weird because into the chrome web store dashboard, we can see this field : https://imgur.com/a/PBHGOvu
Did i miss something ?
For using google analytics with mv3 the easiest way is to use measurement protocol.
In short, it allows you to send event tracking to Google Analytics through a normal POST call.
Here's the setup I used to track a click / other events:
The user clicks a button in the extension
The content script/popup.html sends a message to the service worker (background.js) using Message Passing saying the event should be recorded.
The service worker posts the event to Google Analytics using fetch()
And here's some sample code that runs in the service worker:
const ANALYTICS_PATH = "https://www.google-analytics.com/collect";
async function postData(url = '', data = {}) {
// Default options are marked with *
const response = await fetch(url, {
method: 'POST',
mode: 'no-cors',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
body: data
});
}
var gaParams = new URLSearchParams();
gaParams.append("v", 1);
gaParams.append("tid", "UA-XXXX-X");
gaParams.append("cid", xxxxx);
gaParams.append("t", "event");
gaParams.append("ec", "myEventCategory");
gaParams.append("ea", "myEventAction");
postData(ANALYTICS_PATH, gaParams)
Note:- This api does not give response codes if something is wrong, instead of using https://www.google-analytics.com/mp/collect directly on prod, one should first try https://www.google-analytics.com/debug/mp/collect.
PS. I found this solution from a comment on this source:-
https://www.indiehackers.com/post/how-to-add-google-analytics-with-manifest-v3-468f1750dc
I used this way to add google analytics to override.html(chrome_url_overrides) or popup.html in manifest V3:
override.html:
<head>
// 1- Download from 'https://ssl.google-analytics.com/ga.js' and use locally
<script src="./ga.js"></script>
// 2- Instead of use 'content_security_policy' property in 'manifest.json' add this:
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-eval' http://www.google-analytics.com https://example.com ;style-src 'self' 'unsafe-inline' http://www.google-analytics.com https://example.com; media-src *;img-src *">
// or
<meta http-equiv="Content-Security-Policy" content="default-src *;img-src * 'self' data: https:; script-src 'self' 'unsafe-inline' 'unsafe-eval' *;style-src 'self' 'unsafe-inline' *">
</head>
3- Create analytics-override.js:
var _gaq = _gaq || [];
_gaq.push(["_setAccount", "UA-01234567-89"]);
_gaq.push(["_trackPageview", "/override.html"]);
4- In override.js:
window.onload = function(){
const scriptTag = document.createElement("script");
scriptTag.src = chrome.runtime.getURL("./analytics-override.js");
scriptTag.type = "text/javascript";
document.head.appendChild(scriptTag);
}
I hope it helps you.
Related
Problem: I'm trying to have my webapp communicate with the background script of my chrome extension. However, when I invoke chrome.runtime.sendMessage() from my webapp, in order to send a message to the background.js script in my extension, the response callback never gets invoked. It also appears that background.js isn't receiving the message. Is what I'm trying to do possible? If so, given the information below, can anyone help me?
Background
My browser extension has a Newtab page, that I've configured to load my webapp. It does so by loading the webapp in an iframe:
<html>
<body style="margin:0px;padding:0px;overflow:hidden;">
<iframe src="https://www.example.com" frameBorder="0" style={{height:"100vh", width:"100vw", overflow: "hidden"}} height="100%" width="100%"/>
</body>
</html>
I have set the content-security-policy in manifest.json to allow this to happen:
"content_security_policy": {
"extension_pages": "object-src 'none'; child-src https://www.example.com https://example.com; frame-src https://www.example.com https://example.com; script-src 'self'"
}
I have enabled connectivity between my extension and my webapp using the externally_connectable manifest.json key:
"externally_connectable": {
"matches": [
"http://localhost:3000/*",
"https://*.example.com/*",
"https://example.com/*"
]
}
In my webapp (example.com), I have a script that tries to send a message to the background.js service worker. This script executes without any errors reported in the console. Note: this script is running on a webpage that is inside an iFrame in a page bundled with the extension.
function sendMessageToExtension() {
console.log("I got called");
if (chrome && chrome.runtime) {
console.log("Sending message to extension"); // this code is executed
chrome.runtime.sendMessage(
"extension_id",
{ type: "example.message.type"},
{includeTlsChannelId : true},
function(rsp) {
if(arguments.length === 0) {
console.log(chrome.runtime.lastError);
}
console.log(rsp.message)
} // this call back never gets invoked.
);
} else {
// TODO: need to detect this in production.
console.log("no chrome.runtime");
}
}
My background code to handle the message looks like this:
chrome.runtime.onMessageExternal.addListener(
(request, sender, sendResponse) => {
console.log("onMessageExternal invoked") // never gets called
if (request.type === "example.message.type") {
sendResponse({message: "message received by background worker."});
}
}
)
Other Notes: This doesn't appear to work even when I load the webpage by navigating to it in the URL bar, so I'm not entirely sure what's going on.
The answer to my question is yes you can! And as it turns out, the code I posted is exactly how you would do it.
I'm writing a Thunderbird Extension to add an email to a Rackspace email account spam blocklist.
Rackspace provides an API for adding emails to an accounts blocklist.
My Ajax GET Request is:
$.ajax({
beforeSend: function(request)
{
console.log("beforeSend");
request.setRequestHeader("X-Api-Signature", Signature);
request.setRequestHeader("Accept", "text");
request.setRequestHeader("withCredentials", true);
request.setRequestHeader('crossOrigin','anonymous'),
console.log("beforeSend Request %o: " , request);
return true;
},
contentType: 'application/x-www-form-urlencoded',
crossDomain: true,
dataType: 'jsonp',
method: 'GET',
type: 'GET',
url: Api_Obj.Url,
When I submit the Ajax GET request with the url:
https://api.emailsrvr.com/v1/customers/123456/domains/sandypondconsulting.com/spam/blocklist/EglogicsSoftech#outlook.com
(account number 123456 is placeholder for actual account number for security purposes)
I get:
Loading failed for the with source “https://api.emailsrvr.com/v1/customers/123456/domains/sandypondconsulting.com/spam/blocklist/EglogicsSoftech#outlook.com?callback=jQuery36003174572175672028_1625074250859&_=1625074250860”.
I've set the manifest:
"content_security_policy" : "script-src 'self' https://api.emailsrvr.com; object-src 'self'",
The Ajax error function parameters have these values:
msg: error
error: error
When I click on the url in the Loading failed error I get:
<unauthorizedFault xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://api.emailsrvr.com/v0/Schemas" code="403">
<message>Authentication failed.</message>
<details>6/30/2021 1:34:21 PM</details>
</unauthorizedFault>
The unauthorized fault tells me that I've probably incorrectly generated the X-Api-Signature required by the Rackspace API.
So, looks like I have two problems:
Loading failed for script.
2) UnauthorizedFault
I'm looking to solve the script loading problem first. And, then figure out the UnauthorizedFault 2nd.
Thanks, Ed
I'm trying to use axios with pug template but facing a problem.
here is my code:
base.pug
doctype html
html
head
block head
meta(charset='UTF-8')
meta(name='viewport' content='width=device-width, initial-scale=1.0')
link(rel='stylesheet' href='/css/style.css')
link(rel='shortcut icon' type='image/png' href='/img/favicon.png')
link(rel='stylesheet' href='https://fonts.googleapis.com/css?family=Lato:300,300i,700')
title Natours | #{title}
body
// HEADER
include _header
// CONTENT
block content
h1 This is a placeholder heading
// FOOTER
include _footer
script(src='https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js')
script(src='/js/login.js')
and in login.js
const login = async (email, password) => {
try {
const res = await axios({
method: 'POST',
url: 'http:127.0.0.1:3000/api/v1/login',
data: {
email,
password
}
});
console.log(res);
} catch (err) {
console.log(err);
}
};
document.querySelector('.form').addEventListener('submit', e => {
e.preventDefault();
const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
login(email, password);
});
but everytime i'm trying to submit the form i'm getting this error in console.log
"
Refused to load the script 'https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
"
You need to add Content Security Policy headers for script-src to allow your site to load scripts from a different domain, in this case cdnjs.cloudflare.com.
You can do this either in your webserver (if you're using one) or in the Node.js application.
Content-Security-Policy: script-src <source>;
In Node/Express it would be something like:
res.setHeader('Content-Security-Policy', 'script-src cdnjs.cloudflare.com');
You can also use a library such as: https://www.npmjs.com/package/express-csp-header
I am trying to post an event to my IBM Social Business SmartCloud account. I have been able to grant access to the application and get the access and refresh tokens. but when posting the new even I get a 401 error "No 'Access-Control-Allow-Origin' header is present on the requested resource."
function postEvent(){
var postString = '{'+
'"actor": {'+
'"id": "#me"'+
'},'+
'"verb": "post",'+
'"title": "${share}",'+
'"content":"This event is my <b>first content</b>",'+
'"updated": "2012-01-01T12:00:00.000Z",'+
'"object": {'+
'"summary": "My Summary",'+
'"objectType": "note",'+
'"id": "someid",'+
'"displayName": "My displayName",'+
'"url": "mydomain.com"'+
'}}';
$.ajax({
url: 'https://apps.na.collabserv.com/connections/opensocial/basic/rest/activitystreams/#me/#all?format=json&access_token=<access_token>',
data: postString,
contentType: 'application/json',
method: 'POST',
dataType: 'json',
headers: {
// Set any custom headers here.
// If you set any non-simple headers, your server must include these
// headers in the 'Access-Control-Allow-Headers' response header.
'Content-Type: application/json',
'Origin': 'https://mydomain.com/',
'Access-Control-Allow-Headers' :'*',
'Access-Control-Allow-Origin': '*'
}
}).done(function(data) {
console.log(data);
});
}
so this is the proxy method using file_get_contents I used to get it working in php, cURL did not work.
$post = file_get_contents('https://apps.na.collabserv.com/connections/opensocial/basic/rest/activitystreams/#me/#all?format=json',FALSE,stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' => "Authorization: Bearer $access_token\r\n".
"Content-type: application/json\r\n".
"Content-length: " . strlen($json_data) . "\r\n",
'content' => $json_data,
),
)));
now the issues is other people can not see my posting even though we're following each other.
got the json structure for embedding a webpage with og tags for a video into my ibm sb activity stream. so the video opens directly in my stream with a thumbnail without linking out in a new window
$json_data = '{"content":"https://mydomain.com/somevideo/",
"attachments":[{"objectType":"link",
"displayName":"My Display Name",
"url":"https://mydomain.com/somevideo/",
"summary":"My summary",
"image":{
"url":"{thumbnail}/api/imageProxy?url=http%3a%2f%2fmydomain.com%2fsomevideo%2fthumbnail.jpg",
"alt":"My Display Name"
},
"connections":{
"video":{
"connections":{"mime-type":"application/x-shockwave-flash"},
"width":"853",
"height":"480",
"url":"https://mydomain.com/somevideo/"
}
}
}
]
}';
and you post to this url:
https://apps.na.collabserv.com/connections/opensocial/rest/ublog/#me/#all?format=json
As long as your code is hosted on a different domain than apps.na.collabserv.com you won't be able to access the REST API with JavaScript alone
In this situation, it is the browser blocking you. The cross origin header won't work, because the backend is not configured to enable CORS requests.
You can work around this by accessing the REST API trough an ajax proxy, deployed on the same domain as your page
I know that there's a bunch of questions about the "not allowed by Access-Control-Allow-Origin." error.
But I've tried some of them without success. :(
Some appointments:
I'm trying to build a dev-tools-tab extension
I can touch flickr API like the example shows
I can't reach localhost
Already tried several permission wildcards
http://localhost/
http://*/
*://*/
Already tried pack'd and unpack'd extensions
currently, manifest.json has
"version": "0.0.1",
"manifest_version": 2,
"devtools_page": "components/devtools.html",
"permissions": [
"http://*/"
]
devtools.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script src="../js/devtools.js"></script>
</body>
</html>
and, devtools.js
(function (window) {
"use strict";
var xhr1, xhr2, url;
xhr1 = new window.XMLHttpRequest();
xhr2 = new window.XMLHttpRequest();
xhr1.onreadystatechange = function () {
if (this.readyState === 4) {
console.log('flickr ok');
}
};
xhr2.onreadystatechange = function () {
console.log(this.readyState);
if (this.readyState === 4) {
console.log(this.responseText);
}
};
url = 'https://secure.flickr.com/services/rest/?' +
'method=flickr.photos.search&' +
'api_key=90485e931f687a9b9c2a66bf58a3861a&' +
'text=' + encodeURIComponent('cats') + '&' +
'safe_search=1&' +
'content_type=1&' +
'sort=interestingness-desc&' +
'per_page=20';
xhr1.open('get', url, true);
xhr1.send();
url = 'http://apache.local';
xhr2.open('get', url, true);
xhr2.setRequestHeader('Origin', url);
xhr2.send();
Chrome console output:
1 devtools.js:12
Refused to set unsafe header "Origin" devtools.html:1
XMLHttpRequest cannot load http://apache.local/. Origin chrome-extension://nafbpegjhkifjgmlkjpaaglhdpjchlhk is not allowed by Access-Control-Allow-Origin. devtools.html:1
4 devtools.js:12
flickr ok devtools.js:8
Chrome version:
28.0.1500.20 dev
Thanks in any advice.
I've got it!
Actually, the problem is that I'm trying to perform XHR requests on devtools page and it seems to have no permissions to bypass cross-origin-access policies like a popup page do.
Devtools tab tries are also unsuccessful.
edit
Is an stage-permission related. Not wildcard-permission. As I've said, I've managed to perform queries on some domains, yet not having they explicitly on my permissions array.
The problem really lies on the type of script running.
The same script, if used as a popup, work'd fine. So, I've tried as an background-script with success too! I was facing the problem that devtools_page and related doesn't have such permissions...
The APIs available to extension pages within the Developer Tools window include all devtools modules listed above and chrome.extension API. Other extension APIs are not available to the Developer Tools pages, but you may invoke them by sending a request to the background page of your extension, similarly to how it's done in the content scripts.
http://developer.chrome.com/extensions/devtools.html
That level of script denies non explicit cross xhrs.
Solved the problem putting the requests in a background script and using messages api.
Thank you!