From 27c8526c2ac91e8ee37e6893b5eb35c1f4206d8a Mon Sep 17 00:00:00 2001 From: KatieFrogs <23621460+KatieFrogs@users.noreply.github.com> Date: Fri, 11 Mar 2022 14:20:22 +0300 Subject: [PATCH 1/3] Gpicker API Changes Hopefully that is what is being changed, I do not think there is a way to test this properly until the old API closes down Resources: - https://developers.googleblog.com/2022/03/gis-jsweb-authz-migration.html - https://developers.google.com/drive/api/v3/quickstart/js --- public/src/js/customsongs.js | 6 ++++-- public/src/js/gpicker.js | 35 +++++++++++++++++++++++------------ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/public/src/js/customsongs.js b/public/src/js/customsongs.js index 50ddd5e..7a5a155 100644 --- a/public/src/js/customsongs.js +++ b/public/src/js/customsongs.js @@ -313,8 +313,10 @@ class CustomSongs{ return Promise.reject(e) } }).finally(() => { - var addRemove = !gpicker || !gpicker.oauthToken ? "add" : "remove" - this.linkGdriveAccount.classList[addRemove]("hiddenbtn") + if(this.linkGdriveAccount){ + var addRemove = !gpicker || !gpicker.oauthToken ? "add" : "remove" + this.linkGdriveAccount.classList[addRemove]("hiddenbtn") + } }) } gdriveAccount(event){ diff --git a/public/src/js/gpicker.js b/public/src/js/gpicker.js index f5fb1ab..c7c0590 100644 --- a/public/src/js/gpicker.js +++ b/public/src/js/gpicker.js @@ -9,6 +9,7 @@ class Gpicker{ this.scope = "https://www.googleapis.com/auth/drive.readonly" this.folder = "application/vnd.google-apps.folder" this.filesUrl = "https://www.googleapis.com/drive/v3/files/" + this.discoveryDocs = ["https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"] this.resolveQueue = [] this.queueActive = false } @@ -138,7 +139,9 @@ class Gpicker{ if(!this.auth){ return new Promise((resolve, reject) => { gapi.auth2.init({ + apiKey: this.apiKey, clientId: this.oauthClientId, + discoveryDocs: this.discoveryDocs, fetch_basic_profile: false, scope: this.scope }).then(() => { @@ -164,22 +167,30 @@ class Gpicker{ return Promise.resolve() } return this.getAuth(errorCallback).then(auth => { - var user = force || auth.currentUser.get() - if(force || !this.checkScope(user)){ + if(!force && auth.isSignedIn.get() && this.checkScope()){ + return Promise.resolve() + }else{ lockedCallback(false) - return auth.signIn(force ? { - prompt: "select_account" - } : undefined).then(user => { - if(this.checkScope(user)){ - lockedCallback(true) - }else{ - return Promise.reject("cancel") - } - }, () => Promise.reject("cancel")) + return new Promise((resolve, reject) => + auth.signIn({ + prompt: force ? "select_account" : "consent", + scope: this.scope + }).then(resolve, reject) + ) } + }).then(() => { + if(this.checkScope()){ + lockedCallback(true) + }else{ + return Promise.reject("cancel") + } + }, e => { + console.error(e) + Promise.reject("cancel") }) } - checkScope(user){ + checkScope(){ + var user = this.auth.currentUser.get() if(user.hasGrantedScopes(this.scope)){ this.oauthToken = user.getAuthResponse(true).access_token return this.oauthToken From 9c31d5b8a0843e355dfa1f1489462f0648bb9c27 Mon Sep 17 00:00:00 2001 From: KatieFrogs <23621460+KatieFrogs@users.noreply.github.com> Date: Fri, 11 Mar 2022 17:34:00 +0300 Subject: [PATCH 2/3] Use Google 3P authorization --- public/src/js/customsongs.js | 3 ++ public/src/js/gpicker.js | 90 ++++++++++++++++-------------------- 2 files changed, 42 insertions(+), 51 deletions(-) diff --git a/public/src/js/customsongs.js b/public/src/js/customsongs.js index 7a5a155..e9f2d86 100644 --- a/public/src/js/customsongs.js +++ b/public/src/js/customsongs.js @@ -516,6 +516,9 @@ class CustomSongs{ pageEvents.remove(document, ["dragover", "dragleave", "drop"]) delete this.dropzone } + if(gpicker){ + gpicker.tokenResolve = null + } delete this.browse delete this.linkLocalFolder delete this.linkGdriveFolder diff --git a/public/src/js/gpicker.js b/public/src/js/gpicker.js index c7c0590..5f6eaa0 100644 --- a/public/src/js/gpicker.js +++ b/public/src/js/gpicker.js @@ -9,9 +9,9 @@ class Gpicker{ this.scope = "https://www.googleapis.com/auth/drive.readonly" this.folder = "application/vnd.google-apps.folder" this.filesUrl = "https://www.googleapis.com/drive/v3/files/" - this.discoveryDocs = ["https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"] this.resolveQueue = [] this.queueActive = false + this.clientCallbackBind = this.clientCallback.bind(this) } browse(lockedCallback, errorCallback){ return this.loadApi() @@ -124,9 +124,12 @@ class Gpicker{ if(window.gapi && gapi.client && gapi.client.drive){ return Promise.resolve() } - return loader.loadScript("https://apis.google.com/js/api.js") - .then(() => new Promise((resolve, reject) => - gapi.load("auth2:picker:client", { + var promises = [ + loader.loadScript("https://apis.google.com/js/api.js"), + loader.loadScript("https://accounts.google.com/gsi/client") + ] + return Promise.all(promises).then(() => new Promise((resolve, reject) => + gapi.load("picker:client", { callback: resolve, onerror: reject }) @@ -135,68 +138,53 @@ class Gpicker{ gapi.client.load("drive", "v3").then(resolve, reject) )) } - getAuth(errorCallback=()=>{}){ - if(!this.auth){ - return new Promise((resolve, reject) => { - gapi.auth2.init({ - apiKey: this.apiKey, - clientId: this.oauthClientId, - discoveryDocs: this.discoveryDocs, - fetch_basic_profile: false, - scope: this.scope - }).then(() => { - this.auth = gapi.auth2.getAuthInstance() - resolve(this.auth) - }, e => { - if(e.details){ - var errorStr = strings.gpicker.authError.replace("%s", e.details) - if(/cookie/i.test(e.details)){ - errorStr += "\n\n" + strings.gpicker.cookieError - } - errorCallback(errorStr) - } - reject(e) - }) - }) + getClient(errorCallback=()=>{}, force){ + var obj = { + client_id: this.oauthClientId, + scope: this.scope, + callback: this.clientCallbackBind + } + if(force){ + if(!this.clientForce){ + obj.select_account = true + this.clientForce = google.accounts.oauth2.initTokenClient(obj) + } + return this.clientForce }else{ - return Promise.resolve(this.auth) + if(!this.client){ + this.client = google.accounts.oauth2.initTokenClient(obj) + } + return this.client + } + } + clientCallback(tokenResponse){ + this.tokenResponse = tokenResponse + this.oauthToken = tokenResponse.access_token + if(this.oauthToken && this.tokenResolve){ + this.tokenResolve() } } getToken(lockedCallback=()=>{}, errorCallback=()=>{}, force){ if(this.oauthToken && !force){ return Promise.resolve() } - return this.getAuth(errorCallback).then(auth => { - if(!force && auth.isSignedIn.get() && this.checkScope()){ - return Promise.resolve() - }else{ - lockedCallback(false) - return new Promise((resolve, reject) => - auth.signIn({ - prompt: force ? "select_account" : "consent", - scope: this.scope - }).then(resolve, reject) - ) - } - }).then(() => { + var client = this.getClient(errorCallback, force) + var promise = new Promise(resolve => { + this.tokenResolve = resolve + }) + lockedCallback(false) + client.requestAccessToken() + return promise.then(() => { + this.tokenResolve = null if(this.checkScope()){ lockedCallback(true) }else{ return Promise.reject("cancel") } - }, e => { - console.error(e) - Promise.reject("cancel") }) } checkScope(){ - var user = this.auth.currentUser.get() - if(user.hasGrantedScopes(this.scope)){ - this.oauthToken = user.getAuthResponse(true).access_token - return this.oauthToken - }else{ - return false - } + return google.accounts.oauth2.hasGrantedAnyScope(this.tokenResponse, this.scope) } switchAccounts(lockedCallback, errorCallback){ return this.loadApi().then(() => this.getToken(lockedCallback, errorCallback, true)) From 407f1f35cd0c13fd2bbadac328670a36a0627128 Mon Sep 17 00:00:00 2001 From: KatieFrogs <23621460+KatieFrogs@users.noreply.github.com> Date: Fri, 11 Mar 2022 17:45:05 +0300 Subject: [PATCH 3/3] Update privacy --- templates/privacy.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/templates/privacy.txt b/templates/privacy.txt index 0496f2f..9816989 100644 --- a/templates/privacy.txt +++ b/templates/privacy.txt @@ -23,10 +23,7 @@ You can use the Google Drive integration to let Taiko Web make your Taiko chart Applications that integrate with a Google account must declare their intent by requesting permissions. These permissions to your account must be granted in order for Taiko Web to integrate with Google accounts. Below is a list of these permissions and why they are required. At no time will Taiko Web request or have access to your Google account password. -3.1 "Associate you with your personal info on Google" Permission -Required for Google Sign-In to provide Taiko Web with a non-identifiable user authentication token. No other information provided by this permission is used. - -3.2 "See and download all your Google Drive files" Permission +3.1 "See and download all your Google Drive files" Permission When selecting a folder with the Google Drive file picker, Taiko Web instructs your Browser to recursively download all the files of that folder directly into your computer's memory. Limitation of Google Drive's permission model requires us to request access to all your Google Drive files, however, Taiko Web will only access the selected folder and its children, and only when requested. File parsing is handled locally; none of your Google Drive files is ever sent to our servers or third parties. {% endif %}{% if config.email %} {% if integration %}4{% else %}3{% endif %}. Contact Info