Ultimate Guide to Auth0 In a Chrome Extension Popup

I’ve been reading around how to implement Auth0 in a Chrome Extension but have not encountered any clear step by step guide on what to do. Is there anyone here that has successfully used Auth0 in a chrome popup with react that would be willing to guide us here? Thanks!

So after reading through the posts here, I was finally able to make things work and want to pay it forward by laying out what I did.

Special thanks to @jannik as I use a lot of his code and ideas here. Also thanks to @dhruv2204 for guiding me in implementing Auth0 inside a background page.

I’m building a Popup Chrome Extension using React and use Auth0 for authentication/authorization.

Whilst I’m using react, I imagine the logic should work if you’re using something else. I’m not using any chrome-extension boilerplate and just use good old npx create-react-app.

It is also worth noting that I already have an existing webapp.

I used the auth0-react library. Initially, I was encountering problems wherein loginWithPopup would close my popup(extension) immediately after authorization. When I would reopen I would reopen the popup I’m already logged out so I really couldn’t login. The only thing that prevented this behavior was if the chrome devtools was open. (setting cacheLocation={'localStorage'} did not fix this which was @jannik’s fix)

What I was finally able to do was:

Have my Sign In button just open up my webapp by calling
chrome.tabs.create({url: 'https://mywebapp.com/'})
Users can then just login there then reopen my popup extension afterwards.

Inside App.js I have this running to check authentication everytime someone opens the popup:

const getToken = async () => {
    try {
      const token = await getAccessTokenSilently()
     setToken(token)
    } catch (e) {
      console.log(e)
    }
  }

  useEffect(() => {
    getToken()
  }, [])

My logic for switching the ui for authenticated user is like this

const { isAuthenticated, getAccessTokenSilently, isLoading } = useAuth0()

const [authenticated, setAuthenticated] = useState(isAuthenticated)

useEffect(()=> {
    setAuthenticated(isAthenticated)
}, [isAuthenticated] )

return(
    <div>
        { isLoading ? <LoadingPage /> : !authenticated ? <SignInPage /> : <HomePage /> }
    </div>
)

The reason why we need to cast isAuthenticated from Auth0 to a custom authenticated state is because we need to manually setAuthenticated(false) on logout. @jannik explains the situation better here Sync auth state between multiple applications (SPA & Chrome Extension) - #21 by jannik

Finally, to Logout, this is what I do. This is basically straight from @jannik

const logoutHandler = () => {
    logoutHelper({ federated: true, returnTo: window.location.origin })
    fetch(`https://${auth0Domain}/v2/logout?federated=true&client_id=${auth0ClientId}`, {
      credentials: 'include',
      mode: 'no-cors'
    }).catch()
  }

  const logoutHelper = (options) => {
    logout(options)
    setAuthenticated(false)
  }

//some button in ui calls logoutHandler() onClick

Hope this helps. If you have suggestions please list it out here and just let me know. Thanks guys!

1 Like

hey @lvillacin

thank you for sharing your effort! I came across exactly the same issues as you. In the last week, I started to use a more maintainable way. I’m using now the “Options” page of a chrome extension to manage the whole login and logout process:
Screenshot 2021-04-20 at 11.55.10

In this way, I can use the normal login with popup and logout without any issues. Here is a little code fragment:

                                <Button
                                    id="loginButton"
                                    variant="contained"
                                    color="primary"
                                    onClick={loginWithPopup}
                                >
                                    {isAuthenticated ? 'Re-Authenticate' : 'Login'}
                                </Button>

The options page and the popup are sharing the same localstorage, that’s why that way works seamlessly.

My popup page is just showing a “settings” button, that is opening the options page in a new tab:

Here is the code for the settings button:

                                        <Button
                                            id="loginButton"
                                            variant="contained"
                                            color="primary"
                                            onClick={() => chrome.tabs.create({
                                                url: `${window.location.origin}/options.html`,
                                            })}
                                        >
                                            Start here
                                        </Button>

I like this solution the most, as you can just work with the official auth0 react library and things just work. The user-flow is a little different, but I think that is worth it.

hope that helps as well!! Getting auth0 work in a react chrome extension is a hell of a work!

Thanks @jannik I was actually thinking about doing this but didn’t push through since I’m just making the scaffolding and have not started working on the final UX.

I might try out having the options page too if our design requires it.

Are you using a boilerplate btw? If you are, can you share what boilerplate you’re using? Thanks!

I can highly recommend using the options page to manage auth0 login/logout.

And my boilerplate is this one: GitHub - lxieyang/chrome-extension-boilerplate-react: A Chrome Extensions boilerplate using React 17 and Webpack 5.
Working pretty good!

Thank you very much! Will check this out!

Teamwork makes the dreamwork!

If you need to run Auth0 in Content Script or Background

Special thanks to @dhruv2204 for guiding me with this.

Use Manifest V2. Also Use @auth0/auth0-spa-js inside the background page and make the calls there.

The reason for this is because auth0 needs access to the DOM and background service workers (from manifest v3) do not have access to the DOM. Please not that manifest v2 will be deprecated soon. They just have not released an official deprecation date so for now this can work

Just use the chrome messaging API to call the the methods from content script if necessary. Doing this still works with the earlier mentioned methods.

Background.html

<script src="./background.js"></script>

Background.js:

import { Auth0Client } from '@auth0/auth0-spa-js'

const auth0 = new Auth0Client({
  domain: <YOUR_AUTH0_DOMAIN>,
  client_id: <YOUR_AUTH0_CLIENT_ID>,
  redirect_uri: <YOUR_CHROME_EXTENSION_URI> //chrome-extension:extensionid
})

const getToken = async () => {
  const token = await auth0.getTokenSilently()
  return token
}
1 Like

Thanks for sharing that with the rest of community!