googleworkspace / apps-script-oauth2

An OAuth2 library for Google Apps Script.
https://developers.google.com/apps-script/
Apache License 2.0
1.56k stars 429 forks source link

Auth Token flow does not complete - redirects to Broken Gdrive page #137

Open vyasbhavin opened 6 years ago

vyasbhavin commented 6 years ago

I am attempting to use this library for a GMail add-on. My code is essentially a copy/past of the Non-Google OAuth configuration example from here: https://developers.google.com/gmail/add-ons/how-tos/non-google-services - which pretty much calls methods from this library to build a card and initiate auth-flow.

From everything I understand, the auth-flow itself is fully handled by the library - at the end of which, I can access my auth token.

However, in my case, the auth-flow stops after triggering the redirect URL:

https://script.google.com/a/macros/workboard.com/{script_id}/usercallback/?success=1&code={code}&state={state}. 

At this point, the auth pop-window has the above URL (with actual values, not placeholders) and a drive message saying:

"Sorry, unable to open the file at this time."

I have tried debugging this on my end - but since the entire auth-flow should happen inside the library, can't get much further. Would appreciate any pointers to on what the issue might be or what I can do to debug this further.

Thanks, Bhavin.

erickoledadevrel commented 6 years ago

Can you verify that you have an authCallback function defined? Can you add some logging (console.log()) to it to see if it's being called?

vyasbhavin commented 6 years ago

authCallback is defined indeed. Here is what it looks like (with just-added logging)

function authCallback(callbackRequest) { Logger.log("in authCallback") var authorized = getOAuthService().handleCallback(callbackRequest); if (authorized) { return HtmlService.createHtmlOutput( 'Success! <script>setTimeout(function() { top.window.close() }, 1);</script>'); } else { return HtmlService.createHtmlOutput('Denied'); } }

Here is the setting of the callback function when creating a new OAuth service: .setCallbackFunction('authCallback')

It is not being called. The code get's as far as creating a new service: [18-08-21 12:56:16:864 PDT] Creating New Card i.e. this part of the code

