Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions .changeset/wild-pianos-matter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'@clerk/chrome-extension': minor
---

Added new `createClerkClient()` export from @clerk/chrome-extension/client

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

const publishableKey = process.env.CLERK_PUBLISHABLE_KEY;
// Use createClerkClient in a popup or side panel
const clerk = createClerkClient({ publishableKey });

// Use createClerkClient in a background service worker
const clerk = await createClerkClient({ publishableKey: 'pk_...', background: true });
```

`createClerkClient()` from @clerk/chrome-extension/background is deprecated.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ out-tsc
out
**/dist/*
**/build/*
!playground/browser-extension-js/build/manifest.json
!playground/browser-extension-js/build/popup.html
!playground/browser-extension-js/build/popup.css
packages/*/dist/**
**/.pnpm-store/**

Expand Down Expand Up @@ -62,6 +65,7 @@ lerna-debug.log
.dev.vars
.env.local
playground/*/build
!playground/browser-extension-js/build
playground/*/public/build
playground/*/.cache
playground/custom
Expand Down
67 changes: 35 additions & 32 deletions packages/chrome-extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,41 +41,44 @@ Please see the latest extension [authentication support matrix](https://clerk.co

### Usage

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.

```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>"]}'
```

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.

```sh
# Vite
VITE_CLERK_PUBLISHABLE_KEY=pk_test_xxx
```

```sh
# Plasmo
PLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx
```

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 extension.
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.
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.
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.

```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>"]}'
```

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.

```sh
# Vite
VITE_CLERK_PUBLISHABLE_KEY=pk_test_xxx
```

```sh
# Plasmo
PLASMO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx
```

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 extension.
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.
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.

## Example repositories

- [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
- [Quickstart](https://github.com/clerk/clerk-chrome-extension-quickstart): The extension is using its own authentication
- [SyncHost, Service Workers and `react-router`](https://github.com/clerk/clerk-chrome-extension-demo): The extension shares auth with a website in the same browser, needs to access user information from Clerk in a service worker or needs to use `react-router`

## Support

Expand Down
5 changes: 5 additions & 0 deletions packages/chrome-extension/client/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"main": "../dist/cjs/client/index.js",
"module": "../dist/esm/client/index.js",
"types": "../dist/types/client/index.d.ts"
}
39 changes: 38 additions & 1 deletion packages/chrome-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,48 @@
},
"license": "MIT",
"sideEffects": false,
"exports": {
".": {
"import": {
"types": "./dist/types/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/types/index.d.ts",
"default": "./dist/cjs/index.js"
}
},
"./client": {
"import": {
"types": "./dist/types/client/index.d.ts",
"default": "./dist/esm/client/index.js"
},
"require": {
"types": "./dist/types/client/index.d.ts",
"default": "./dist/cjs/client/index.js"
}
},
"./background": {
"import": {
"types": "./dist/types/background/index.d.ts",
"default": "./dist/esm/background/index.js"
},
"require": {
"types": "./dist/types/background/index.d.ts",
"default": "./dist/cjs/background/index.js"
}
},
"./types": {
"types": "./dist/types/types/index.d.ts"
},
"./package.json": "./package.json"
},
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts",
"files": [
"background",
"client",
"dist",
"internal",
"react",
Expand All @@ -42,7 +79,7 @@
"format": "node ../../scripts/format-package.mjs",
"format:check": "node ../../scripts/format-package.mjs --check",
"lint": "eslint src",
"lint:attw": "attw --pack . --profile node16",
"lint:attw": "attw --pack . --profile node16 --ignore-rules unexpected-module-syntax",
"lint:publint": "publint",
"test": "vitest run",
"test:ci": "vitest run --maxWorkers=70%",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`client public exports > should not include a breaking change 1`] = `
[
"createClerkClient",
]
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { describe, expect, it } from 'vitest';

import * as publicExports from '../client';

describe('client public exports', () => {
it('should not include a breaking change', () => {
expect(Object.keys(publicExports).sort()).toMatchSnapshot();
});
});
35 changes: 28 additions & 7 deletions packages/chrome-extension/src/background/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,34 @@ import type { Clerk } from '@clerk/clerk-js/no-rhc';
import {
createClerkClient as _createClerkClient,
type CreateClerkClientOptions as _CreateClerkClientOptions,
} from '../internal';
import { SCOPE } from '../types';
} from '../utils/clerk-client';

export type CreateClerkClientOptions = Omit<_CreateClerkClientOptions, 'scope'>;
/**
* @deprecated Use `createClerkClient` from `@clerk/chrome-extension/client` with `{ background: true }` instead.
*
* @example
* // Before (deprecated):
* import { createClerkClient } from '@clerk/chrome-extension/background';
* const clerk = await createClerkClient({ publishableKey: 'pk_...' });
*
* // After:
* import { createClerkClient } from '@clerk/chrome-extension/client';
* const clerk = await createClerkClient({ publishableKey: 'pk_...', background: true });
*/
export type CreateClerkClientOptions = Omit<_CreateClerkClientOptions, 'background'>;

