eight04 / webext-launch-web-auth-flow

Polyfill `launchWebAuthFlow` with popups
MIT License
5 stars 3 forks source link

Pop-up opens and then quickly closes itself every time the library is called #4

Open lucasrmendonca opened 2 years ago

lucasrmendonca commented 2 years ago

I'm having a strange bug where the popup dialog opens and then closes every time this is called. By setting alwaysUseTab: true, a new tab is created and then quickly closed every time, which is very distracting

eight04 commented 2 years ago

Did you get the final URL correctly?

lucasrmendonca commented 2 years ago

Yep, the final URL is being retrieved correctly and the authentication part works fine. However, after the user logs in a first time, every time my getToken() function is called a popup flashes on the screen, self-closing after a split second. (Or a new tab is created and quickly self-closes is using alwaysUseTab) Token retrieval is working fine as well, apart from the UI issue.

const AUTH_PARAMS = {
  response_type: 'token',
  hd: 'gmail.com',
  login_hint: 'username@gmail.com',
  redirect_uri: browser.identity.getRedirectURL(),
  client_id: browser.runtime.getManifest().oauth2.client_id,
  scope: browser.runtime.getManifest().oauth2.scopes.join(' '),
};
const AUTH_URL = `https://accounts.google.com/o/oauth2/auth?${qs.stringify(AUTH_PARAMS)}`;

async function getToken() {
  // previous code, works without UI glitches but doesn't keep user session after browser is closed
  // const finalUrl = await browser.identity.launchWebAuthFlow({ interactive: true, url: AUTH_URL });

  // has prompt UI glitch
  const finalUrl = await launchWebAuthFlow({
    url: AUTH_URL,
    redirect_uri: browser.identity.getRedirectURL(),
    interactive: true,
  });

  const token = regex.extractTokenFromUrl(finalUrl);

  return token;
}
eight04 commented 2 years ago

The UI problem is a known issue: https://github.com/eight04/webext-launch-web-auth-flow#known-issues. Ideally, we should create a hidden window to login.

To avoid calling the login process every time you need the token, you have to:

  1. Cache the token after login.
  2. Only call the login process when the cache is invalid.
  3. Invalidate the cache when getting an auth error e.g. HTTP 401.

Here is an example: the token manager of Stylus extension

eight04 commented 2 years ago

Or maybe we should try using a hidden tab on Firefox. https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/hide

lucasrmendonca commented 2 years ago

By using the cache solution we still call the login process every hour, since access tokens expire after sixty minutes. It's less annoying, but still not ideal.

The delay could come from WebRequestBlocking, since normal WebAuthFlow doesn't have this UI issue.

I see they replaced it with a declarative API in Manifest 3.0, maybe by adding support to Manifest 3.0 we also solve this issue...

eight04 commented 2 years ago

In our cases (Stylus extension), we request offline access to get a long live (6 months?) refresh token: https://github.com/openstyles/stylus/blob/b5fd8b63dc09510909aa28cdaa77b2834576257c/background/token-manager.js#L30

I didn't read manifest v3. If they start using browser session to login, feel free to link the doc. I can put a notice in the README.

The delay could come from WebRequestBlocking, since normal WebAuthFlow doesn't have this UI issue.

It's not. The "normal" browser.indentify.launchWebAuthFlow doesn't have this issue because it uses a hidden, isolated window. Therefore you won't see a window/tab created and closed immediately.

lucasrmendonca commented 2 years ago

I see, but isn't the refresh token solution a security vulnerability since it exposes the client secret in the code ?

Also if the identity API LaunchWebAuthFlow can open stuff in a hidden window, we should (theoretically) be able to as well, right ? 🤔

eight04 commented 2 years ago

I see, but isn't the refresh token solution a security vulnerability since it exposes the client secret in the code ?

Yes. Since it's an decentralized open source extension, anyone can use the app ID/secret and pretend to be Stylus.

Also if the identity API LaunchWebAuthFlow can open stuff in a hidden window, we should (theoretically) be able to as well, right ?

Unfortunately, browsers don't want extensions to create hidden windows. Except Firefox, which can create hidden tabs. But you need an extra permission tabHide: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/hide

lucasrmendonca commented 2 years ago

It seems there's a workaround for opening hidden windows by opening it inside an iframe in the background page. This way, we can open an "invisible" window, thus removing the need to request a long-lived refresh token that exposes the client_secret inside the source code and be able to use the Implicit Flow instead.

I didn't investigate any further, though... do you know anything about this iframe strategy? Does it sound like something that it's worth investigating? If so, I could try it out in the weekend and report my findings

eight04 commented 2 years ago

It seems there's a workaround for opening hidden windows by opening it inside an iframe in the background page.

How do you popup the background iframe when user interaction is required?

eight04 commented 2 years ago

Maybe we can open the window outside of the screen to "hide" it.

lucasrmendonca commented 1 year ago

It seems that this will be fixed on Chromium 111: https://chromium.googlesource.com/chromium/src/+/4f7b538e700a4fe70ba7fc6f631651b674b25191