if (!maybeAuthorized) { // Invoke the authorization flow using the default authorization // prompt card. Logger.log("Creating New Card") CardService.newAuthorizationException() .setAuthorizationUrl(service.getAuthorizationUrl()) .setResourceDisplayName("Authenticate through Workboard") .throwException(); }

I have set up the Redirect URL in the service correctly, which is where it's sending the authorized user with a OAuth code.

Thanks! Bhavin.

jilagan commented 6 years ago

I am having similar issues. I also copied from the example linked above. authCallback is not called (I placed logging to make sure).

vyasbhavin commented 6 years ago

Here is a screenshot in case that helps further: https://photos.app.goo.gl/ZnbgxtnEVZnMbrMB8

erickoledadevrel commented 6 years ago

Sorry for the slow response on this issue. Are you perhaps logged into multiple Google accounts in your browser? Try opening your add-on in an incognito tab with only one Google account signed in and see if it still returns the error.

vyasbhavin commented 6 years ago

Thanks Eric for getting back. Not logged into multiple Google accounts but I just tried it in an incognito window to be sure. Still getting the same error.

Cameron-InterlockIT commented 6 years ago

I am also encountering this issue with the callback url as specified in the documentation and generated by service.getRedirectUri()

As a temporary workaround, I have:

function doGet(e){
    if(e.parameter['code']){
        console.log('authorization code in request, do callback');
        return authCallback(e);
    }

    var hoverService = getHoverService();  //get service for hover.to
    if(!hoverService.hasAccess()){
        //display authorization link
    }else{
        //do test request
    }
}
OAuth2.getRedirectUri = function(scriptId) {
  return ScriptApp.getService().getUrl();
}
erickoledadevrel commented 6 years ago

Thanks for the additional detail, and sorry for the slow reply. If the web app approach works, then the problem must be with the /usercallback endpoint, but I'm still not sure how to repro the issue. My next guess is maybe it has to do with the script file permissions. Are the owner of the script and the user going through the OAuth flow in different G Suite domains (or one is a G Suite domain and one a gmail.com account)? What's the file sharing settings on the script?

erickoledadevrel commented 5 years ago

Has anyone made progress on this issue? Any additional details, especially around permissions, could help me reproduce this.

vyasbhavin commented 5 years ago

Thanks for checking, I haven't tried further - Another eng. built the auth and extension that we were looking to (not sure what lib hey used) so I deprioritized this project.

yathit commented 5 years ago

Having same issue for third party custom domain login user. No issue from authentication with same domain user or consumer gmail account.

erickoledadevrel commented 5 years ago

@yathit - Can you explain what you mean by "third party custom domain login user"? How can I recreate this scenario?

yathit commented 5 years ago

@erickoledadevrel It is working for myself (owner of the project) and consumer gmail. The problem appear only for Google domain account users.

erickoledadevrel commented 5 years ago

Is the owner account also in G Suite domain? The same domain or a different one?

yathit commented 5 years ago

Yes, owner account is G Suite domain.

The problem appear only for other G Suite domain users.

davidsertillange-optimizely commented 5 years ago

Can you share the callback URL?

I've had the exact same issue and my callback URL from my OAuth2 provider had a # in the URL instead of a ? for query parameters causing Google Scripts to be unable to parse the query parameters properly.

dtod commented 5 years ago

I am also encountering this issue with the callback url as specified in the documentation and generated by service.getRedirectUri()

As a temporary workaround, I have:

  •    defined a `doGet()` function
  •    check for the "code" parameter in the get request
  •    manually pass the request to oauthCallback if "code" is present. 
  •    published my script as a web-app
  •    set redirect url on my OAuth Provider to the web-app url
  •    override Oauth2 "generateRedirectUri" to return published script URI
function doGet(e){
    if(e.parameter['code']){
        console.log('authorization code in request, do callback');
        return authCallback(e);
    }

    var hoverService = getHoverService();  //get service for hover.to
    if(!hoverService.hasAccess()){
        //display authorization link
    }else{
        //do test request
    }
}
OAuth2.getRedirectUri = function(scriptId) {
  return ScriptApp.getService().getUrl();
}

This is an interesting flow design. Does it solve the problem I have described here: https://stackoverflow.com/questions/54933478/oauth2-library-redirect-uri-options where the redirect_uri changes for every copy of the spreadsheet I make?

fnlctrl commented 5 years ago

I've ran into this error too, and it was caused by using OAuth2.createService(...).setScope('')(empty string) or not calling setScope. The reason I did this is that our API had a behavior to use all scopes requested on marketplace setup, if no scope is passed on authorize url query params.

I'm wondering why this error would lead to a Google Drives page showing "Sorry, unable to open the file at this time.", instead adding an entry of error on Stackdriver logs? The error somehow does not get logged. This made it a lot harder to locate the cause.

erickoledadevrel commented 5 years ago

I haven't been able to reproduce the error shown in the screenshot from @vyasbhavin, but I have gotten a similar case to fail, where the user accessing the web app has the Drive service disabled by their administrator. More broadly, it seems the /usercallback URL has some sort of dependency on Drive that web apps themselves do not. The workaround suggested by @Cameron-InterlockIT works because it bypasses the /usercallback. I've opened an issue with the Apps Script engineering team to investigate this issue.

@fnlctrl - Sounds like that was a different cause. Can you share some code that reproduces that error?

arvs47 commented 5 years ago

I have similar issue with one of my simple Facebook login code . https://stackoverflow.com/questions/55058009/google-app-script-unable-to-open-the-file-at-this-time-error-while-collecting-f @erickoledadevrel you can check it with this simple facebook login button code add as a button in html

<a class="loginBtn loginBtn--facebook btn btn-info" href=' https://www.facebook.com/dialog/oauth?client_id=**REPLACEYOURFBAPPID**&redirect_uri=https%3A%2F%2Fscript.google.com%2Fmacros%2Fd%2F**SCRIPTID**%2Fusercallback&exec?&response_type=token&scope=pages_show_list,publish_pages' target='_blank'>Authorize Facebook</a>

I solved this issue by replacing this as button code

<a class="loginBtn loginBtn--facebook btn btn-info" href='<?=getService().getAuthorizationUrl()?>' target='_blank'>Authorize Facebook</a>

derrickrc commented 5 years ago

Is there any update on this? Updating the main github page with the web app workaround provided by @Cameron-InterlockIT would definitely be helpful, I ran into the same issue and his workaround works.

My other question (perhaps the same one asked by @dtod) is can a single GAS file be used for multiple auth tokens for a single API? I am working with multiple clients for a single API and it seems that with the current structure I'd have to create new GAS files and web app links for each client (not a big deal), BUT the bigger pain is changing the redirect_uri with the third-party provider each time before a client does their oauth2 so as not to overwrite another token in another file.

ghost commented 5 years ago

I am also encountering this issue with the callback url as specified in the documentation and generated by service.getRedirectUri()

As a temporary workaround, I have:

  •    defined a `doGet()` function
  •    check for the "code" parameter in the get request
  •    manually pass the request to oauthCallback if "code" is present. 
  •    published my script as a web-app
  •    set redirect url on my OAuth Provider to the web-app url
  •    override Oauth2 "generateRedirectUri" to return published script URI
function doGet(e){
    if(e.parameter['code']){
        console.log('authorization code in request, do callback');
        return authCallback(e);
    }

    var hoverService = getHoverService();  //get service for hover.to
    if(!hoverService.hasAccess()){
        //display authorization link
    }else{
        //do test request
    }
}
OAuth2.getRedirectUri = function(scriptId) {
  return ScriptApp.getService().getUrl();
}

This approach really worked well for me.

sambragg commented 5 years ago

Is there any update on this issue @erickoledadevrel ?

I am also having an issue where my callback function isn't being called, exactly the same as what @vyasbhavin described. I am only singed into 1 Google account and have also tried in incognito mode.

zakmatik commented 5 years ago

I'm running into the same issue, where the redirect_uri redirects to my gsuite url and I get the same 'Sorry, unable to open the file at this time.' message. In addition to the steps taken above, I've tried to play around with the oauth scopes in the manifest file to see if it was somehow related to not having the scripts scope, but that didn't seem to help.

sambragg commented 4 years ago

I've been playing about with the Google Oauth sample included with this library - and it works just fine!

That would lead me to believe it's either my code or the external API I'm trying to consume (Tradestation).

Not sure where to go with this now...

erickoledadevrel commented 4 years ago

No, there hasn't been any progress on this issue. If other samples are working that is interesting. It doesn't look like Tradestation allows anyone to register for an API key, so I wouldn't be able to reproduce the problem with that API.

sambragg commented 4 years ago

Yes that's correct, TS don't allow public access.

If I could get some credentials for you, would it be something you have time to investigate further?

No worries if not, I realise it's not a very common API to be working with!

Thanks for your response,

Sam

On Wed, 18 Dec 2019, 21:13 Eric Koleda, notifications@github.com wrote:

No, there hasn't been any progress on this issue. If other samples are working that is interesting. It doesn't look like Tradestation allows anyone to register for an API key, so I wouldn't be able to reproduce the problem with that API.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/gsuitedevs/apps-script-oauth2/issues/137?email_source=notifications&email_token=AFZSTEE37TBNQ43I6IQHIU3QZIV2JA5CNFSM4FPUYTQKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEHGHUAQ#issuecomment-567048706, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFZSTEFTQ3PFHZYFW3UMZ5DQZIV2JANCNFSM4FPUYTQA .

erickoledadevrel commented 4 years ago

No, I'd be concerned about using the API credentials granted to another company or individual, and how that squares with their terms of service. But if you can turn up any more clues let me know.

sambragg commented 4 years ago

Can't argue with that, will keep this issue updated

On Wed, 18 Dec 2019, 21:49 Eric Koleda, notifications@github.com wrote:

No, I'd be concerned about using the API credentials granted to another company or individual, and how that squares with their terms of service. But if you can turn up any more clues let me know.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/gsuitedevs/apps-script-oauth2/issues/137?email_source=notifications&email_token=AFZSTEFS6XUSUAKMTBID5XDQZI2A5A5CNFSM4FPUYTQKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEHGLHTA#issuecomment-567063500, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFZSTEGLG4Z4J5WQ3MPAG23QZI2A5ANCNFSM4FPUYTQA .

alexander-soare commented 4 years ago

I had this same issue and and eventually figured out that I was calling .setParam('response_type', 'token') instead of .setParam('response_type', 'code') which was required by the specific api I was using. Just a warning to those who might spend a lot of time trying to debug the source code :P when that's not the real issue :P

kaushaldeck commented 4 years ago

Hi I am also facing this same issue. My redirect URI is resulting in an error page about google drive.

bevbomb commented 4 years ago

Has anyone got this working properly as a Gmail addon? I'm seeing this issue and I have set everything up following the installation and using my own API to authenticate. When the redirect occurs it takes me to the Google drive error page. I've tried using the Webapp method but I couldn't find a way to update the Gmail sidebar after the web app was called

derrickrc commented 4 years ago

I am not sure if this is specific to this library, but I've been employing the doGet(e) workaround and calling authCallback when there is a ['code'] parameter as outlined above and it's been working fine on Chrome - however, on Safari (on both iOS and OSX) I always get the broken GDrive page. I've even tried to simplify my code down to the below and just clicking on the script link and I get the broken GDrive page. Last night I thought I was able to get it to work, and this morning it's returning the broken GDrive page consistently. Is this a known issue with Safari and doGet(e) and createHtmlOutput?

function doGet(e){
          return HtmlService.createHtmlOutput("<b>Hello world!</b>") 
}

EDIT Looks like someone else is facing the same issue today and we've isolated it to a browser window without a logged-in Google user (even though it's set to anyone, even anonymous): https://stackoverflow.com/questions/62770318/google-script-app-fails-in-incognito-mode

