diff --git a/.vscode/settings.json b/.vscode/settings.json
index 87589341d80..a6b1d9843e3 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -10,6 +10,12 @@
"mode": "auto"
}
],
+ "json.schemas": [
+ {
+ "fileMatch": ["manifest.json"],
+ "url": "https://json.schemastore.org/chrome-manifest.json"
+ }
+ ],
"cSpell.words": [
"accountsstage",
"adduser",
diff --git a/package-lock.json b/package-lock.json
index b95d0b128f6..68ba7bff125 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -33280,16 +33280,16 @@
},
"packages/backend": {
"name": "@clerk/backend",
- "version": "1.0.0-alpha-v5.4",
+ "version": "1.0.0-alpha-v5.6",
"license": "MIT",
"dependencies": {
- "@clerk/shared": "2.0.0-alpha-v5.3",
+ "@clerk/shared": "2.0.0-alpha-v5.5",
"cookie": "0.5.0",
"snakecase-keys": "5.4.4",
"tslib": "2.4.1"
},
"devDependencies": {
- "@clerk/types": "4.0.0-alpha-v5.6",
+ "@clerk/types": "4.0.0-alpha-v5.8",
"@cloudflare/workers-types": "^3.18.0",
"@types/chai": "^4.3.3",
"@types/cookie": "^0.5.1",
@@ -33319,11 +33319,11 @@
},
"packages/chrome-extension": {
"name": "@clerk/chrome-extension",
- "version": "1.0.0-alpha-v5.6",
+ "version": "1.0.0-alpha-v5.8",
"license": "MIT",
"dependencies": {
- "@clerk/clerk-js": "5.0.0-alpha-v5.6",
- "@clerk/clerk-react": "5.0.0-alpha-v5.6"
+ "@clerk/clerk-js": "5.0.0-alpha-v5.8",
+ "@clerk/clerk-react": "5.0.0-alpha-v5.8"
},
"devDependencies": {
"@types/chrome": "*",
@@ -33344,12 +33344,12 @@
},
"packages/clerk-js": {
"name": "@clerk/clerk-js",
- "version": "5.0.0-alpha-v5.6",
+ "version": "5.0.0-alpha-v5.8",
"license": "MIT",
"dependencies": {
- "@clerk/localizations": "2.0.0-alpha-v5.5",
- "@clerk/shared": "2.0.0-alpha-v5.3",
- "@clerk/types": "4.0.0-alpha-v5.6",
+ "@clerk/localizations": "2.0.0-alpha-v5.6",
+ "@clerk/shared": "2.0.0-alpha-v5.5",
+ "@clerk/types": "4.0.0-alpha-v5.8",
"@emotion/cache": "11.11.0",
"@emotion/react": "11.11.1",
"@floating-ui/react": "0.25.4",
@@ -33684,17 +33684,17 @@
},
"packages/expo": {
"name": "@clerk/clerk-expo",
- "version": "1.0.0-alpha-v5.6",
+ "version": "1.0.0-alpha-v5.8",
"license": "MIT",
"dependencies": {
- "@clerk/clerk-js": "5.0.0-alpha-v5.6",
- "@clerk/clerk-react": "5.0.0-alpha-v5.6",
- "@clerk/shared": "2.0.0-alpha-v5.3",
+ "@clerk/clerk-js": "5.0.0-alpha-v5.8",
+ "@clerk/clerk-react": "5.0.0-alpha-v5.8",
+ "@clerk/shared": "2.0.0-alpha-v5.5",
"base-64": "^1.0.0",
"react-native-url-polyfill": "2.0.0"
},
"devDependencies": {
- "@clerk/types": "^4.0.0-alpha-v5.6",
+ "@clerk/types": "^4.0.0-alpha-v5.8",
"@types/base-64": "^1.0.2",
"@types/node": "^18.17.0",
"@types/react": "*",
@@ -33717,12 +33717,12 @@
},
"packages/fastify": {
"name": "@clerk/fastify",
- "version": "1.0.0-alpha-v5.6",
+ "version": "1.0.0-alpha-v5.8",
"license": "MIT",
"dependencies": {
- "@clerk/backend": "1.0.0-alpha-v5.4",
- "@clerk/shared": "2.0.0-alpha-v5.3",
- "@clerk/types": "4.0.0-alpha-v5.6",
+ "@clerk/backend": "1.0.0-alpha-v5.6",
+ "@clerk/shared": "2.0.0-alpha-v5.5",
+ "@clerk/types": "4.0.0-alpha-v5.8",
"cookies": "0.8.0"
},
"devDependencies": {
@@ -33740,17 +33740,17 @@
}
},
"packages/gatsby-plugin-clerk": {
- "version": "5.0.0-alpha-v5.6",
+ "version": "5.0.0-alpha-v5.8",
"license": "MIT",
"dependencies": {
- "@clerk/backend": "1.0.0-alpha-v5.4",
- "@clerk/clerk-react": "5.0.0-alpha-v5.6",
- "@clerk/clerk-sdk-node": "5.0.0-alpha-v5.4",
+ "@clerk/backend": "1.0.0-alpha-v5.6",
+ "@clerk/clerk-react": "5.0.0-alpha-v5.8",
+ "@clerk/clerk-sdk-node": "5.0.0-alpha-v5.6",
"cookie": "0.5.0",
"tslib": "2.4.1"
},
"devDependencies": {
- "@clerk/types": "4.0.0-alpha-v5.6",
+ "@clerk/types": "4.0.0-alpha-v5.8",
"@types/cookie": "^0.5.0",
"@types/node": "^18.17.0",
"eslint-config-custom": "*",
@@ -33770,10 +33770,10 @@
},
"packages/localizations": {
"name": "@clerk/localizations",
- "version": "2.0.0-alpha-v5.5",
+ "version": "2.0.0-alpha-v5.6",
"license": "MIT",
"devDependencies": {
- "@clerk/types": "4.0.0-alpha-v5.6",
+ "@clerk/types": "4.0.0-alpha-v5.8",
"@types/node": "^18.17.0",
"eslint-config-custom": "*",
"tsup": "*",
@@ -33789,16 +33789,16 @@
},
"packages/nextjs": {
"name": "@clerk/nextjs",
- "version": "5.0.0-alpha-v5.6",
+ "version": "5.0.0-alpha-v5.8",
"license": "MIT",
"dependencies": {
- "@clerk/backend": "1.0.0-alpha-v5.4",
- "@clerk/clerk-react": "5.0.0-alpha-v5.6",
- "@clerk/shared": "2.0.0-alpha-v5.3",
+ "@clerk/backend": "1.0.0-alpha-v5.6",
+ "@clerk/clerk-react": "5.0.0-alpha-v5.8",
+ "@clerk/shared": "2.0.0-alpha-v5.5",
"path-to-regexp": "6.2.1"
},
"devDependencies": {
- "@clerk/types": "4.0.0-alpha-v5.6",
+ "@clerk/types": "4.0.0-alpha-v5.8",
"@types/node": "^18.17.0",
"@types/react": "*",
"@types/react-dom": "*",
@@ -33823,11 +33823,11 @@
},
"packages/react": {
"name": "@clerk/clerk-react",
- "version": "5.0.0-alpha-v5.6",
+ "version": "5.0.0-alpha-v5.8",
"license": "MIT",
"dependencies": {
- "@clerk/shared": "2.0.0-alpha-v5.3",
- "@clerk/types": "4.0.0-alpha-v5.6",
+ "@clerk/shared": "2.0.0-alpha-v5.5",
+ "@clerk/types": "4.0.0-alpha-v5.8",
"eslint-config-custom": "*",
"semver": "^7.5.4",
"tslib": "2.4.1"
@@ -33855,17 +33855,17 @@
},
"packages/remix": {
"name": "@clerk/remix",
- "version": "4.0.0-alpha-v5.6",
+ "version": "4.0.0-alpha-v5.8",
"license": "MIT",
"dependencies": {
- "@clerk/backend": "1.0.0-alpha-v5.4",
- "@clerk/clerk-react": "5.0.0-alpha-v5.6",
- "@clerk/shared": "2.0.0-alpha-v5.3",
+ "@clerk/backend": "1.0.0-alpha-v5.6",
+ "@clerk/clerk-react": "5.0.0-alpha-v5.8",
+ "@clerk/shared": "2.0.0-alpha-v5.5",
"cookie": "0.5.0",
"tslib": "2.4.1"
},
"devDependencies": {
- "@clerk/types": "4.0.0-alpha-v5.6",
+ "@clerk/types": "4.0.0-alpha-v5.8",
"@remix-run/react": "^2.0.0",
"@remix-run/server-runtime": "^2.0.0",
"@types/cookie": "^0.5.0",
@@ -33891,16 +33891,16 @@
},
"packages/sdk-node": {
"name": "@clerk/clerk-sdk-node",
- "version": "5.0.0-alpha-v5.4",
+ "version": "5.0.0-alpha-v5.6",
"license": "MIT",
"dependencies": {
- "@clerk/backend": "1.0.0-alpha-v5.4",
- "@clerk/shared": "2.0.0-alpha-v5.3",
+ "@clerk/backend": "1.0.0-alpha-v5.6",
+ "@clerk/shared": "2.0.0-alpha-v5.5",
"camelcase-keys": "6.2.2",
"snakecase-keys": "3.2.1"
},
"devDependencies": {
- "@clerk/types": "4.0.0-alpha-v5.6",
+ "@clerk/types": "4.0.0-alpha-v5.8",
"@types/express": "4.17.14",
"@types/node": "^18.17.0",
"eslint-config-custom": "*",
@@ -33933,7 +33933,7 @@
},
"packages/shared": {
"name": "@clerk/shared",
- "version": "2.0.0-alpha-v5.3",
+ "version": "2.0.0-alpha-v5.5",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -33942,7 +33942,7 @@
"swr": "2.2.0"
},
"devDependencies": {
- "@clerk/types": "4.0.0-alpha-v5.6",
+ "@clerk/types": "4.0.0-alpha-v5.8",
"@types/glob-to-regexp": "0.4.1",
"@types/js-cookie": "3.0.2",
"@types/node": "^18.17.0",
@@ -33978,7 +33978,7 @@
"version": "2.0.0-alpha-v5.2",
"license": "MIT",
"devDependencies": {
- "@clerk/types": "4.0.0-alpha-v5.6",
+ "@clerk/types": "4.0.0-alpha-v5.8",
"@types/node": "^18.17.0",
"eslint-config-custom": "*",
"typescript": "*"
@@ -33993,7 +33993,7 @@
},
"packages/types": {
"name": "@clerk/types",
- "version": "4.0.0-alpha-v5.6",
+ "version": "4.0.0-alpha-v5.8",
"license": "MIT",
"dependencies": {
"csstype": "3.1.1"
diff --git a/playground/chrome-extension/.env.example b/playground/chrome-extension/.env.example
new file mode 100644
index 00000000000..4f4796f3fc9
--- /dev/null
+++ b/playground/chrome-extension/.env.example
@@ -0,0 +1 @@
+VITE_CLERK_PUBLISHABLE_KEY=YOUR_CLERK_PUBLISHABLE_KEY_GOES_HERE
diff --git a/playground/chrome-extension/.gitignore b/playground/chrome-extension/.gitignore
new file mode 100644
index 00000000000..b780dd861d3
--- /dev/null
+++ b/playground/chrome-extension/.gitignore
@@ -0,0 +1,14 @@
+# dependencies
+/node_modules
+
+# manifests
+manifest.*
+!manifest.*.example
+
+# production
+/build
+/dist
+
+# misc
+.DS_Store
+.env
diff --git a/playground/chrome-extension/README.md b/playground/chrome-extension/README.md
new file mode 100644
index 00000000000..ec04cbcaf49
--- /dev/null
+++ b/playground/chrome-extension/README.md
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+# Clerk Chrome Extension Starter
+
+This starter project shows how to use [Clerk](https://www.clerk.dev/?utm_source=github&utm_medium=starter_repos&utm_campaign=chrome_extension_start) authentication in a React based [Chrome Extension](https://developer.chrome.com/docs/extensions/).
+
+[![chat on Discord](https://img.shields.io/discord/856971667393609759.svg?logo=discord)](https://discord.com/invite/b5rXHjAg7A)
+[![documentation](https://img.shields.io/badge/documentation-clerk-green.svg)](https://docs.clerk.dev)
+[![twitter](https://img.shields.io/twitter/follow/ClerkDev?style=social)](https://twitter.com/intent/follow?screen_name=ClerkDev)
+
+If you run into issues, be sure to check our [main npm page](https://www.npmjs.com/package/@clerk/chrome-extension) for any updated settings/steps you may need to be aware of.
+
+---
+
+**Clerk is Hiring!**
+
+Would you like to work on Open Source software and help maintain this repository? [Apply today!](https://apply.workable.com/clerk-dev/)
+
+---
+
+## Introduction
+
+This project was bootstrapped with [Vite](https://vitejs.dev/) with [CRXJ](https://crxjs.dev/vite-plugin/).
+
+It's a kitchen-sink starter of how to use ClerkJS in a Chrome Extension either as a Standalone App or alongside a Web Application via WebSSO.
+
+
+
+It demonstrates a basic password or OTP flow using ClerkJS Components in the following extension contexts:
+
+- [x] Popup `action.*`
+- [x] Chrome Pages
+ - [x] New Tab `chrome_url_overrides.newtab`
+ - [x] History `chrome_url_overrides.history`
+ - [x] Bookmark Manager `chrome_url_overrides.bookmarks`
+- [x] Dev Tools `devtools_page`
+- [x] Extension Options `options_ui`
+
+You may safely remove any of the above contexts from the `manifest.json` if you do not need them.
+
+This repo will be enhanced with examples of authentication redirection flows such as OAuth or Magic Links and advanced extension patterns.
+
+## Getting Started
+
+1. Sign up for a Clerk account at [https://clerk.com](https://dashboard.clerk.com/sign-up?utm_source=github&utm_medium=template_repos&utm_campaign=chrome_extension_template).
+2. Go to the [Clerk dashboard](https://dashboard.clerk.com?utm_source=github&utm_medium=template_repos&utm_campaign=chrome_extension_template) and create an application.
+3. Clone the repository `git clone https://github.com/clerkinc/clerk-chrome-extension-starter.git clerk-chrome-extension-starter`
+4. Go to the project directory: cd clerk-chrome-extension-starter
+5. Install dependencies: `npm install`
+6. Copy example files and sent the required variables in each file:
+ - `cp .env.example .env`
+ - `cp manifest.json.example manifest.json`
+ - `cp manifest.dev.json.example manifest.dev.json` (The attributes in this file overwrite the attributes in `manifest.json` when running in development mode.)
+7. Launch the development server: `npm run dev`
+
+The files generated in the `dist` directory can be loaded as an unpacked extension in Chrome.
+
+PLEASE NOTE: Any changes to the manifest require a reload of the extension in [chrome://extensions/](chrome://extensions/).
diff --git a/playground/chrome-extension/demo.png b/playground/chrome-extension/demo.png
new file mode 100644
index 00000000000..4f7f1975a5e
Binary files /dev/null and b/playground/chrome-extension/demo.png differ
diff --git a/playground/chrome-extension/manifest.dev.json.example b/playground/chrome-extension/manifest.dev.json.example
new file mode 100644
index 00000000000..4e80c18a706
--- /dev/null
+++ b/playground/chrome-extension/manifest.dev.json.example
@@ -0,0 +1,10 @@
+{
+ "host_permissions": [
+ "*://localhost/*",
+ "https:///*"
+ ],
+ "icons": {
+ "32": "dev-icon-32.png",
+ "128": "dev-icon-128.png"
+ }
+}
diff --git a/playground/chrome-extension/manifest.json.example b/playground/chrome-extension/manifest.json.example
new file mode 100644
index 00000000000..adf133812bc
--- /dev/null
+++ b/playground/chrome-extension/manifest.json.example
@@ -0,0 +1,26 @@
+{
+ "manifest_version": 3,
+ "name": "Clerk Chrome Extension Playground",
+ "description": "Playground App for the Clerk Chrome Extension",
+ "action": {
+ "default_popup": "src/pages/popup/index.html"
+ },
+ "chrome_url_overrides": {
+ "newtab": "src/pages/new-tab/index.html"
+ },
+ "devtools_page": "src/pages/devtools/index.html",
+ "host_permissions": [
+ "https:///*"
+ ],
+ "icons": {
+ "32": "icon-32.png",
+ "128": "icon-128.png"
+ },
+ "options_ui": {
+ "page": "src/pages/options/index.html"
+ },
+ "permissions": [
+ "cookies",
+ "storage"
+ ]
+}
diff --git a/playground/chrome-extension/nodemon.json b/playground/chrome-extension/nodemon.json
new file mode 100644
index 00000000000..72faa888928
--- /dev/null
+++ b/playground/chrome-extension/nodemon.json
@@ -0,0 +1,14 @@
+{
+ "env": {
+ "__DEV__": "true"
+ },
+ "watch": [
+ ".yalc/**/*", "src", "utils", "vite.config.ts", "manifest.json", "manifest.dev.json", "node_modules/@clerk/**/*"
+ ],
+ "ext": "tsx,css,html,ts,json",
+ "ignore": [
+ "src/**/*.spec.ts"
+ ],
+ "exec": "vite build",
+ "delay": "1000"
+}
diff --git a/playground/chrome-extension/package.json b/playground/chrome-extension/package.json
new file mode 100644
index 00000000000..fe85b6c9017
--- /dev/null
+++ b/playground/chrome-extension/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "clerk-chrome-extension-starter",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "dependencies": {
+ "@clerk/chrome-extension": "file:.yalc/@clerk/chrome-extension",
+ "@clerk/clerk-js": "file:.yalc/@clerk/clerk-js",
+ "@clerk/clerk-react": "file:.yalc/@clerk/clerk-react",
+ "@clerk/localizations": "file:.yalc/@clerk/localizations",
+ "@clerk/shared": "file:.yalc/@clerk/shared",
+ "@clerk/themes": "file:.yalc/@clerk/themes",
+ "@clerk/types": "file:.yalc/@clerk/types",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.9.0",
+ "webextension-polyfill": "^0.10.0"
+ },
+ "devDependencies": {
+ "@crxjs/vite-plugin": "^1.0.14",
+ "@types/chrome": "^0.0.253",
+ "@types/node": "^18.17.1",
+ "@types/react": "^18.2.39",
+ "@types/react-dom": "^18.2.17",
+ "@types/webextension-polyfill": "^0.10.0",
+ "@vitejs/plugin-react-swc": "^3.0.1",
+ "autoprefixer": "^10.4.16",
+ "nodemon": "^2.0.20",
+ "postcss": "^8.4.31",
+ "ts-node": "^10.9.1",
+ "typescript": "^4.9.4",
+ "vite": "^4.5.0"
+ },
+ "scripts": {
+ "build": "vite build",
+ "dev": "npm run yalc:add && nodemon",
+ "yalc:add": "yalc add -- @clerk/chrome-extension @clerk/clerk-react @clerk/clerk-js @clerk/localizations @clerk/themes @clerk/types @clerk/shared"
+ }
+}
diff --git a/playground/chrome-extension/public/dev-icon-128.png b/playground/chrome-extension/public/dev-icon-128.png
new file mode 100644
index 00000000000..2272f3c2883
Binary files /dev/null and b/playground/chrome-extension/public/dev-icon-128.png differ
diff --git a/playground/chrome-extension/public/dev-icon-32.png b/playground/chrome-extension/public/dev-icon-32.png
new file mode 100644
index 00000000000..2b72ebdb2ce
Binary files /dev/null and b/playground/chrome-extension/public/dev-icon-32.png differ
diff --git a/playground/chrome-extension/public/icon-128.png b/playground/chrome-extension/public/icon-128.png
new file mode 100644
index 00000000000..15bd934a116
Binary files /dev/null and b/playground/chrome-extension/public/icon-128.png differ
diff --git a/playground/chrome-extension/public/icon-32.png b/playground/chrome-extension/public/icon-32.png
new file mode 100644
index 00000000000..d93c9d3a09f
Binary files /dev/null and b/playground/chrome-extension/public/icon-32.png differ
diff --git a/playground/chrome-extension/src/assets/styles/index.css b/playground/chrome-extension/src/assets/styles/index.css
new file mode 100644
index 00000000000..629dadba4fa
--- /dev/null
+++ b/playground/chrome-extension/src/assets/styles/index.css
@@ -0,0 +1,134 @@
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=Source+Code+Pro&display=swap');
+
+body {
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ background-color: #0c042a;
+ font-family: 'Inter', sans-serif;
+ font-weight: 400;
+ margin: 0;
+ padding: 24px;
+}
+
+pre {
+ background-image: radial-gradient(at 50% 100%, rgba(91, 197, 239, 0.1) 0%, rgba(255, 255, 255, 0) 80%, rgba(91, 197, 239, 0) 80%);
+ background: linear-gradient(0deg, rgba(28, 15, 77, 0.12) -52.71%, rgba(28, 15, 77, 0) 85%),rgba(10, 12, 61, 1);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
+ box-shadow: 0px 42px 56px rgba(5, 1, 54, 0.42),0px 17.5466px 23.3955px rgba(5, 1, 54, 0.301919),0px 9.38125px 12.5083px rgba(5, 1, 54, 0.250365),0px 5.25905px 7.01207px rgba(5, 1, 54, 0.21),0px 2.79304px 3.72406px rgba(5, 1, 54, 0.169635),0px 1.16225px 1.54966px rgba(5, 1, 54, 0.118081);
+ font-family: 'Source Code Pro', monospace;
+ font-weight: 400;
+ letter-spacing: 1px;
+ line-height: 1.5;
+ margin: auto;
+ max-width: 100%;
+ min-height: 100px;
+ padding: 1.5rem;
+ text-align: left;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ user-select: all;
+}
+
+a {
+ color: #61dafb;
+}
+
+h1 {
+ font-size: 56px;
+ font-weight: 700;
+ letter-spacing: -1.12px;
+ margin: 2vh 0 0;
+ margin-bottom: 5vh;
+}
+
+h2 {
+ font-size: 32px;
+ font-weight: 700;
+ letter-spacing: -1.12px;
+ margin: 0;
+}
+
+header {
+ align-items: center;
+ border-bottom: 1px solid #3d3b3b;
+ color: white;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ min-height: 40vh;
+ padding-bottom: 5vh;
+}
+
+main {
+ margin-left: 2vw;
+ margin-right: 2vw;
+ text-align: left;
+}
+
+.cl-rootBox {
+ margin: auto;
+}
+
+.container {
+ color: white;
+ display: flex;
+ flex-direction: column;
+ gap: 10vh;
+ margin-inline: auto;
+ min-height: 100vh;
+ text-align: center;
+ width: 600px;
+}
+
+.content {
+ display: flex;
+ flex-direction: column;
+ gap: 18px;
+}
+
+button, a.button {
+ align-items: center;
+ appearance: none;
+ background: #FFFFFF;
+ border: 0 solid #d8dde8;
+ border-width: 0;
+ border-style: solid;
+ border-radius: 0.5rem;
+ border-color: #D8DDE8;
+ box-sizing: border-box;
+ box-shadow: 0px 1px 2px rgba(57, 67, 86, 0.08);
+ color: #6C47FF;
+ cursor: pointer;
+ display: inline-flex;
+ font-size: 14px;
+ font-weight: 700;
+ height: 3rem;
+ justify-content: center;
+ line-height: 1.2;
+ margin: 0;
+ min-width: 2.5rem;
+ min-height: 48px;
+ outline: 2px solid transparent;
+ outline-offset: 2px;
+ overflow: visible;
+ text-decoration: none;
+ text-transform: uppercase;
+ padding: 0;
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ padding-inline-start: 1rem;
+ padding-inline-end: 1rem;
+ position: relative;
+ user-select: none;
+ vertical-align: middle;
+ white-space: nowrap;
+ width: 225px;
+ word-wrap: break-word;
+ z-index: 100;
+}
+
+a.invert {
+ background: #6C47FF;
+ color: #FFFFFF;
+}
diff --git a/playground/chrome-extension/src/components/CurrentUser.tsx b/playground/chrome-extension/src/components/CurrentUser.tsx
new file mode 100644
index 00000000000..5b8d5ddb796
--- /dev/null
+++ b/playground/chrome-extension/src/components/CurrentUser.tsx
@@ -0,0 +1,36 @@
+import { useEffect, useState } from 'react';
+import { useAuth, useUser } from "@clerk/chrome-extension";
+
+export function CurrentUser() {
+ const [sessionToken, setSessionToken] = useState("");
+ const { isSignedIn, user } = useUser();
+ const { getToken, signOut } = useAuth();
+
+ useEffect(() => {
+ const scheduler = setInterval(async () => {
+ const token = await getToken();
+ setSessionToken(token as string);
+ }, 1000);
+
+ return () => clearInterval(scheduler);
+ }, []);
+
+ if (!isSignedIn) {
+ return null;
+ }
+
+ const email = user.primaryEmailAddress?.emailAddress;
+
+ return (
+
+
Hi, {email ? `${email}!` : ''}
+
+
+
Clerk Session Token:
+
{sessionToken}
+
+
+
+
+ );
+}
diff --git a/playground/chrome-extension/src/components/SharedApp.tsx b/playground/chrome-extension/src/components/SharedApp.tsx
new file mode 100644
index 00000000000..7b49b579938
--- /dev/null
+++ b/playground/chrome-extension/src/components/SharedApp.tsx
@@ -0,0 +1,78 @@
+import { ClerkProvider, type ChromeExtensionClerkProviderProps } from "@clerk/chrome-extension";
+import { Routes, Route, useNavigate } from "react-router-dom";
+import {
+ SignedIn,
+ SignedOut,
+ SignIn,
+ SignUp,
+} from "@clerk/chrome-extension";
+
+import "@/assets/styles/index.css"
+
+import { CurrentUser } from '@/components/CurrentUser';
+
+const publishableKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY || "";
+
+export type SharedAppProps = Pick & {
+ className?: string;
+}
+
+export function SharedApp({ className, ...rest }: SharedAppProps) {
+ const navigate = useNavigate();
+
+ return (
+ navigate(to)}
+ routerReplace={to => navigate(to, { replace: true })}
+ syncSessionWithTab
+ {...rest}
+ >
+
+
+
+
+
+ } />
+
+
+
+
+
+
+
+ >
+ }
+ />
+
+
+
+
+ );
+}
diff --git a/playground/chrome-extension/src/global.d.ts b/playground/chrome-extension/src/global.d.ts
new file mode 100644
index 00000000000..7ad3edd14db
--- /dev/null
+++ b/playground/chrome-extension/src/global.d.ts
@@ -0,0 +1,11 @@
+declare module '*.svg' {
+ import React = require('react');
+ export const ReactComponent: React.SFC>;
+ const src: string;
+ export default src;
+}
+
+declare module '*.json' {
+ const content: string;
+ export default content;
+}
diff --git a/playground/chrome-extension/src/pages/background/index.ts b/playground/chrome-extension/src/pages/background/index.ts
new file mode 100644
index 00000000000..def0db5c2cf
--- /dev/null
+++ b/playground/chrome-extension/src/pages/background/index.ts
@@ -0,0 +1 @@
+console.log('background script loaded');
diff --git a/playground/chrome-extension/src/pages/content/index.tsx b/playground/chrome-extension/src/pages/content/index.tsx
new file mode 100644
index 00000000000..de0869e4012
--- /dev/null
+++ b/playground/chrome-extension/src/pages/content/index.tsx
@@ -0,0 +1 @@
+console.log('content script loaded')
diff --git a/playground/chrome-extension/src/pages/devtools/index.html b/playground/chrome-extension/src/pages/devtools/index.html
new file mode 100644
index 00000000000..666bb682e32
--- /dev/null
+++ b/playground/chrome-extension/src/pages/devtools/index.html
@@ -0,0 +1,10 @@
+
+
+
+
+ Devtools
+
+
+
+
+
diff --git a/playground/chrome-extension/src/pages/devtools/index.ts b/playground/chrome-extension/src/pages/devtools/index.ts
new file mode 100644
index 00000000000..cf2258c594f
--- /dev/null
+++ b/playground/chrome-extension/src/pages/devtools/index.ts
@@ -0,0 +1,6 @@
+import browser from 'webextension-polyfill';
+
+browser
+ .devtools
+ .panels
+ .create('Clerk Starter', 'icon-32.png', 'src/pages/panel/index.html');
diff --git a/playground/chrome-extension/src/pages/new-tab/index.html b/playground/chrome-extension/src/pages/new-tab/index.html
new file mode 100644
index 00000000000..e794ed94acd
--- /dev/null
+++ b/playground/chrome-extension/src/pages/new-tab/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+ New tab
+
+
+
+
+
+
+
diff --git a/playground/chrome-extension/src/pages/new-tab/index.tsx b/playground/chrome-extension/src/pages/new-tab/index.tsx
new file mode 100644
index 00000000000..48b5565878b
--- /dev/null
+++ b/playground/chrome-extension/src/pages/new-tab/index.tsx
@@ -0,0 +1,11 @@
+import { createRoot } from 'react-dom/client';
+import NewTab from './new-tab';
+
+function init() {
+ const rootContainer = document.querySelector("#__root");
+ if (!rootContainer) throw new Error("Can't find NewTab root element");
+ const root = createRoot(rootContainer);
+ root.render();
+}
+
+init();
diff --git a/playground/chrome-extension/src/pages/new-tab/new-tab.tsx b/playground/chrome-extension/src/pages/new-tab/new-tab.tsx
new file mode 100644
index 00000000000..aeb1cd4b2dc
--- /dev/null
+++ b/playground/chrome-extension/src/pages/new-tab/new-tab.tsx
@@ -0,0 +1,12 @@
+import { MemoryRouter } from "react-router-dom";
+import { SharedApp } from '@/components/SharedApp';
+
+function NewTab() {
+ return (
+
+
+
+ )
+}
+
+export default NewTab;
diff --git a/playground/chrome-extension/src/pages/options/Options.tsx b/playground/chrome-extension/src/pages/options/Options.tsx
new file mode 100644
index 00000000000..f2b20d9fed4
--- /dev/null
+++ b/playground/chrome-extension/src/pages/options/Options.tsx
@@ -0,0 +1,12 @@
+import { MemoryRouter } from "react-router-dom";
+import { SharedApp } from '@/components/SharedApp';
+
+function Options() {
+ return (
+
+
+
+ )
+}
+
+export default Options;
diff --git a/playground/chrome-extension/src/pages/options/index.html b/playground/chrome-extension/src/pages/options/index.html
new file mode 100644
index 00000000000..db5fa745afe
--- /dev/null
+++ b/playground/chrome-extension/src/pages/options/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+ Options
+
+
+
+
+
+
+
diff --git a/playground/chrome-extension/src/pages/options/index.tsx b/playground/chrome-extension/src/pages/options/index.tsx
new file mode 100644
index 00000000000..48630999fb9
--- /dev/null
+++ b/playground/chrome-extension/src/pages/options/index.tsx
@@ -0,0 +1,11 @@
+import { createRoot } from 'react-dom/client';
+import Options from './options';
+
+function init() {
+ const rootContainer = document.querySelector("#__root");
+ if (!rootContainer) throw new Error("Can't find Options root element");
+ const root = createRoot(rootContainer);
+ root.render();
+}
+
+init();
diff --git a/playground/chrome-extension/src/pages/panel/Panel.tsx b/playground/chrome-extension/src/pages/panel/Panel.tsx
new file mode 100644
index 00000000000..a2ae4f3cb00
--- /dev/null
+++ b/playground/chrome-extension/src/pages/panel/Panel.tsx
@@ -0,0 +1,12 @@
+import { MemoryRouter } from "react-router-dom";
+import { SharedApp } from '@/components/SharedApp';
+
+function Panel() {
+ return (
+
+
+
+ )
+}
+
+export default Panel;
diff --git a/playground/chrome-extension/src/pages/panel/index.html b/playground/chrome-extension/src/pages/panel/index.html
new file mode 100644
index 00000000000..7aa306d0ca7
--- /dev/null
+++ b/playground/chrome-extension/src/pages/panel/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+ Devtools Panel
+
+
+
+
+
+
+
diff --git a/playground/chrome-extension/src/pages/panel/index.tsx b/playground/chrome-extension/src/pages/panel/index.tsx
new file mode 100644
index 00000000000..88ce49ce797
--- /dev/null
+++ b/playground/chrome-extension/src/pages/panel/index.tsx
@@ -0,0 +1,11 @@
+import { createRoot } from 'react-dom/client';
+import Panel from './panel';
+
+function init() {
+ const rootContainer = document.querySelector("#__root");
+ if (!rootContainer) throw new Error("Can't find Panel root element");
+ const root = createRoot(rootContainer);
+ root.render();
+}
+
+init();
diff --git a/playground/chrome-extension/src/pages/popup/Popup.tsx b/playground/chrome-extension/src/pages/popup/Popup.tsx
new file mode 100644
index 00000000000..f3fa7470612
--- /dev/null
+++ b/playground/chrome-extension/src/pages/popup/Popup.tsx
@@ -0,0 +1,12 @@
+import { MemoryRouter } from "react-router-dom";
+import { SharedApp } from '@/components/SharedApp';
+
+function Popup() {
+ return (
+
+
+
+ )
+}
+
+export default Popup;
diff --git a/playground/chrome-extension/src/pages/popup/index.html b/playground/chrome-extension/src/pages/popup/index.html
new file mode 100644
index 00000000000..b60f054fcee
--- /dev/null
+++ b/playground/chrome-extension/src/pages/popup/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+ Popup
+
+
+
+
+
+
+
diff --git a/playground/chrome-extension/src/pages/popup/index.tsx b/playground/chrome-extension/src/pages/popup/index.tsx
new file mode 100644
index 00000000000..695a35a37d8
--- /dev/null
+++ b/playground/chrome-extension/src/pages/popup/index.tsx
@@ -0,0 +1,11 @@
+import { createRoot } from 'react-dom/client';
+import Popup from './popup';
+
+function init() {
+ const rootContainer = document.querySelector("#__root");
+ if (!rootContainer) throw new Error("Can't find Popup root element");
+ const root = createRoot(rootContainer);
+ root.render();
+}
+
+init();
diff --git a/playground/chrome-extension/src/vite-env.d.ts b/playground/chrome-extension/src/vite-env.d.ts
new file mode 100644
index 00000000000..11f02fe2a00
--- /dev/null
+++ b/playground/chrome-extension/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/playground/chrome-extension/tsconfig.json b/playground/chrome-extension/tsconfig.json
new file mode 100644
index 00000000000..5dd31527680
--- /dev/null
+++ b/playground/chrome-extension/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "target": "esnext",
+ "types": ["vite/client", "node", "chrome"],
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"]
+ }
+ },
+ "include": ["src", "vite.config.ts"],
+}
diff --git a/playground/chrome-extension/vite.config.ts b/playground/chrome-extension/vite.config.ts
new file mode 100644
index 00000000000..0e40558bb78
--- /dev/null
+++ b/playground/chrome-extension/vite.config.ts
@@ -0,0 +1,50 @@
+import { fileURLToPath, URL } from 'url';
+import { resolve } from 'path';
+
+import react from '@vitejs/plugin-react-swc';
+import { crx, ManifestV3Export } from '@crxjs/vite-plugin';
+import { defineConfig } from 'vite';
+
+import manifest from './manifest.json';
+import devManifest from './manifest.dev.json';
+import pkg from './package.json';
+
+const isDev = process.env.__DEV__ === 'true';
+
+const extensionManifest = {
+ ...manifest,
+ ...(isDev ? devManifest : {} as ManifestV3Export),
+ name: isDev ? `[DEV] ${manifest.name}` : manifest.name,
+ version: pkg.version,
+};
+
+const crxPlugin = crx({
+ manifest: extensionManifest as ManifestV3Export,
+ contentScripts: {
+ injectCss: true,
+ }
+})
+
+export default defineConfig({
+ resolve: {
+ alias: [
+ { find: '@/', replacement: fileURLToPath(new URL('./src/', import.meta.url)) },
+ ],
+ },
+ plugins: [
+ react(),
+ crxPlugin,
+ ],
+ publicDir: resolve(__dirname, 'public'),
+ build: {
+ emptyOutDir: !isDev,
+ outDir: resolve(__dirname, 'dist'),
+ minify: !isDev,
+ sourcemap: isDev,
+ rollupOptions: {
+ input: {
+ panel: 'src/pages/panel/index.html',
+ },
+ },
+ },
+});
diff --git a/playground/vite-react-ts/.gitignore b/playground/vite-react-ts/.gitignore
index a547bf36d8d..d7de12f3812 100644
--- a/playground/vite-react-ts/.gitignore
+++ b/playground/vite-react-ts/.gitignore
@@ -12,6 +12,8 @@ dist
dist-ssr
*.local
+.env
+
# Editor directories and files
.vscode/*
!.vscode/extensions.json