Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(chrome-extension): Apply RHC updates to 2.0 #4428

Draft
wants to merge 23 commits into
base: tm/ext-updates
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
06fa37e
chore(chrome-extension): Interim commit
tmilewski Sep 9, 2024
5d794dd
feat(chrome-extension): Make sync hosts more deterministic
tmilewski Sep 10, 2024
e907a1d
chore(chrome-extension): snapshot test
tmilewski Sep 19, 2024
d7ad7ba
fix(chrome-extention): syncHost resolution
tmilewski Sep 27, 2024
eadadee
fix(chrome-extention): update req/res handlers
tmilewski Sep 27, 2024
5628c53
chore(chrome-extension): Remove unauthenticatedHandler
tmilewski Oct 1, 2024
67cc429
chore(extension): Remove testing logs
tmilewski Oct 1, 2024
0bb7903
chore(extension): Update to syncHost
tmilewski Oct 1, 2024
b4a42c9
docs(chrome-extension): Update to support latest changes
tmilewski Oct 3, 2024
c3e06d9
chore(chrome-extension): Update internal docs urls
tmilewski Oct 3, 2024
32e0b4a
docs(chrome-extension): Update latest manifest docs
tmilewski Oct 3, 2024
34d9a43
feat(chrome-extension): Add env constants
tmilewski Oct 9, 2024
1f34f02
chore(chrome-extension): Update to ES2022
tmilewski Oct 9, 2024
dd0c5cc
fix(chrome-extension): AUTH_HEADER enviroment changes
tmilewski Oct 9, 2024
e150832
fix(chrome-extension): Vite env vars
tmilewski Oct 9, 2024
c6c054c
chore(chrome-extension): Release Clean Up
tmilewski Oct 10, 2024
7735ae4
chore(chrome-extension): Release Clean Up
tmilewski Oct 10, 2024
c849ec2
chore(chrome-extension): Release Clean Up
tmilewski Oct 10, 2024
b196f0b
chore(chrome-extension): Loosen syncHost type
tmilewski Oct 17, 2024
d620f6e
fix(chrome-extension): Update dev auth header
tmilewski Oct 18, 2024
5846131
fix: Out-of-sync background clients
tmilewski Oct 30, 2024
c4ff4c8
chore(chrome-extension): Apply RHC updates to 2.0
tmilewski Oct 29, 2024
cc7edf7
fix: shared/devBrowser import
tmilewski Oct 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .changeset/wise-spiders-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
'@clerk/chrome-extension': major
---

#### Permission Updates (BREAKING)

The `storage` entry in `host_permissions` is now required for all extensions.
While it's likely that this is already enabled in your extension, this change is to ensure that Clerk can store the necessary data for the extension to function properly.

**How to Update:** Add the following to your `manifest.json` file:

```json
{
"host_permissions": ["storage"]
}
```

#### Introducing `syncHost` (BREAKING)

In an effort to make the handling of sync hosts more deterministic, we have introduced a new parameter `syncHost` to `<ClerkProvider>`

**How to Update:** Replace `syncSessionWithTab` with `syncHost` in the `<ClerkProvider>` component and set `syncHost` to the host that you intend to synchronize with.

#### Service Workers `createClerkClient`

We've introduced a new method `createClerkClient` to handle background tasks in your extension!

```ts
import { createClerkClient } from '@clerk/chrome-extension/background';

// Create a new Clerk instance and get a fresh token for the user
async function getToken() {
const clerk = await createClerkClient({
publishableKey: process.env.PLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY,
});
return await clerk.session?.getToken();
}

// Create a listener to listen for messages from content scripts
// NOTE: A runtime listener cannot be async.
// It must return true, in order to keep the connection open and send a response later.
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
// You can use the token in the listener to perform actions on behalf of the user
// OR send the token back to the content script
getToken().then(token => sendResponse({ token }));
return true;
});
```
142 changes: 32 additions & 110 deletions packages/chrome-extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,134 +27,56 @@