EDIT #2: https://issuetracker.google.com/issues/160665120

Cameron-InterlockIT commented 4 years ago

I have recently looped back to this issue as I'm using the library on another project and bumped into the same issue. On further testing in this case I have isolated the issue to a situation where the "state" query string parameter is missing from the request to /usercallback.

https://script.google.com/a/macros/interlockit.com/d/xxxxx/usercallback?state=a receives a valid response: "The state token is invalid or has expired. Please try again."

https://script.google.com/a/macros/interlockit.com/d/xxxxx/usercallback?code=xxx&expires_in=1111 receives the error: "Page Not Found" / "Sorry, unable to open the file at this time. Please check the address and try again...."

It appears the original reporter was passing in the state, so I'm not sure how relevant this is overall, I can't recall the specifics of my last case where I implemented the doGet() workaround.

SardarTejpalSingh commented 4 years ago

Apps script (DataStudio Community connector) failing with a Google Drive error.

We had the same issue, our use case is a custom Data Studio Community connector using a standalone AppScript that is meant to fetch data from Cloud Spanner. We are using OAuth2 provider(Tyk) and the OAuth2 Appscript library as specified.

Redirect URI for AppScript is setup as https://script.google.com/macros/d/{SCRIPT ID}/usercallback as per the this library documentation. During script execution the callback step is failing with a Google Drive error message.

Could you please guide us as to the best way to resolve this issue ? Also, any documentation or insight into which repository/library we should use for AppScript will be appreciated. issue

m-pd commented 3 years ago

Hi. Any update on this? Still seeing the issue.

derrickrc commented 3 years ago

The broken Gdrive page seems to be ongoing and intermittently also affects Apps Scripts deployed web apps more broadly - see: here and here

I ended up eventually migrating away from this library to instead use Google Cloud Functions (Node.js runtime) and simple-oauth2 with Firebase as my database and have been quite happy since.