OAuth Flow not working when using Promise - javascript

I am building a simple chrome extension which integrates with Twitter using OAuth. I have slightly modified the Chrome OAuth Tutorial to integrate with Twitter. The extension is build in Reactjs+Flux.
When the user clicks on "Sign in with Twitter" button, an Action signin is triggered, which is declared as follows:
signin: function(){
ChromeUtils.connecttotwitter().then(alert("Step After Then"));
AppDispatcher.dispatch({actionType:TweetSmartActions.SIGN_IN,signedInTwitterUserId: response.user_id});
},
The ChromeUtils.connecttotwitter() is defined as follows:
var ChromeUtils = {
connecttotwitter: function () {
return new Promise(function(fulfill,reject){
var request = {
type : "background.twitterRequestToken",
};
alert("Before sendMessage");
chrome.runtime.sendMessage(request, function(response) {
if (response)
{
fulfill(response);
}
else
{
reject(response);
}
});
});
},
And the event listener onMessage is defined in the background.js as:
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
console.log("background.js: " + JSON.stringify(request));
var type = request.type;
if (type == "background.twitterRequestToken")
{
oauth.authorize(function(token,secret,userId,screenname){
sendResponse({success:true,userId:userId,screenName:screenname});
});
alert("Alerting before returning true");
return true;
}
When I click on the "Sign In With Twitter" button, the authentication flow does start and a new page opens. However, after I introduced the Promise, the new page does not redirect to the twitter oauth page. In fact, to debug that I have put the following alert statements in chrome_ex_oauth.js:
ChromeExOAuth.prototype.initOAuthFlow = function(callback) {
if (!this.hasToken()) {
var params = ChromeExOAuth.getQueryStringParams();
if (params['chromeexoauthcallback'] == 'true') {
var oauth_token = params['oauth_token'];
var oauth_verifier = params['oauth_verifier']
this.getAccessToken(oauth_token, oauth_verifier, callback);
} else {
var request_params = {
'url_callback_param' : 'chromeexoauthcallback'
}
this.getRequestToken(function(url) {
alert("Url after get request token " + url);
window.location.href = url;
alert(window.location.href);
}, request_params);
}
Here, the url in the first alert is the twitter oauth url but the second alert gives the Chrome extension Url -
chrome-extension://kiekipigbdldhggmlohbnhofnjhcbmem/chrome_ex_oauth.html
Why did the url not get assigned to window.location.href?
Any ideas on what might be happening?

The issue was not because I was using a Promise, but because when using Flux, the Action was being Dispatched before the response from the Promise was received and this was causing the app to hang somehow
signin: function(){
ChromeUtils.connecttotwitter().then(alert("Step After Then"));
AppDispatcher.dispatch({actionType:TweetSmartActions.SIGN_IN,signedInTwitterUserId: response.user_id});
},
In the above, the AppDispatcher.dispatch should be called in the function which is invoked on then.

Related

How to call a chrome API function from an injected function (through scripting.executeScript)?

I injected a function through scripting.executeScript to recursively create buttons. I want the buttons to call a Chrome API function (downloads.download) once clicking. To generate the buttons, I did:
// background.js
chrome.tabs.onUpdated.addListener( function (tabId, changeInfo, tab) {
if (changeInfo.status == 'complete') {
// intended to pass the below download function for it to be run later on <<
chrome.scripting.executeScript({
target: {tabId: tabId},
func: addButtons,
args: [download]
})
}
})
function addButtons(downloadCallback) {
// get all current posts
let posts = ...
// add download button to all posts
for (let i = 0; i < posts.length; i++) {
...
let url = tryQuickUrl(post);
...
let button = document.createElement("button");
button.innerText = "Download";
// intended to use the callback to the download function below <<<<<
button.onclick = function() { downloadCallback(url); };
post.appendChild(button);
}
}
The error here is that download can't be serialized, but if I try to use no arguments and attempt to call the function by button.onclick = function() { download(url); };, it gives Uncaught ReferenceError: download is not defined. If I try embedding the function into the anonymous function:
button.onclick = function() { chrome.downloads.download({ // error here
url: url,
filename: "test.jpg"
}); };
that gives an error Uncaught TypeError: Cannot read properties of undefined (reading 'download') (so you can't access API functions here?).
How would I call chrome.downloads.download from an injected script? Thanks!
If anyone sees this in the future, I just had to use Message Passing to pass messages between the extension and script. I used a listener to listen for incoming messages (from scripts), and handled them accordingly;
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension"
);
if (request.request === "downloadUrl") {
let [result, response] = download(request.url);
sendResponse({
success: result,
response: response,
});
}
}
);
and sent a message upon button clicking.
button.onclick = function() {
chrome.runtime.sendMessage({
request: "downloadUrl",
url: url,
}, function(response) {
console.log("WEEE", response.farewell);
});
};

How to run callback function from content script after loading page ? chrome extension

I want to run a callback function from content script after tab loading new page .
Here is my code :
content_script.js
chrome.runtime.onMessage.addListener(function(request, sender, callback) {
if (request.id == "content_script") {
// open google.com
chrome.runtime.sendMessage({
"id" : "openUrl",
"url" : "https://google.com"
}, function(response) {
});
// call background script
// go to the claim code page
chrome.runtime.sendMessage({
"id" : "background"
}, function() {
alert("test");
});
}
});
background.js
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
if (msg.id == "openUrl") {
var tabId = sender.tab.id;
var url = msg.url;
openUrl(tabId, url);
} else if (msg.id == "background") {
setTimeout(function() {
sendResponse();
}, 5000);
}
});
function openUrl(tabId, url) {
chrome.tabs.update(tabId, {
url : url,
active : false
}, function() {
console.log("open tab url callback");
});
};
I also uploaded the source code to google drive, you can download it using the bellow link :
https://drive.google.com/open?id=15zSn40z4zYkvCZ8B-gclzixvy6b0C8Zr
as you can see the alert test don't show !
However if I remove the code which open new url , then alert ("test") appear !
I am not sure why ! but it looks like javascript lost the reference to the call back function when I open new url .
How can I solve the problem ? what's the correct way ?
Thank you
The sendResponse function becomes invalid after the message callback returns, unless you return true from the event listener to indicate you wish to send a response asynchronously. (https://developer.chrome.com/extensions/runtime#event-onMessage)
Add return true; in background.js to make this handler asynchronous.
Now you get an error Attempting to use a disconnected port object in the sendResponse(); call of background.js, and yes, that's a result of the page navigating away and losing the context that the content script was running in.
There's no way to make this work: The context in which you wanted to call alert() simply doesn't exist anymore.
Try using chrome.tabs.sendMessage instead. But this means you have to set up the listener at the top level, and not inside of any callback. Message passing is hard.

Avoid popup blocker when opening new window that requires an async lookup

I've got a button in my Meteor application that does the following:
user clicks button > event calls method > method calls external api using http > external api returns single sign on link > method returns link > event opens new window (tab) with link as url
My problem is that the new tab is being blocked by a popup blocker even though it is based on user action
Here's the event code:
Template.welcome.events({
'click #accessLms': function(e) {
e.preventDefault();
​
var submitButton = $('#accessLms').button('loading');
​
Meteor.call('getLmsLink', function(error, portalLink) {
if(error) {
sAlert.error(error.message);
submitButton.button('reset');
} else if(portalLink) {
window.open(
portalLink,
'_blank'
);
submitButton.button('reset');
}
});
}
});
Here is the method:
Meteor.methods({
'getLmsLink': function () {
[set vars...]
try {
var response = HTTP.call( verb, wceaApiAddress + endPoint, {
headers: {
"Request-Time": timeStamp,
"Api-Key": key,
"Signature": hash
}
});
} catch(error) {
throw new Meteor.Error(501, 'There was a problem getting a link to the E-Learning Portal');
}
​
var result = JSON.parse(response.content);
var portalLink = result.records.accessLink;
return portalLink;
}
});
Basic approach:
On the click event in your app open a new window with a specific url to your own app
Include a route parameter that can be used in the new window, for example /redirect/token/
In the Template.onCreated event of the template used in that route, perform the method call and get the url and auth token to the 3rd party site.
Finally just set location = newSiteHref in that same code (in the new window) and redirect the user

Detect closing the "Request for Permission" window (Google Account login)

I'm implementing a Google Account login on my website and I've reached the following problem: when the user chooses to login using his/her Google Account, Google's Request for Permission popup window opens and, if the user closes it, I have no way of detecting this in any way.
Here's the relevant code, using GoogleAuth.attachClickHandler method:
gapi.load('auth2', function() {
var auth2 = gapi.auth2.init({
client_id: 'my-client-id.apps.googleusercontent.com',
cookiepolicy: 'single_host_origin',
});
auth2.attachClickHandler(button, {},
function(googleUser) {
successHandler(googleUser);
}, function(error) {
failureHandler(error);
});
});
If the user presses Accept, successHandler is called and if the user presses Cancel, failureHanlder is called. However, if the user simply closes this window, nothing happens and my website keeps waiting for some callback.
Is there any way to detect this, other than adding a timeout?
So, I guess I came up with a (very ugly) solution. Answers are still welcome, if someone knows of a better way.
Basically, I replace the default window.open function with my own and monitor if a Google window opens and then periodically check if it closed:
Function.prototype.isNative = function() {
return this.toString().indexOf('[native code]') > -1;
}
function registerGoogleTimeoutCallback() {
if (window.open.isNative()) {
var originalOpen = window.open;
window.open = function(URL, name, specs, replace) {
var newWindow = originalOpen(URL, name, specs, replace);
if (URL.indexOf('https://accounts.google.com/') === 0) {
var interval = setInterval(function() {
if (newWindow.closed) {
clearInterval(interval);
setTimeout(function() {
$(window).trigger('googleWindowClosed');
}, 2000);
}
}, 1000);
}
return newWindow;
}
} else {
throw 'window.open already replaced'
}
}
This way, I use $(window).on('googleWindowClosed', callback) to register a failure callback and also $(window).off('googleWindowClosed') to unregister it, in case of success.

login into the site with my javascript

i'm trying to login into the site with the following javascript. But, i'm loading only the complete page. I'm developing windows 8 app
(function () {
"use strict";
WinJS.UI.Pages.define("/pages/home/home.html", {
// This function is called whenever a user navigates to this page. It
// populates the page elements with the app's data.
ready: function (element, options) {
// TODO: Initialize the page here.
document.getElementById("bt_login").addEventListener("click", login, false);
}
});
})();
function login() {
var xhrDiv = document.getElementById("xhrReport");
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = dataLoaded;
xhr.open("POST", "http://www.160by2.com", true, <username>, <password>);
xhr.send();
function dataLoaded() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// OK
xhrDiv.innerHTML = window.toStaticHTML("response:" + xhr.responseText);
} else {
// not OK
xhrDiv.innerText = "failure";
}
}
};}
I want to dsiplay in xhrdiv.innerHTML as "LOGIN SUCCESS" or "LOGIN ERROR"
EDIT:
I tried the following code:
iframe = document.getElementById("ifra_op");
iframe.setAttribute("src", "http://www.160by2.com/index");
document.getElementById("op").appendChild(iframe);
iframe.contentWindow.document.getElementById("MobileNoLogin") = "<mobno>";
iframe.contentWindow.document.getElementById("LoginPassword") = "<pass>;
iframe.contentWindow.document.getElementsByName("LoginForm").click();
But, there is an error. It says "Javascript run time error:math is undefined"
"math" comes from the website. I don't know how to handle this. Also, the permission is denied. Why is that so?
You need to make sure you have a service (something like a web service) in the remote server to process the request.
In here what you can do is.
Create an iframe and set the src to 160by2;
Access the webpage using iframe.contentwindow, inject your code to fill up the forms and trigger the submit button.
Parse the received html to verify your login.
Use jquery , makes life easier.
var iframe = $("#ifra_op");
$(iframe).attr("src", "http://www.160by2.com/index");
$(iframe).attr("onload","submitForm();");
function submitForm(){
$(iframe.contentWindow.document).find("#MobileNoLogin").val("9999");
$(iframe.contentWindow.document).find("#LoginPassword").val("pass");
$('#button').click();
}

Categories