Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GMail Add-On not reloading after AuthorizationAction completes

I have a GMail AddOn that uses OAuth2 for authorization of an external service. When starting the AddOn, I check if the user already has an access token by calling oauthService.hasAccess(). If the user doesn't have a token, then I'll show a card that says "you need to authorize the service".

The card contain a link built like this:

section.addWidget(CardService.newTextButton()
      .setText("Authorize MyService")
      .setAuthorizationAction(
        CardService.newAuthorizationAction()
          .setAuthorizationUrl(authorizationUrl)
      ));

When the user clicks on the "Authorize MyService" button, a popup window appears and a spinner appears in the old card. Then the user logs into MyService, Authorizes access, and my AuthorizeCallback gets called with the token. I store the token, and return a HtmlPage that just closes itself.

The spinner in the original card stops spinning. However, the AddOn does not reload. This suggests that there is something preventing the AuthorizationAction card from reloading when the authorization is completed. Any suggestions what that might be?

I want the AddOn to reload because then the entrypoint function will discover that now oauthService.hasAccess() returns true, and then will show the normal "you are authorized" card (and start processing the email to MyService).

Thanks for your help!

like image 606
Conrad Herrmann Avatar asked Dec 01 '17 01:12

Conrad Herrmann


2 Answers

There might be a better way to do this but this is how I solved it.

First, instead of using setAuthorizationAction use setOnClickAction and set a function name.

section.addWidget(CardService.newTextButton()
.setText("Authorize MyService")
.setOnClickAction(CardService.newAction().setFunctionName('loginAction'));

loginFunction will return an ActionResponse. It will have a Navigation and OpenLink associated with it.

function loginAction(){
    var nav = CardService.newNavigation().pushCard(getLoginStepsCard());
    return CardService.newActionResponseBuilder()
        .setStateChanged(true)
        .setNavigation(nav)
        .setOpenLink(
            CardService.newOpenLink().setUrl(getLoginUrl()  
        )
        .setOnClose(CardService.OnClose.RELOAD_ADD_ON)
        .setOpenAs(CardService.OpenAs.OVERLAY)).build();
}

This will open the login popup. getLoginUrl will return OAuth login URL. getLoginStepsCard will return a card containing some info. In my case, I'm showing the steps required to log in, show whatever you want.

Inside authorize callback function, save your token and don't close the popup immediately. Closing the popup worked while I was testing (using HEAD) but it didn't work for versioned deployment. The problem was it takes a few seconds for PropertiesService to save the token. Just wait a few seconds (I use a setTimeout of 5s) and close the pop-up or ask the user to close it. Add-on will reload and PropertiesService will have your token.

I hope this helps. It works for me and I have published my addon ;)

like image 122
AKT Avatar answered Nov 15 '22 08:11

AKT


As mentioned in @AKT's answer, the core problem here is that the authorization callback is saving the token to properties, which don't replicate instantly to all Apps Script backends. So although the auth flow has been completed, when the add-on reloads it is still seeing the stale properties which show that it hasn't.

One way to work around this is to wait longer before closing the popup (and refreshing the add-on sidebar). A better option seems to be to ensure you are setting a cache on your OAuth service:

.setCache(CacheService.getUserCache())

This adds an additional caching layer that replicates faster, which seems to work around this issue.

like image 36
Eric Koleda Avatar answered Nov 15 '22 08:11

Eric Koleda