Monetize your Chrome extension with OpenID Connect (OAuth 2.0) authentication

Learn how to monetize your Chrome extension by adding user authentication to it.
Gao
GaoFounder
April 02, 20248 min read
Monetize your Chrome extension with OpenID Connect (OAuth 2.0) authentication

Chrome extensions are a great way to extend the functionality of the Chrome browser. When you have a popular extension, you might want to monetize it by offering pro features to users who pay for them. User authentication is must for this purpose:

  • User identification: You need to know who the user is to provide personalized features.
  • Access control: You need to control who can access the paid features.
  • Subscription management: You need to provide a way for users to subscribe and manage their subscriptions.

On the other hand, we don't want to stick on the Google account system, as your users might prefer to use other accounts, or, you may have multiple services that you want to integrate with the same identity system.

A quick summary of the benefits of using OpenID Connect (OIDC) for authentication:

  • No vendor lock-in: Your users can use various methods to sign in, rather than being forced to Google.
  • Single Sign-On (SSO): Users can sign in once and access multiple services or applications.
  • Standardized: OIDC is an open standard that is widely adopted and supported, also it's secure.

In this tutorial, we will use Logto as the OIDC provider, which is an Auth0 alternative for building identity infrastructures.

Let's get started!

You can find a sample project for this tutorial on our GitHub repository.

Introduction

Assuming you put a "Sign in" button in your Chrome extension's popup, the authentication flow will look like this:

Logto sign-in experienceExtension service workerExtension popupLogto sign-in experienceExtension service workerExtension popupInvokes sign-inRedirects to LogtoUser signs inRedirects back to extensionNotifies the popup

For other interactive pages in your extension, you just need to replace the Extension popup participant with the page's name. In this tutorial, we will focus on the popup page.

Create a Logto application

To get started, create a Logto application with the "Single page app" type. Follow these steps to create a Logto application:

  1. Sign in to the Logto Console.
  2. Click on Create application.
  3. In the opened page, find the "Create app without framework" button in the bottom and click on it.
  4. Choose the "Single page app" type, and enter your application name.
  5. Click on Create.

Set up your Chrome extension

Install Logto SDK

Install Logto SDK in your Chrome extension project:

# or pnpm, yarn, etc.
npm i @logto/chrome-extension

Update the manifest.json

Logto SDK requires the following permissions in the manifest.json:

{
  "permissions": ["identity", "storage"],
  "host_permissions": ["https://*.logto.app/*"]
}
  • permissions.identity: Required for the Chrome Identity API, which is used to sign in and sign out.
  • permissions.storage: Required for storing the user's session.
  • host_permissions: Required for the Logto SDK to communicate with the Logto APIs.

If you are using a custom domain on Logto Cloud, you need to update the host_permissions to match your domain.

Set up a background script (service worker)

In your Chrome extension's background script, initialize the Logto SDK:

// service-worker.js
import LogtoClient from '@logto/chrome-extension';

export const client = new LogtoClient({
  endpoint: '<your-logto-endpoint>'
  appId: '<your-logto-app-id>',
});

Replace <your-logto-endpoint> and <your-logto-app-id> with the actual values. You can find these values in the application page you just created in the Logto Console.

If you don't have a background script, you can follow the official guide to create one.

Why do we need a background script?

Normal extension pages like the popup or options page can't run in the background, and they have the possibility to be closed during the authentication process. A background script ensures the authentication process can be properly handled.

Then, we need to listen to the message from other extension pages and handle the authentication process:

// service-worker.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  // In the below code, since we return `true` for each action, we need to call `sendResponse`
  // to notify the sender. You can also handle errors here, or use other ways to notify the sender.

  if (message.action === 'signIn') {
    const redirectUri = chrome.identity.getRedirectURL('/callback');
    client.signIn(redirectUri).finally(sendResponse);
    return true;
  }

  if (message.action === 'signOut') {
    const redirectUri = chrome.identity.getRedirectURL();
    client.signOut(redirectUri).finally(sendResponse);
    return true;
  }

  return false;
});

