Chrome extension: chrome local storage is not set instantly - javascript

I am having strange issues with Chrome Local storage setting and retrieval.
In background.js I am setting it when a certain URL's HTML is fetched once the page loading is completed and then in content.js I am fetching values from local storage. At times it is stored and fetched instantly while other times results.html is undefined. And if I use chrome.storage.local.clear() it makes it more worst, make you to refresh the page 2-3 times at least. Below is my code:
background.js
chrome.runtime.onMessage.addListener(
async function(request, sender, sendResponse) {
// Reset storage
// chrome.storage.local.clear() // it is lagging refreshing values
sendResponse("bar")
// Check whether it is correct URL
var url = 'http://localhost:8000/get.php?url='+request
console.log('URL for AJAX =',url)
var result = await sendReq(url)
var json_result = JSON.parse(result)
var status = json_result['status']
var rules = []
console.log('Status = '+status)
if(status == 'ok') {
rules = json_result['msg']['rules']
chrome.storage.local.set({'rules': rules}, function() {});
url = 'http://localhost:8000/read.php?url='+request
result = await sendReq(url)
// console.log(result)
chrome.storage.local.set({'html': result}, function() {}); // this goes undefined if the URL of visiting page is changed.
} else {
// To check on content script
chrome.storage.local.set({'html': '__FAIL__'}, function() {});
}
}
);
content.js (Using JQuery)
$(function() {
// console.clear()
console.log('The Agile Super Cluster extension cleared all previous output')
chrome.storage.local.get(['html','rules'], function(result) {
// Do not perform things below if it is not a relevant Super Cluster URL
if(result.html == '__FAIL__' || typeof (result.html) == 'undefined') {
return
}
.....
// Out of onReady() block
chrome.runtime.sendMessage(
url,
function (response) {
console.log('Sending Response')
console.log(response);
}
);

The solution is to use messaging correctly so you can wait on the result reliably.
remove chrome.storage, it's not necessary for this task;
remove async from onMessage listener (why?) and use a separate async function to get info;
return the result via sendResponse + return true.
content.js:
chrome.runtime.sendMessage(url, res => {
console.log(res);
if (res.html) {
// use `res.html` here
}
});
background.js:
const API_GET = 'http://localhost:8000/get.php?url=';
const API_READ = 'http://localhost:8000/read.php?url=';
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
getInfo(request).then(sendResponse);
return true; // keep the channel open for asynchronous sendResponse
});
async function getInfo(url) {
const res = JSON.parse(await sendReq(`${API_GET}${url}`));
return res.status === 'ok' ? {
rules: res.msg.rules,
html: await sendReq(`${API_READ}${url}`),
} : {};
}

Related

How to run this function on an interval?

I am trying to run everything within the checkUser() function but its not running on the interval specified. Maybe there is a better way to do this? I am just trying to check the address every few minutes. The line const accounts = await ethereum.request({ method: 'eth_accounts' }); does get the address and works fine if I just run it once. Just need to try do it on an interval though. Full code below:
function checkUser()
{
window.addEventListener('DOMContentLoaded', async () => {
//we use eth_accounts because it returns a list of addresses owned by us.
const accounts = await ethereum.request({ method: 'eth_accounts' });
//We take the first address in the array of addresses and display it
// getAccountsResult.innerHTML = accounts[0] || 'not able to get accounts';
console.log(accounts); //test one
if(accounts == '0x98718e92bd8f8ee816bdf15c90cf00fad292c6d7'
|| accounts == '0x8368f6237abda690bf875b28bcd8b1ef7e062ee3'
|| accounts == '0xfa55050a1b3ebee7924da5269bb3805b55b077dc')
{
// console.log("you are going in!");
// window.location.href = "members_home.html";
}
else
{
console.log(accounts); //test one
window.location.href = "normal_home.html";
}
});
}
setInterval(checkUser, 50);
This function adds an eventListener on DOMContentLoaded. So when you run this function at an interval you create a new eventlistener every 50ms. If you want to run the function inside eventListener at the specified interval you can put it in a seperate function.
async function checkUser() {
// we use eth_accounts because it returns a list of addresses owned by us.
const accounts = await ethereum.request({ method: 'eth_accounts' });
// We take the first address in the array of addresses and display it
// getAccountsResult.innerHTML = accounts[0] || 'not able to get accounts';
console.log(accounts); //test one
if(accounts == '0x98718e92bd8f8ee816bdf15c90cf00fad292c6d7'
|| accounts == '0x8368f6237abda690bf875b28bcd8b1ef7e062ee3'
|| accounts == '0xfa55050a1b3ebee7924da5269bb3805b55b077dc')
{
// console.log("you are going in!");
// window.location.href = "members_home.html";
}
else
{
console.log(accounts); //test one
window.location.href = "normal_home.html";
}
}
window.addEventListener('DOMContentLoaded', checkUser);
setInterval(checkUser, 50);
This way the function gets executed when the dom content is loaded and every 50ms.
Why are DOMContentLoaded and setInterval inside your checkUser function ?
Your instructions order is all wrong.
Reading your code, I think you don't even want to use setInterval...
I guess what you want to do is :
wait for DOMContentLoaded to define checkUser
checkUser to do the ethereum.request
window.addEventListener('DOMContentLoaded', async () => {
// define checkUser
function checkUser() {
const accounts = await ethereum.request({ method: 'eth_accounts' });
//We take the first address in the array of addresses and display it
// getAccountsResult.innerHTML = accounts[0] || 'not able to get accounts';
console.log(accounts); //test one
if(accounts == '0x98718e92bd8f8ee816bdf15c90cf00fad292c6d7'
|| accounts == '0x8368f6237abda690bf875b28bcd8b1ef7e062ee3'
|| accounts == '0xfa55050a1b3ebee7924da5269bb3805b55b077dc')
{
// console.log("you are going in!");
// window.location.href = "members_home.html";
}
else
{
console.log(accounts); //test one
window.location.href = "normal_home.html";
}
}
// run checkUser
checkUser();
}

Simple MSAL Login/Authentication in JavaScript

I'm trying to do a simple login to Azure AD using the MSAL for JavaScript v2.0 library. We want users to be able to authenticate into our site with their work Microsoft accounts. All I need to do is be able to authenticate/login the user via Microsoft, and if they can login via their work Microsoft account, then they're granted access to our site.
I'm using the Javascript library and have followed the code from the Github page and while the login prompt is coming up, afterwards I have no idea how to check if the user is signed in.
Here's the code I'm using, which is basically what's in the sample code from Github:
<script type="text/javascript" src="https://alcdn.msauth.net/browser/2.15.0/js/msal-browser.min.js"></script>
<script type="text/javascript">
const msalConfig = {
auth: {
clientId: "[ClientID goes here]",
authority: "https://login.microsoftonline.com/[tenant ID]",
knownAuthorities: ["login.microsoftonline.com"],
protocolMode: "OIDC",
redirectUri: "[page on our site that doesn't have MSAL auth, listed in Azure Reply URLs]"
},
cache: {
cacheLocation: "sessionStorage", // This configures where your cache will be stored
storeAuthStateInCookie: true, // Set this to "true" if you are having issues on IE11 or Edge
},
system: {
loggerOptions: {
loggerCallback: (level, message, containsPii) => {
if (containsPii) {
return;
}
switch (level) {
case msal.LogLevel.Error:
console.error(message);
return;
case msal.LogLevel.Info:
console.info(message);
return;
case msal.LogLevel.Verbose:
console.debug(message);
return;
case msal.LogLevel.Warning:
console.warn(message);
return;
}
}
}
}
};
// Add here scopes for id token to be used at MS Identity Platform endpoints.
const loginRequest = {
scopes: ["User.Read"]
};
const silentRequest = {
scopes: ["openid", "profile", "User.Read"]
};
const ua = window.navigator.userAgent;
const msie = ua.indexOf("MSIE ");
const msie11 = ua.indexOf("Trident/");
const msedge = ua.indexOf("Edge/");
const isIE = msie > 0 || msie11 > 0;
const isEdge = msedge > 0;
let signInType;
let accountId = "";
let credType = "";
// Create the main myMSALObj instance
const myMSALObj = new msal.PublicClientApplication(msalConfig);
// Register Callbacks for Redirect flow
myMSALObj.handleRedirectPromise().then(handleResponse).catch((error) => {
console.log(error);
});
function handleResponse(resp) {
alert("beginning handleResponse");
if (resp !== null) {
accountId = resp.account.homeAccountId;
credType = resp.account.credentialType;
myMSALObj.setActiveAccount(resp.account);
alert("response not null (already auth), accountId: " + accountId + ", credType: " + credType);
}
else {
const currentAccounts = myMSALObj.getAllAccounts();
if (!currentAccounts || currentAccounts.length < 1) {
alert("currentAccounts null/empty, going to signIn");
signIn("loginRedirect");
//return;
}
else if (currentAccounts.length > 1) {
// add choose account code here
alert("currentAccounts has multiple");
}
else if (currentAccounts.length === 1) {
const activeAccount = currentAccounts[0];
myMSALObj.setActiveAccount(activeAccount);
accountId = activeAccount.homeAccountId;
credType = activeAccount.credentialType;
alert("currentAccounts == 1; accountId: " + accountId + ", credType: " + credType);
}
}
}
async function signIn(method) {
signInType = isIE ? "loginRedirect" : method;
if (signInType === "loginPopup") {
return myMSALObj.loginPopup(loginRequest).then(handleResponse).catch(function (error) {
console.log(error);
});
}
else if (signInType === "loginRedirect") {
return myMSALObj.loginRedirect(loginRequest);
}
}
function signOut() {
const logoutRequest = {
account: myMSALObj.getAccountByHomeId(accountId)
};
myMSALObj.logoutRedirect(logoutRequest);
}
async function getTokenPopup(request, account) {
request.account = account;
return await myMSALObj.acquireTokenSilent(request).catch(async (error) => {
console.log("silent token acquisition fails.");
if (error instanceof msal.InteractionRequiredAuthError) {
console.log("acquiring token using popup");
return myMSALObj.acquireTokenPopup(request).catch(error => {
console.error(error);
});
}
else {
console.error(error);
}
});
}
// This function can be removed if you do not need to support IE
async function getTokenRedirect(request, account) {
request.account = account;
return await myMSALObj.acquireTokenSilent(request).catch(async (error) => {
console.log("silent token acquisition fails.");
if (error instanceof msal.InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
console.log("acquiring token using redirect");
myMSALObj.acquireTokenRedirect(request);
}
else {
console.error(error);
}
});
}
So what happens upon going to this page is I get the two alerts saying "beginning handleResponse" and then "currentAccounts null/empty, going to signIn."
Then I'm redirected to MS sign-in page which I do with my work MS account. This succeeds.
I'm then redirected to the site I have listed in Azure Reply URLs, another page on our site that isn't secure and has no Azure login code.
The problem is I have no idea where to check that the user is signed in. If I try and check immediately after the signIn("loginRedirect") call in the handleResponse() function on the first page, the code never gets hit apparently. If I try and check on the page I'm redirected to, by instantiating the MSAL object and calling getAllAccounts(), this returns null.
It seems maybe on the page I'm redirected to I could call the ssoSilent() function (seems like this can check if user is authenicated?), but this requires a username/AccountId parameter. Well I don't frickin know this if a user hasn't (possibly) been authenticated yet! I don't really understand that.
So I don't know. It's probably something stupid I'm doing but I'm a pretty basic JavaScript person and am pretty much a total noob with authenication stuff. Any help would be epic.

fastest way sending datas from Injected to content Script

I am building a chrome extension and getting datas from a webpage(a list with 20 elements and hundreds of pages ) through my injected script.
This injected script is sending the datas via chrome storage to the background script.
The background script is calculating an array.
Then sending the result to my content script where a mutation observer is waiting for an event where it’s using the calculated array.
I am sending all these data’s around by chrome.local.storage.set / get.
Because so there are so many different specs around, my mutation observer has an timeout of 1second for every loaded page / mutation because else the data’s are loaded to slow and it still has the data’s from the page before.
Is there a faster way sending these data’s around besides the chrome storage ?
Injected.js
//Gettig Datas before as constant players
const payload = {
PlayerList: players.map(player => {
return {
ID: player.id, //resource Id
Price: player.bin // Price
};
}),
};
var payload2 = Object.values(payload);
chrome.runtime.sendMessage(extId, {type: 'GetPlayerList', data: payload2});
background.js
chrome.runtime.onMessageExternal.addListener(
function (request, sender, sendResponse) {
if (request.type === "GetPlayerList") {
var playertest = request;
var playertest2 = playertest.data[0];
var playerbase = chrome.storage.local.get("datafut", function (data) {
playerbase = data.datafut;
var data = mergeArrays(playertest2, playerbase);
chrome.storage.local.set(
{
playerDataListCurrent: data
});
console.log(mergeArrays(playertest2, playerbase));
})
}
});
function mergeArrays(playertest2, playerbase) { //Calculate an array
by filter the IDs from a 30Element Array and a 500Element Array}
mergeArrays function: array
content.js
var s = document.createElement('script');
s.src = chrome.extension.getURL('injected.js');
s.dataset.variable = JSON.stringify(chrome.runtime.id);
s.asnyc = false; (document.head ||
document.documentElement).appendChild(s); s.onload = function () { s.remove(); };
var observeTransferList = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
mutation.addedNodes.forEach(function (node) {
if (node.nodeType === 1 && node.matches(".has-auction-data")) {
$(node).css("height", "28");
setTimeout(() => {
var playerDataListCurrent;
chrome.storage.sync.get(function (items) {
platform = items.platform;
discountprice = items.discountprice;
average = parseInt(items.average);
percentage = parseInt(items.percentage);
if (percentage === 0) {
//Data get
chrome.storage.local.get(function (items) {
playerDataListCurrent = items.playerDataListCurrent;
for (i = 0; i < playerDataListCurrent.length; i++) {
//DO STUFF
}
})
}, 1000); // Timeout for unknown delay. Else its sometimes getting datas from array calculated before
}
});
});
});

Using YouTube data api within chrome extension embedded iframe

I am trying to create a chrome extension that adds an iframe to an existing website and populates it with data from the YouTube data api however I am having trouble with restrictions imposed by the chrome extension content policy.
My current issue comes from user login, I need to use the gapi to get the users OAuth2 key however it appears that gapi isn't supported within sandboxed environments. Is it possible to use the YouTube data api without gapi? Or more directly is it possible have the code below work inside an iframe placed there by a chrome extension? This example comes from the Google docs.
<html><head><title>Google APIs - Sample JS Page</title></head>
<body>
<script>
/***** START BOILERPLATE CODE: Load client library, authorize user. *****/
// Global variables for GoogleAuth object, auth status.
var GoogleAuth;
/**
* Load the API's client and auth2 modules.
* Call the initClient function after the modules load.
*/
function handleClientLoad() {
gapi.load('client:auth2', initClient);
}
function initClient() {
// Initialize the gapi.client object, which app uses to make API requests.
// Get API key and client ID from API Console.
// 'scope' field specifies space-delimited list of access scopes
gapi.client.init({
'clientId': 'REPLACE_ME',
'discoveryDocs': ['https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest'],
'scope': 'https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/youtubepartner'
}).then(function () {
GoogleAuth = gapi.auth2.getAuthInstance();
// Listen for sign-in state changes.
GoogleAuth.isSignedIn.listen(updateSigninStatus);
// Handle initial sign-in state. (Determine if user is already signed in.)
setSigninStatus();
// Call handleAuthClick function when user clicks on "Authorize" button.
$('#execute-request-button').click(function() {
handleAuthClick(event);
});
});
}
function handleAuthClick(event) {
// Sign user in after click on auth button.
GoogleAuth.signIn();
}
function setSigninStatus() {
var user = GoogleAuth.currentUser.get();
isAuthorized = user.hasGrantedScopes('https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/youtubepartner');
// Toggle button text and displayed statement based on current auth status.
if (isAuthorized) {
defineRequest();
}
}
function updateSigninStatus(isSignedIn) {
setSigninStatus();
}
function createResource(properties) {
var resource = {};
var normalizedProps = properties;
for (var p in properties) {
var value = properties[p];
if (p && p.substr(-2, 2) == '[]') {
var adjustedName = p.replace('[]', '');
if (value) {
normalizedProps[adjustedName] = value.split(',');
}
delete normalizedProps[p];
}
}
for (var p in normalizedProps) {
// Leave properties that don't have values out of inserted resource.
if (normalizedProps.hasOwnProperty(p) && normalizedProps[p]) {
var propArray = p.split('.');
var ref = resource;
for (var pa = 0; pa < propArray.length; pa++) {
var key = propArray[pa];
if (pa == propArray.length - 1) {
ref[key] = normalizedProps[p];
} else {
ref = ref[key] = ref[key] || {};
}
}
};
}
return resource;
}
function removeEmptyParams(params) {
for (var p in params) {
if (!params[p] || params[p] == 'undefined') {
delete params[p];
}
}
return params;
}
function executeRequest(request) {
request.execute(function(response) {
console.log(response);
});
}
function buildApiRequest(requestMethod, path, params, properties) {
params = removeEmptyParams(params);
var request;
if (properties) {
var resource = createResource(properties);
request = gapi.client.request({
'body': resource,
'method': requestMethod,
'path': path,
'params': params
});
} else {
request = gapi.client.request({
'method': requestMethod,
'path': path,
'params': params
});
}
executeRequest(request);
}
/***** END BOILERPLATE CODE *****/
function defineRequest() {
// See full sample for buildApiRequest() code, which is not
// specific to a particular API or API method.
buildApiRequest('GET',
'/youtube/v3/search',
{'maxResults': '25',
'part': 'snippet',
'q': 'surfing',
'type': ''});
}
</script>
<button id="execute-request-button">Authorize</button>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></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></html>
I will refrain from posting any of my code as it's a total mess and will just confuse anybody who reads it. I also can't help but feel I am going in the wrong direction.
Any pointers as to how to achieve this would be greatly appreciated.

What's the best(right) way to write a polling method (with Typescript & AngularJS)?

I am trying to write a polling method that polls a server periodically to check whether a zip file has already been created or not.
What I want to accomplish are the following:
Calls(ajax) an API that creates a zip file on server
Calls(ajax) another API that checks if the zip file has already been created (polling method)
Some subsequent process
Here is my code snippet ↓
var success: boolean = false;
//1. requests a server to create a zip file
this.apiRequest.downloadRequest(params,ApiUrl.URL_FOR_DOWNLOAD_REQUEST)
.then((resObj) => {
var apiRes: IDownloadService = resObj.data;
if (apiRes.status[0].statusCode == "000") {
success = true;
} else {
//Error
}
}).then(() => {
if (success) {
//2. polls the server to check if the zip file is ready
<- Polling method↓ ->
this.polling(params).then((zipUrl) => {
console.log(zipUrl); //always logs zipUrl
//some subsequent process...
});
}
});
Could anyone give some examples of polling method that would work in this case?
Added:
private polling(params: any): ng.IPromise<any> {
var poller = () => this.apiRequest.polling(params, ApiUrl.URL_FOR_POLLING);
var continuation = () => poller().then((resObj) => {
var apiRes: IDownloadService = resObj.data;
if (apiRes.zipFilePath == "") {
return this.$timeout(continuation, 1000);
} else {
return apiRes.zipFilePath;
}
})
var result: ng.IPromise<any> = continuation();
return result;
}
Basically abstract the methods out as shown below:
let poll = () => this.apiRequest.downloadRequest(params,ApiUrl.URL_FOR_DOWNLOAD_REQUEST)
let continuation = () => poll().then((/*something*/)=> {
/*if still bad*/ return continuation();
/*else */ return good;
})
continuation().then((/*definitely good*/));
Update
As requested in the comment below:
return this.$timeout(continuation, 1000);
This is needed to get angular to kick off a digest cycle.

Categories