I'm building my first Firefox Web Extension, and I am integrating with an API using OAuth.
What happens is when the user clicks the icon in the toolbar, it gives them an authentication number to enter into a web page. They then do this, authorize my app and it stores an access token.
Where I'm getting stuck is currently my code relies on the popup window staying open to poll for a response from the responding API. I'm assuming I need to run the poll in a background file, but I'm not sure how I would do this.
My popup.js
// Set Vars
var successComp = 0;
// Poll to check for authentication
function pollAccessToken(device_code, interval) {
var request = new XMLHttpRequest();
request.open('POST', 'https://private-anon-535ecb43fa-trakt.apiary-mock.com/oauth/device/token');
request.setRequestHeader('Content-Type', 'application/json');
request.onreadystatechange = function () {
if (this.readyState === 4) {
switch (this.status) {
case 200:
// Get Response and put in array
var response = JSON.parse(this.responseText);
// Save Access Token and Refresh Token to storage
chrome.storage.local.set({"access_token": response.access_token, "refresh_token": response.refresh_token});
// Set success notification
chrome.runtime.sendMessage({"title": "Trakt for IMDb Authentication Success", "content": "Awesome, you've authenticated your Trakt account! Enjoy using Trakt for IMDb."});
// Set successComp to 1 so clearTimeout doesn't trigger
successComp = 1;
break;
case 400:
// Request still pending
setTimeout(pollAccessToken.bind(null, device_code).bind(null, interval), interval * 1000);
break;
case 404:
// Display Request not found/invalid error
chrome.runtime.sendMessage({"title": "Trakt for IMDb Authentication Error", "content": "Whoops, looks like there was an error authenticating your Trakt account. Please try again."});
break;
case 409:
// Code already used
chrome.runtime.sendMessage({"title": "Trakt for IMDb already approved", "content": "Whoops, looks like you've already approved this code. If you continue to have issues, please try again."});
break;
case 410:
// Code Expired
chrome.runtime.sendMessage({"title": "Trakt for IMDb Authentication Token Error", "content": "Whoops, looks like your token expired and there was an error authenticating your Trakt account. Please try again"});
break;
case 418:
// Code Denied
chrome.runtime.sendMessage({"title": "Trakt for IMDb Authentication Denied", "content": "Oh no, it looks like you denied us access to your account! If you didn't mean to do this, please try again."});
break;
case 429:
// Slow down polling
setTimeout(pollAccessToken.bind(null, device_code).bind(null, interval), interval * 1100);
break;
default:
// Request still pending
setTimeout(pollAccessToken.bind(null, device_code).bind(null, interval), interval * 1000);
break;
}
}
};
var body = {
'code': device_code,
'client_id': APP_KEY,
'client_secret': APP_SEC
};
request.send(JSON.stringify(body));
}
// Get and display authentication
function outputAuth() {
var request = new XMLHttpRequest();
request.open('POST', 'https://private-anon-535ecb43fa-trakt.apiary-mock.com/oauth/device/code');
request.setRequestHeader('Content-Type', 'application/json');
request.onreadystatechange = function () {
if (this.readyState === 4) {
// Get Response and put in array
var response = JSON.parse(this.responseText);
// Output authentication data
document.body.innerHTML = '<section id="traktLogo"><img src="../icons/trakt-logo.png" width="100px" height="100px" alt="Trakt" /></section><h1>Trakt Authentication</h1><h2>Enter the following code into the activation page.</h2><code>' + response.user_code + '</code><a href="' + response.verification_url + '" id="activation" target="_newtab">Activation Page</a>';
var pollAccess = pollAccessToken(response.device_code, response.interval);
setTimeout(stopPolling, response.expires_in * 1000);
}
};
var body = {
'client_id': APP_KEY
};
request.send(JSON.stringify(body));
}
// Stop Polling Function
function stopPolling() {
if (successComp === 0) {
// Response polling expired
clearTimeout(pollAccess);
chrome.runtime.sendMessage({"title": "Trakt for IMDb Authentication Timed Out", "content": "Whoops, looks like you took too long in authenticating your Trakt account. Please try again."});
}
}
// Check if Access Token Exists
chrome.storage.local.get("access_token", function (result) {
if (result.access_token && typeof result.access_token !== undefined) {
// Output Profile Page
document.body.innerHTML = '<p>Profile Page</p>';
} else {
// Output Authenctication Page
outputAuth();
}
});
My background.js
// Display a Notification
function notify(message) {
chrome.notifications.create({
"type": "basic",
"title": message.title,
"message": message.content,
"iconUrl": chrome.extension.getURL("icons/icon-256.png")
});
}
/*
Assign `notify()` as a listener to messages from the content script.
*/
chrome.runtime.onMessage.addListener(notify);
I'm happy enough to send the polling code to the background.js file, I'm just not quite sure how I would trigger it. It's also very important that I have the code that will stop the pollAccessToken
function from running after x amount of minutes as specified by the API docs.
Full source code can be previewed at Trakt for IMDb on Github
Polling API docs can be found at Trakt.tv API on Apiary