## Getting Started

[Clerk](https://clerk.com/?utm_source=github&utm_medium=clerk_chrome_extension) is the easiest way to add authentication and user management to your Chrome Extension. Add sign up, sign in, and profile management to your application in minutes.
[Clerk](https://clerk.com/?utm_source=github&utm_medium=clerk_chrome_extension) is the easiest way to add authentication and user management to your Browser Extension. Add sign up, sign in, and profile management to your application in minutes.

### Prerequisites

- Node.js `>=18.17.0` or later
- An existing Clerk application. [Create your account for free](https://dashboard.clerk.com/sign-up?utm_source=github&utm_medium=clerk_chrome_extension).
- An existing React app (using [Vite](https://crxjs.dev/vite-plugin/) for example)

### Installation

1. Add `@clerk/chrome-extension` to your project:

```shell
npm install @clerk/chrome-extension
```

1. Retrieve the **Publishable key** from your [Clerk dashboard](https://dashboard.clerk.com/last-active?path=api-keys) and set it as an environment variable. For example, if you used Vite:

```sh
VITE_CLERK_PUBLISHABLE_KEY=pk_test_xxx
```

1. Add `<ClerkProvider>` to your app and define the `routerPush` & `routerReplace` properties. For example, with using `react-router-dom`:

```tsx
// App.tsx
import { SignedIn, SignedOut, SignIn, SignUp, ClerkProvider } from '@clerk/chrome-extension';
import { useNavigate, Routes, Route, MemoryRouter } from 'react-router-dom';

function HelloUser() {
return <p>Hello user</p>;
}

const publishableKey = process.env.VITE_CLERK_PUBLISHABLE_KEY || '';

function ClerkProviderWithRoutes() {
const navigate = useNavigate();

return (
<ClerkProvider
publishableKey={publishableKey}
routerPush={to => navigate(to)}
routerReplace={to => navigate(to, { replace: true })}
>
<Routes>
<Route
path='/sign-up/*'
element={<SignUp signInUrl='/' />}
/>
<Route
path='/'
element={
<>
<SignedIn>
<HelloUser />
</SignedIn>
<SignedOut>
<SignIn
afterSignInUrl='/'
signUpUrl='/sign-up'
/>
</SignedOut>
</>
}
/>
</Routes>
</ClerkProvider>
);
}

function App() {
return (
<MemoryRouter>
<ClerkProviderWithRoutes />
</MemoryRouter>
);
}

export default App;
```

## Usage

Example repositories:
### Feature Support

- [Standalone](https://github.com/clerk/clerk-chrome-extension-starter/tree/main): The extension is using its own authentication
- [WebSSO](https://github.com/clerk/clerk-chrome-extension-starter/tree/webapp_sso): The extensions shares authentication with a website in the same browser

### WebSSO

If you want to use **WebSSO** (extension shares authentication state with a website in same browser) you'll need to add the `syncSessionWithTab` prop to `<ClerkProvider>`.
Please see the latest extension [authentication support matrix](https://clerk.com/docs/references/chrome-extension/overview?utm_source=github&utm_medium=clerk_chrome_extension) in the Clerk documentation for more information.

#### Extension Manifest (`manifest.json`)
### Usage

You must enable the following permissions in your `manifest.json` file:
1. **Installation:** `npm install @clerk/chrome-extension`
2. **Set a consistent extension key**: A browser extension can be identified by its unique key, in a similar way to how a website can be identified by its domain. You will need to explicitly configure your extension's key or it will change often. If the key changes, it can cause the extension to fail. See the [Configure a Consistent Key](https://clerk.com/docs/references/chrome-extension/configure-consistent-crx-id?utm_source=github&utm_medium=clerk_chrome_extension) guide for more information.
3. **Update Clerk Settings**: Once you've set up a consistent extension key, you'll need to configure your Clerk settings to allow the extension to communicate with your Clerk API.
You can do this by adding the extension key to the list of allowed origins in your Clerk settings. Setting the `allowed_origins` is **required** for both **Development** and **Production** instances.

```json
{
"permissions": ["cookies", "storage"]
}
```
```bash
curl -X PATCH https://api.clerk.com/v1/instance \
-H "Content-type: application/json" \
-H "Authorization: Bearer <CLERK_SECRET_KEY>" \
-d '{"allowed_origins": ["chrome-extension://<YOUR_EXTENSION_KEY>"]}'
```

More info on the "cookies" permission: [Google Developer Cookies Reference](https://developer.chrome.com/docs/extensions/reference/cookies/).
More info on the "storage" permission: [Google Developer Storage Reference](https://developer.chrome.com/docs/extensions/reference/storage/).
4. **Set Environment Variables:** Retrieve the **Publishable key** from your [Clerk dashboard](https://dashboard.clerk.com/last-active?path=api-keys&utm_source=github&utm_medium=clerk_chrome_extension) and set it as an environment variable.

#### Host Permissions
```sh
# Vite
VITE_CLERK_PUBLISHABLE_KEY=pk_test_xxx
```

You must enable the following host permissions in your `manifest.json` file:
```sh
# Plasmo
PLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx
```

- **Development:** `"host_permissions": ["http://localhost"]`
- If you're using a domain other than `localhost`, you'll want replace that entry with your domain: `http://<DOMAIN>`
- **Production:** `"host_permissions": ["https://<YOUR_CLERK_FRONTEND_API_GOES_HERE>/"]`
- Your Frontend API URL can be found in [Clerk Dashboard](https://dashboard.clerk.com/last-active?path=api-keys) under the **Show API URLs** option.
5. **Update the extension manifest:** You'll need to update your extension manifest permissions to support Clerk.
1. [**Base configuration**:](/packages/chrome-extension/docs/manifest.md#base-configuration) Use this if you plan to only use Clerk in the context of the extention.
2. [**Session sync configuration**:](/packages/chrome-extension/docs/manifest.md#sync-host-configuration) Use this if you plan to share authentication with a website in the same browser.
6. **Add Clerk to your app:** Though not required, we generally suggest using Plasmo for browser extension development. This will enforce common standards across your extension as well as allow for easier integration with other browsers in the future.

For more info on host permissions visit [Google's developer `host_permissions` reference](https://developer.chrome.com/docs/extensions/mv3/declare_permissions/#host-permissions).
1. [**Via `ClerkProvider`:**](/packages/chrome-extension/docs/clerk-provider.md) This is the general approach to all extensions. From here you'll be able to support extension-only authentication as well as sharing authentication with a website in the same browser.
2. [**Via service workers**:](/packages/chrome-extension/docs/service-workers.md) If you also require the use of background service workers, this will allow you to access the Clerk client from the extension context.

#### Clerk Settings
## Example repositories

Add your Chrome extension origin to your instance's `allowed_origins` using the [Backend API](https://clerk.com/docs/reference/backend-api):

```bash
curl -X PATCH https://api.clerk.com/v1/instance \
-H "Authorization: Bearer sk_secret_key" \
-H "Content-type: application/json" \
-d '{"allowed_origins": ["chrome-extension://extension_id_goes_here"]}'
```

Setting the `allowed_origins` is **required** for both **Development** and **Production** instances.
- [Standalone](https://github.com/clerk/clerk-chrome-extension-starter/tree/main): The extension is using its own authentication
- [WebSSO](https://github.com/clerk/clerk-chrome-extension-starter/tree/webapp_sso): The extensions shares authentication with a website in the same browser

## Support

Expand Down
114 changes: 114 additions & 0 deletions packages/chrome-extension/docs/clerk-provider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Clerk Provider Usage

## Core App

```tsx
// App.tsx
import { SignedIn, SignedOut, SignInButton, UserButton } from '@clerk/chrome-extension';

function App() {
return (
<>
<header>
<SignedOut>
<SignInButton mode='modal' />
</SignedOut>
<SignedIn>
<UserButton />
</SignedIn>
</header>
<main>
<SignedOut>Please Sign In</SignedOut>
<SignedIn>Welcome!</SignedIn>
</main>
</>
);
}

export default App;
```

## Provider

Though not required, we generally suggest using Plasmo for browser extension development.
This will enforce common standards across your extension as well as allow for easier integration with other browsers in the future.

```tsx
// IndexPopup.tsx
import { ClerkProvider } from '@clerk/chrome-extension';
import App from './App';

const PUBLISHABLE_KEY = process.env.PLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY; // REQUIRED
const SYNC_HOST = process.env.PLASMO_PUBLIC_CLERK_SYNC_HOST; // OPTIONAL

function IndexPopup() {
return (
<ClerkProvider
publishableKey={PUBLISHABLE_KEY}
/* OPTIONAL: If syncHost is set, the extension will attempt to grab credentials from the host provided */
syncHost={SYNC_HOST}
>
<App />
</ClerkProvider>
);
}

export default IndexPopup;
```

## Routing

You can hook into the router of your choice to handle navigation. Here's an example using `react-router-dom`:

```tsx
import { ClerkProvider } from '@clerk/chrome-extension';
import { useNavigate, Routes, Route, MemoryRouter } from 'react-router-dom';
import App from './App';

const publishableKey = process.env.VITE_CLERK_PUBLISHABLE_KEY;

function AppWithRouting() {
const navigate = useNavigate();

return (
<Routes>
<Route
path='/sign-up/*'
element={<SignUp signInUrl='/' />}
/>
<Route
path='/'
element={
<>
<SignedIn>Welcome User!</SignedIn>
<SignedOut>
<SignIn
forceRedirectUrl='/'
signUpUrl='/sign-up'
/>
</SignedOut>
</>
}
/>
</Routes>
);
}

function IndexPopupWithRouting() {
const navigate = useNavigate();

return (
<MemoryRouter>
<ClerkProvider
publishableKey={PUBLISHABLE_KEY}
routerPush={to => navigate(to)}
routerReplace={to => navigate(to, { replace: true })}
>
<AppWithRouting />
</ClerkProvider>
</MemoryRouter>
);
}

export default IndexPopupWithRouting;
```
53 changes: 53 additions & 0 deletions packages/chrome-extension/docs/manifest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Clerk Browser Extension Manifest

## Base Configuration

All Browser Extensions require, at minimum, the following configuration:

### Permissions

```json
{
"permissions": ["storage"]
}
```

More info on the "storage" permission: [Google Developer Storage Reference](https://developer.chrome.com/docs/extensions/reference/storage/).

## Sync Host Configuration

When syncing with a host application, you must enable the following permissions:

### Permissions

```json
{
"permissions": ["cookies", "storage"]
}
```

More info on the "cookies" permission: [Google Developer Cookies Reference](https://developer.chrome.com/docs/extensions/reference/cookies/).

More info on the "storage" permission: [Google Developer Storage Reference](https://developer.chrome.com/docs/extensions/reference/storage/).

### Host Permissions

You must enable the following host permissions in your `manifest.json` file. This will allow the extension to communicate with the host application.

```json
{
"host_permissions": [
"http://localhost/*"
"https://<YOUR_PRODUCTION_APP_DOMAIN>/*",
"https://YOUR_CLERK_DEVELOPMENT_FRONTEND_API.clerk.accounts.dev/*",
"https://<YOUR_CLERK_PRODUCTION_FRONTEND_API>/*"
]
}
```

**Notes:**

- Please make sure to include `/*` at the end of each `host_permission`. Feel free to later scope this down, if your usage sees fit.
- The `YOUR_PRODUCTION_APP_DOMAIN` and `YOUR_CLERK_PRODUCTION_FRONTEND_API` are only required when you're ready to go to production.

Your Frontend API URLs can be found in [Clerk Dashboard](https://dashboard.clerk.com/last-active?path=api-keys) under the **Show API URLs** option.
Loading
Loading