Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get a new Access Token before its expiration after user login with chrome.identity.launchWebAuthFlow?

Sorry if this have been asked before but I've spend the whole day googling without any results.

I'm using Chrome Identity API for a Chrome Extension I'm building for my work (interacting with Google Sheets API), and I'm looking to persist the login for the user but refresh tokens with this API are nowhere to be found.

I first implemented chrome.identity.getAuthToken() and I was able to authorize the request to gSheets API, the problem came when the Access Token expired. I'm aware that calling getAuthToken() again refreshes the token if you call it with interactive: false, but only if the People you're logged in with has the same email you authorized in the initial interaction with the popup, and this is not what I want simply because in my browser I'm logged with my personal email, and I want to authorize the work email for the extension. So, after 1 hour I need to request a new token which will prompt a login popup and this is a poor user experience.

Then I tried with chrome.identity.launchWebAuthFlow() with the endpoint https://accounts.google.com/o/oauth2/v2/auth knowing that launchWebAuthFlow() is for non-Google accounts, and even though it worked well, as far as I know, there is no way to refresh the token after — let's say — 45 minutes to avoid kicking out the user.

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
  switch (request.type) {
    case "login":
      let authURL = "https://accounts.google.com/o/oauth2/v2/auth";
      const clientId = `<CLIENT_ID>`;
      const redirectURL = chrome.identity.getRedirectURL("oauth2");
      const auth_params = {
        client_id: clientId,
        redirect_uri: redirectURL,
        response_type: "token",
        scope: "https://www.googleapis.com/auth/spreadsheets"
      };

      const paramString = Object.keys(auth_params)
        .map(function(k) {
          return k + "=" + auth_params[k];
        })
        .join("&");

      authURL += "?" + paramString;

      chrome.identity.launchWebAuthFlow(
        { url: authURL, interactive: true },
        function(responseURL) {
          if (chrome.runtime.lastError) {
            console.log(chrome.runtime.lastError);
            return;
          }

          if (responseURL) {
            const paramsString = responseURL.substring(
              responseURL.indexOf("#") + 1
            );
            const params = new URLSearchParams(paramsString);

            const paramsObj = () => {
              const obj = {};
              for (var entry of params.entries()) {
                obj[entry[0]] = entry[1];
              }
              return obj;
            };

            const { access_token } = paramsObj();

            sendResponse({
              access_token
            });
          } else {
            console.log(`${responseURL} defined?`);
          }
        }
      );

      return true;
    default:
      console.log("placeholder");
      break;
      }
});

I'm willing to ditch Chrome Identity API and use another OAuth2 flow if it means I can improve the user experience for the user.

TL;DR: I want to get a new access token either with Chrome Identity API or any other OAuth2 implementation a few minutes before the previous token expires, while being able to use a Google Account different than the logged in Chrome (People).

like image 281
Ivor Avatar asked Jul 14 '18 06:07

Ivor


People also ask

How do I handle access token expiry?

This can be done using the following steps: convert expires_in to an expire time (epoch, RFC-3339/ISO-8601 datetime, etc.) store the expire time. on each resource request, check the current time against the expire time and make a token refresh request before the resource request if the access_token has expired.

How long do Google access tokens last?

The access token is set with a reasonably lower expiration time of 30 mins. The refresh token is set with a very long expiration time of 200 days. If the traffic to this API is 10 requests/second, then it can generate as many as 864,000 tokens in a day.


2 Answers

I could've edited the previous question, but instead I'm going to answer it:

There's a way to get a new token by issuing a code instead of a token using access_type=offline and response_type:'code'.

let authURL = 'https://accounts.google.com/o/oauth2/v2/auth';
const redirectURL = chrome.identity.getRedirectURL("oauth2");
const auth_params = {
  client_id: clientId,
  redirect_uri: redirectURL,
  response_type: 'code',
  access_type: 'offline',
  scope: 'https://www.googleapis.com/auth/spreadsheets',
};
chrome.identity.launchWebAuthFlow({url: authURL, interactive: true}, responseURL => console.log(responseURL))

The response will be a URL:

https://<extension_id>.chromiumapp.org/oauth2?code=4/A<code>#

The code returned can be redeemed for a token that can be used only 1 time. You then send a POST request to https://www.googleapis.com/oauth2/v4/token with the following snippet as the request:

POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=4/A<code>&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https://<extension_id>.chromiumapp.org/oauth2&
grant_type=authorization_code

The response will be a JSON object which you can use for ~1 hour to make requests to Google APIs:

{
    "access_token": "<access_token>",
    "token_type": "Bearer",
    "expires_in": 3578
}

Finally before the access token expires you can call launchWebAuthFlow() with interactive: false and it will return a new code and with that you get a new token.

like image 199
Ivor Avatar answered Oct 18 '22 01:10

Ivor


To get a new token by a code is the wrong way. Instead, a refresh token is designed to get a new token after the old one expired.

  1. Exchange a code for refresh and access tokens.

    https://developers.google.com/identity/protocols/oauth2/web-server?hl=en#exchange-authorization-code
    
  2. Save the refresh token. When the current access token expires, get a new token by the refresh token.

    https://developers.google.com/identity/protocols/oauth2/web-server?hl=en#offline
    

Good luck to coding.

like image 2
Jk L. Avatar answered Oct 18 '22 03:10

Jk L.