export async function createClerkClient(opts: CreateClerkClientOptions): Promise<Clerk> {
const clerk = await _createClerkClient({ ...opts, scope: SCOPE.BACKGROUND });
await clerk.load({ standardBrowser: false });
return clerk;
/**
* @deprecated Use `createClerkClient` from `@clerk/chrome-extension/client` with `{ background: true }` instead.
*
* @example
* // Before (deprecated):
* import { createClerkClient } from '@clerk/chrome-extension/background';
* const clerk = await createClerkClient({ publishableKey: 'pk_...' });
*
* // After:
* import { createClerkClient } from '@clerk/chrome-extension/client';
* const clerk = await createClerkClient({ publishableKey: 'pk_...', background: true });
*/
export function createClerkClient(opts: CreateClerkClientOptions): Promise<Clerk> {
return _createClerkClient({ ...opts, background: true }) as Promise<Clerk>;
}
2 changes: 2 additions & 0 deletions packages/chrome-extension/src/client/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { createClerkClient } from '../utils/clerk-client';
export type { CreateClerkClientOptions } from '../utils/clerk-client';
54 changes: 54 additions & 0 deletions packages/chrome-extension/src/utils/__tests__/clerk-client.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';

const mockLoad = vi.fn().mockResolvedValue(undefined);
const mockUi = { __brand: 'clerk-ui', ClerkUI: vi.fn() };

vi.mock('@clerk/clerk-js/no-rhc', () => {
const Clerk = vi.fn(() => ({
load: mockLoad,
})) as ReturnType<typeof vi.fn> & { sdkMetadata: Record<string, string> };
Clerk.sdkMetadata = {};
return { Clerk };
});

vi.mock('@clerk/ui', () => ({
ui: mockUi,
}));

import { createClerkClient } from '../clerk-client';

describe('createClerkClient', () => {
beforeEach(() => {
vi.clearAllMocks();
});

describe('non-background (popup)', () => {
it('returns a Clerk instance synchronously', () => {
const clerk = createClerkClient({ publishableKey: 'pk_test_123' });
expect(clerk).toBeDefined();
expect(clerk).not.toBeInstanceOf(Promise);
});

it('wraps load() to inject @clerk/ui', async () => {
const clerk = createClerkClient({ publishableKey: 'pk_test_123' });
const loadOpts = { afterSignOutUrl: '/signed-out' };

await clerk.load(loadOpts);

expect(mockLoad).toHaveBeenCalledOnce();
expect(mockLoad).toHaveBeenCalledWith({
...loadOpts,
ui: mockUi,
});
});

it('calls load() with ui even when no options are passed', async () => {
const clerk = createClerkClient({ publishableKey: 'pk_test_123' });

await clerk.load();

expect(mockLoad).toHaveBeenCalledOnce();
expect(mockLoad).toHaveBeenCalledWith({ ui: mockUi });
});
});
});
36 changes: 36 additions & 0 deletions packages/chrome-extension/src/utils/clerk-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Clerk } from '@clerk/clerk-js/no-rhc';

import {
createClerkClient as _createClerkClient,
type CreateClerkClientOptions as _CreateClerkClientOptions,
} from '../internal';
import { SCOPE } from '../types';

export type CreateClerkClientOptions = Omit<_CreateClerkClientOptions, 'scope'> & {
background?: boolean;
};

export function createClerkClient(opts: CreateClerkClientOptions & { background: true }): Promise<Clerk>;
export function createClerkClient(opts: Omit<CreateClerkClientOptions, 'background'>): Clerk;
export function createClerkClient(opts: CreateClerkClientOptions): Clerk | Promise<Clerk> {
if (opts.background) {
const { background: _, ...rest } = opts;
const clerk = _createClerkClient({ ...rest, scope: SCOPE.BACKGROUND });
return clerk.load({ standardBrowser: false }).then(() => clerk);
}

Clerk.sdkMetadata = {
name: PACKAGE_NAME,
version: PACKAGE_VERSION,
};

const clerk = new Clerk(opts.publishableKey, {});

const originalLoad = clerk.load.bind(clerk);
clerk.load = async (loadOpts?: Parameters<typeof clerk.load>[0]) => {
const { ui } = await import('@clerk/ui');
return originalLoad({ ...loadOpts, ui });
};

return clerk;
}
8 changes: 7 additions & 1 deletion packages/chrome-extension/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ export default defineConfig(overrideOptions => {
const shouldPublish = !!overrideOptions.env?.publish;

const common: Options = {
entry: ['./src/index.ts', './src/background/index.ts', './src/react/index.ts', './src/types/index.ts'],
entry: [
'./src/index.ts',
'./src/background/index.ts',
'./src/client/index.ts',
'./src/react/index.ts',
'./src/types/index.ts',
],
bundle: true,
clean: true,
minify: false,
Expand Down
1 change: 1 addition & 0 deletions playground/browser-extension-js/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CLERK_PUBLISHABLE_KEY=
7 changes: 7 additions & 0 deletions playground/browser-extension-js/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules/
pnpm-lock.yaml
build/*.js
.env
.yalc
yalc.lock
.npmrc
Loading
Loading