Need help selecting correct auth flow for chrome extension

My current chrome extension works by injecting a “content script” onto webpages a user might go to. It will enable certain functionality on those web pages, but I would like the user to be authenticated first.

These content scripts effectively run in the same scope as the actual website. Therefore it shares their origins. However, I do also have a service worker running in the background to coordinate the scripts across many websites.

Intended experience
I want the user to login once, and then use that auth token across many different websites AND refresh the token as regularly as is necessary to ensure they aren’t constantly logging back in.

I currently have a dedicated, extension hosted page, that handles authorising through a redirect (although I wish I could get popup working… another thread maybe). When that page finishes authenticating, I want to be able to store the access token and refresh token somewhere so that my extension can access it from any web page. Right now I am leaning towards using chrome’s chrome.storage API to share memory between the tabs. My hope is to then use the service worker to regularly refresh that access token, so whenever my extension loads on a webpage, it can access the storage and get a relevant access token.

What I’ve tried

  1. Implicit Flow
    I requested a token, tried to include the offline_access scope and quickly realised I am not able to get a refresh token this way.

  2. Code Flow with PKCE
    This felt promising, but seems to rely on the session to refresh the token, which I won’t have from the different web pages, and am not sure how to use in a service worker.

I would like to get some advise on the following questions:

  1. Is what I’m trying possible?
  2. If so, what is the recommended flow to be able to authenticate the user as securely as possible?
1 Like

Hey there @rb03 !

Thanks for sharing all that context! @john.gateley would you be able to help here? Thank you!

Hi @konrad.sopala @rb03

This is a complicated scenario (one that would probably benefit from Professional Services Auth0 Professional Services)

Do not play fast and loose with access tokens: Token Storage

Instead, you want to use Auth Code + PKCE and rely on the auth0 session. You are already redirecting to your extension hosting page for auth. Your apps should use silent auth via redirecting to get their own access tokens as needed.

The Auth0 session is a cookie in your Auth0 tenant domain, so it IS available to all your different web pages.

I haven’t secured an extension before, but it is a public, not confidential, client so it requires Auth Code + PKCE.

John

1 Like

Appreciate the help @konrad.sopala and @john.gateley!

Do you mind expanding on

Do not play fast and loose with access tokens

From your link:

Using Web Workers to handle the transmission and storage of tokens is the best way to protect the tokens, as Web Workers run in a separate global scope than the rest of the application.

I imagined this may expand to a chrome extension’s service worker / storage API, but now I think maybe not?

I’m glad to hear session cookies are stored on the tenant domain and not on the callback domain, that does make things far easier. When I originally tried to get an access token from the webpages themselves, it didn’t work. After some digging I am seeing a reason why, the request is timing out:

Error: Timeout
    at new OAuthError (index.js:33538)
    at index.js:33556
    at index.js:33718
    at step (index.js:33484)
    at Object.throw (index.js:33465)
    at rejected (index.js:33456)

This is when I call getAccessTokenSilently using the react SDK, but I imagine it would be the same for the SPA SDK too. Oddly, the logs are showing that there was a successful silent auth, it just doesn’t seem to return on the page itself.

My google searches yielded nothing for timeouts and this specific method. I initially concluded this isn’t the correct way to go about things, but possibly something else is going awry?

Hi @rb03

For “fast and loose” I mostly mean don’t share access tokens between various instances of your extension. Always be very careful with access tokens, and consider the security implications.

For your timeout: you probably need to allow access. I am just guessing but:

  • Your app is local host
  • Local host requires permission to allow access
  • You haven’t granted that yet.
    Try the popup version of getAccessTokenSIlently to see if you get the same behavior.

John

1 Like

Hey @john.gateley!

Thanks for clarifying, some of these concepts are a bit new to me so it really helps.

So with a chrome extension I’m not hosting anything locally to test it, I run the script on live websites and the origin matches that webpage.

However, a few observations based on your suggestions:

getAccessTokenWithPopup

