I have created a chrome extension that reads email, does something and create tasks using google client API for javascript. I am using chrome identity for authentication and authorization. The extension works as expected. However, it keeps asking for sign every once in a while. What I want is to authorize the user in the background script so that they don't need to do it over and over again, after the initial authentication and authorization.
What I have done so far:
- I read that I need a refresh token to avoid this. However, refresh tokens are expected to be exchanged and stored on the server side and not client side (which wouldn't work because the background script is doing the job here which is client side)
- Using gapi.auth.authorize with immediate true. That gives error regarding external visibility. When I read else, they suggested using it inside a server. I am not sure how can I do that in a chrome extension.
- Turn interactive to false in getAuthToken, which starts giving error 401 due to authentication problem after the access token expires.
Following is the code I am using for authentication and authorization, with function onGoogleLibraryLoaded being called after loading the google api's client js file.
var signin = function (callback) {
chrome.identity.getAuthToken({interactive: true}, callback);
};
function onGoogleLibraryLoaded() {
signin(authorizationCallback);
}
var authorizationCallback = function (data) {
gapi.auth.setToken({access_token: data});
gapi.client.load('tasks', 'v1')
gapi.client.load('gmail', 'v1', function () {
console.log("Doing my stuff after this ..")
});
};
UPDATE: As per the suggestion in the answer, I made some changes to the code. However, I am still facing the same issue. Following is the updated code snippet
jQuery.loadScript = function (url, callback) {
jQuery.ajax({
url: url,
dataType: 'script',
success: callback,
async: false
});
}
//This is the first thing that happens. i.e. loading the gapi client
if (typeof someObject == 'undefined') $.loadScript('https://apis.google.com/js/client.js',
function(){
console.log("gapi script loaded...")
});
//Every 20 seconds this function runs with internally loads the tasks and gmail
// Once the gmail module is loaded it calls the function getLatestHistoryId()
setInterval(function() {
gapi.client.load('tasks', 'v1')
gapi.client.load('gmail', 'v1', function(){
getLatestHistoryId()
})
// your code goes here...
}, 20 * 1000); // 60 * 1000 milsec
// This is the function that will get user's profile and when the response is received
// it'll check for the error i.e. error 401 through method checkForError
function getLatestHistoryId(){
prevEmailData = []
var request = gapi.client.gmail.users.getProfile({
'userId': 'me'
});
request.execute(function(response){
console.log("User profile response...")
console.log(response)
if(checkForError(response)){
return
}
})
}
// Now here I check for the 401 error. If there's a 401 error
// It will call the signin method to get the token again.
// Before calling signin it'll remove the saved token from cache through removeCachedAuthToken
// I have also tried doing it without the removeCachedAuthToken part. However the results were the same.
// I have left console statements which are self-explanatory
function checkForError(response){
if("code" in response && (response["code"] == 401)){
console.log(" 401 found will do the authentication again ...")
oooAccessToken = localStorage.getItem("oooAccessTokenTG")
console.log("access token ...")
console.log(oooAccessToken)
alert("401 Found Going to sign in ...")
if(oooAccessToken){
chrome.identity.removeCachedAuthToken({token: oooAccessToken}, function(){
console.log("Removed access token")
signin()
})
}
else{
console.log("No access token found to be remove ...")
signin()
}
return true
}
else{
console.log("Returning false from check error")
return false
}
}
// So finally when there is 401 it returns back here and calls
// getAuthToken with interactive true
// What happens here is that everytime this function is called
// there is a signin popup i.e. the one that asks you to select the account and allow permissions
// That's what is bothering me.
// I have also created a test chrome extension and uploaded it to chrome web store.
// I'll share the link for it separately.
var signin = function (callback) {
console.log(" In sign in ...")
chrome.identity.getAuthToken({interactive: true}, function(data){
console.log("getting access token without interactive ...")
console.log(data)
gapi.auth.setToken({access_token: data});
localStorage.setItem("oooAccessTokenTG", data)
getLatestHistoryId()
})
};
Manifest goes like this:
{
"manifest_version": 2,
"name": "Sign in Test Extension ",
"description": "",
"version": "0.0.0.8",
"icons": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
},
"content_security_policy": "script-src 'self' 'unsafe-eval' https://apis.google.com; object-src 'self'",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"identity",
"storage"
],
"oauth2": {
"client_id": "1234.apps.googleusercontent.com",
"scopes": [
"https://www.googleapis.com/auth/gmail.readonly"
]
},
"background":{
"scripts" : ["dependencies/jquery.min.js", "background.js"]
}
}
Anyone else facing the same issue?