You may notice there are two redirect URIs used in the code above. They are both created by chrome.identity.getRedirectURL, which is a built-in Chrome API to generate a redirect URL for auth flows. The two URIs will be:

  • https://<extension-id>.chromiumapp.org/callback for sign-in.
  • https://<extension-id>.chromiumapp.org/ for sign-out.

Note that these URIs are not accessible, and they are only used for Chrome to trigger specific actions for the authentication process.

Update Logto application settings

Now we need to update the Logto application settings to allow the redirect URIs we just created.

  1. Go to the application page in the Logto Console.
  2. In the "Redirect URIs" section, add the URI: https://<extension-id>.chromiumapp.org/callback.
  3. In the "Post sign-out redirect URIs" section, add the URI: https://<extension-id>.chromiumapp.org/.
  4. In the "CORS allowed origins" section, add the URI: chrome-extension://<extension-id>. The SDK in Chrome extension will use this origin to communicate with the Logto APIs.
  5. Click on Save changes.

Remember to replace <extension-id> with your actual extension ID. You can find the extension ID in the chrome://extensions page.

After updating the settings, your Logto application settings should look like this:

Application settings

Add sign-in and sign-out buttons to the popup

We're almost there! Let's add the sign-in and sign-out buttons and other necessary logic to the popup page.

In the popup.html file:

<!-- popup.html -->
<button id="sign-in">Sign in</button>
<button id="sign-out">Sign out</button>

In the popup.js file (assuming popup.js is included in the popup.html):

// popup.js
document.getElementById('sign-in').addEventListener('click', async () => {
  await chrome.runtime.sendMessage({ action: 'signIn' });
  // Sign-in completed (or failed), you can update the UI here.
});

document.getElementById('sign-out').addEventListener('click', async () => {
  await chrome.runtime.sendMessage({ action: 'signOut' });
  // Sign-out completed (or failed), you can update the UI here.
});

Checkpoint: Test the authentication flow

Now you can test the authentication flow in your Chrome extension:

  1. Open the extension popup.
  2. Click on the "Sign in" button.
  3. You will be redirected to the Logto sign-in page.
  4. Sign in with your Logto account.
  5. You will be redirected back to the Chrome.

Check authentication state

Since Chrome provide unified storage APIs, rather than the sign-in and sign-out flow, all other Logto SDK methods can be used in the popup page directly.

In your popup.js, you can reuse the LogtoClient instance created in the background script, or create a new one with the same configuration:

// popup.js
import LogtoClient from '@logto/chrome-extension';

const client = new LogtoClient({
  endpoint: '<your-logto-endpoint>'
  appId: '<your-logto-app-id>',
});

// Or reuse the client instance created in the background script
import { client } from './service-worker.js';

Then you can create a function to load the authentication state and user's profile:

// popup.js
const loadAuthenticationState = async () => {
  const isAuthenticated = await client.isAuthenticated();
  // Update the UI based on the authentication state

  if (isAuthenticated) {
    const user = await client.getIdTokenClaims(); // { sub: '...', email: '...', ... }
    // Update the UI with the user's profile
  }
};

You can also combine the loadAuthenticationState function with the sign-in and sign-out logic:

// popup.js
document.getElementById('sign-in').addEventListener('click', async () => {
  await chrome.runtime.sendMessage({ action: 'signIn' });
  await loadAuthenticationState();
});

document.getElementById('sign-out').addEventListener('click', async () => {
  await chrome.runtime.sendMessage({ action: 'signOut' });
  await loadAuthenticationState();
});

Here's an example of the popup page with the authentication state:

Popup page

For more information about the SDK, you can refer to the official documentation of browser SDK. The browser SDK shares the same APIs with the Chrome extension SDK.

Other considerations

  • Service worker bundling: If you use a bundler like Webpack or Rollup, you need to explicitly set the target to browser or similar to avoid unnecessary bundling of Node.js modules.
  • Module resolution: Logto Chrome extension SDK is an ESM-only module.

See our sample project for a complete example with TypeScript, Rollup, and other configurations.

Conclusion

With users authenticated, you can now securely offer paid features in your Chrome extension. For instance, you can store the user's subscription status in the user profile, and check it when the user opens the extension.

Combining the power of Chrome extensions and Logto, you can build a more flexible and customizable extension that both you and your users will love.