Using the react SDK, getAccessTokenWithPopup actually didn’t do anything. No modal popped up and no request in the browser, however, I did see the same error in the console 1 minute later. I am guessing it’s triggered by the web worker and I don’t have visibility, but, the same problem happened.

getAccessTokenSilently

Reading the type definition of getAccessTokenSilently I realised I may be using it incorrectly, however I would assume the error would be more specific if this was the case. More specifically it states:

There’s no actual redirect when getting a token silently…Auth0 uses this parameter to validate that the current origin matches the redirect_uri origin when sending the response.

My challenge here is I want to accept callback urls from any website. From the documentation I’ve read, it seems like I can’t use wildcards to effectively specify any website since it’s a security risk, so that’s not an option.

When I fix the redirectURI to something that is in my application settings, for every webpage that tries to get an access token, I do oddly see a successful silent auth in the logs, it’s just the client that has a timeout, so I’m not entirely sure if this is an SDK constraint, or an API constraint.


One alternative I thought about was routing all of my extension requests through the extension service-worker, however it seems like the js-spa SDK has a dependency on the DOM which means it can’t be loaded in a service worker.

I imagine I could also try manually calling the endpoints to get a token and cache them myself, but I feel like this opens up security implications.

Are you able to advise on whether this IS in fact an issue with callback URLs or whether something else might be amiss?

Hi @rb03

This is complex, and hard to debug via a community forum. You might consider professional services (link above)

The callback URL could be an issue, but you can find out for sure. Check the tenant logs and see what error is there. If it is a callback URL mismatch, it will say so there. If not, post the tenant log error here and I’ll try to help

John

1 Like

Hey @john.gateley,

I do appreciate that. This isn’t for a professional endeavour so I think professional services may slightly price me out. I do appreciate your help though and also the fact you aren’t any under obligation to respond. I’d still prefer to put the problem out there incase anyone else in the community is able to provide input.

In this instance, there are no errors in the tenant logs. Right now I am only seeing a timeout from the javascript SDK, the tenant logs show a successful silent auth attempt. Which is why I wasn’t sure if this was an SDK problem or an API problem.

For me an API problem would imply I need to take a different approach, an SDK bug is possibly something I can contribute to directly.

Hey @john.gateley and @konrad.sopala ,

I did end up reaching out to the professional services sales team and was redirected to read the docs and “raise a ticket” via support.

I feel like that might put me back in a circle as the last time I raised a support ticket it was mostly troubleshooting steps rather than help with architecture.

Are you able to advise on an appropriate next step here? Or is auth0 unlikely to be able to help here?

Can you capture the flow in a HAR file and post it here? (Sanitize it if needed)

John

The best way would be to send it to @john.gateley in a private message here in the forum. You can do that by clicking on his avatar and there should be a blue message button

Hey all, just updating this post with my findings (special thanks to @john.gateley for his input here).

To recap, one of the major challenges was having multiple domains make requests to a single API. Each one having its own access token was not possible because I wasn’t able to know in advance what domain it was and therefore could not add it as an allowed origin. Therefore I was left with maintaining a single access token for a known domain. In this case, that known domain was my chrome extension.

Funnelling all the requests through the chrome extension’s service worker was fine, but a new challenge arose with refreshing the token. Since it’s a service worker, the act of launching an authorization flow can be a little jarring as a user experience, therefore I was looking for programmatic ways to get a new access token. Unfortunately, the available method (a refresh token) was worrisome since chrome’s native storage API is not necessarily a safe way to store these tokens.

Therefore I approached the authorization flow in another way. I placed an auth proxy infront of my API that maintains a session for the user on the same domain as the API. Thankfully, the work needed to this had already been done for me: auth0/express-openid-connect. Now my chrome extension doesn’t necessarily need to store anything since all requests include a session cookie, and the auth proxy can safely deal with refresh the session if necessary.

My final architecture looks like so:

Seems like @david20 solution is easier (no need to add an Auth Proxy)

Link to the thread → Auth0 in Chrome Extension Content/Background script MV3 - #4 by david20