diff --git a/README.md b/README.md
index 44375ab..9679552 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,8 @@ This package includes several entry points for building apps on Convex:
React component for authenticating users with Auth0.
- [`convex/react-clerk`](https://docs.convex.dev/api/modules/react_clerk): A
React component for authenticating users with Clerk.
+- [`convex/react-descope`](https://docs.convex.dev/api/modules/react_descope): A
+ React component for authenticating users with Descope.
This package also includes [`convex`](https://docs.convex.dev/using/cli), the
command-line interface for managing Convex projects.
diff --git a/package.json b/package.json
index ce01689..8fefffb 100644
--- a/package.json
+++ b/package.json
@@ -64,6 +64,16 @@
"import": "./dist/esm/react-clerk/index.js"
}
},
+ "./react-descope": {
+ "require": {
+ "types": "./dist/internal-cjs-types/react-descope/index.d.ts",
+ "require": "./dist/cjs/react-descope/index.js"
+ },
+ "import": {
+ "types": "./dist/internal-esm-types/react-descope/index.d.ts",
+ "import": "./dist/esm/react-descope/index.js"
+ }
+ },
"./nextjs": {
"require": {
"types": "./dist/internal-cjs-types/nextjs/index.d.ts",
@@ -119,6 +129,9 @@
"react-clerk": [
"./dist/internal-cjs-types/react-clerk/index.d.ts"
],
+ "react-descope": [
+ "./dist/internal-cjs-types/react-descope/index.d.ts"
+ ],
"nextjs": [
"./dist/internal-cjs-types/nextjs/index.d.ts"
],
@@ -168,6 +181,7 @@
"peerDependencies": {
"@auth0/auth0-react": "^2.0.1",
"@clerk/clerk-react": "^4.12.8",
+ "@descope/react-sdk": "^2.0.10",
"react": "^17.0.2 || ^18.0.0",
"react-dom": "^17.0.2 || ^18.0.0"
},
@@ -183,6 +197,9 @@
},
"@clerk/clerk-react": {
"optional": true
+ },
+ "@descope/react-sdk": {
+ "optional": true
}
},
"@comment devDependencies": [
@@ -193,6 +210,7 @@
"@auth0/auth0-react": "2.0.1",
"@babel/parser": "^7.21.3",
"@clerk/clerk-react": "4.18.0",
+ "@descope/react-sdk": "2.0.10",
"@commander-js/extra-typings": "^11.1.0",
"@jest/globals": "^28.1.0",
"@microsoft/api-extractor": "~7.36.4",
diff --git a/react-descope/package.json b/react-descope/package.json
new file mode 100644
index 0000000..de30aa5
--- /dev/null
+++ b/react-descope/package.json
@@ -0,0 +1,6 @@
+{
+ "main": "../dist/cjs/react-descope/index.js",
+ "module": "../dist/esm/react-descope/index.js",
+ "types": "../dist/internal-cjs-types/react-descope/index.d.ts"
+ }
+
\ No newline at end of file
diff --git a/src/react-descope/ConvexProviderWithDescope.test.tsx b/src/react-descope/ConvexProviderWithDescope.test.tsx
new file mode 100644
index 0000000..4adc698
--- /dev/null
+++ b/src/react-descope/ConvexProviderWithDescope.test.tsx
@@ -0,0 +1,17 @@
+/**
+ * @jest-environment jsdom
+ */
+import { test } from "@jest/globals";
+import React from "react";
+import { ConvexProviderWithDescope } from "./ConvexProviderWithDescope.js";
+import { ConvexReactClient } from "../react/index.js";
+
+test("Helpers are valid children", () => {
+ const convex = new ConvexReactClient("https://localhost:3001");
+
+ const _ = (
+
+ Hello world
+
+ );
+});
diff --git a/src/react-descope/ConvexProviderWithDescope.tsx b/src/react-descope/ConvexProviderWithDescope.tsx
new file mode 100644
index 0000000..3cee0cc
--- /dev/null
+++ b/src/react-descope/ConvexProviderWithDescope.tsx
@@ -0,0 +1,61 @@
+import { useSession, useDescope, getSessionToken } from "@descope/react-sdk";
+import React from "react";
+
+import { ReactNode, useCallback, useMemo } from "react";
+import { AuthTokenFetcher } from "../browser/sync/client.js";
+import { ConvexProviderWithAuth } from "../react/ConvexAuthState.js";
+
+// Until we can import from our own entry points (requires TypeScript 4.7),
+// just describe the interface enough to help users pass the right type.
+type IConvexReactClient = {
+ setAuth(fetchToken: AuthTokenFetcher): void;
+ clearAuth(): void;
+};
+
+/**
+ * A wrapper React component which provides a {@link react.ConvexReactClient}
+ * authenticated with Descope.
+ *
+ * It must be wrapped by a configured `AuthProvider` from `@descope/react-sdk`.
+ *
+ * See [Convex Descope](https://docs.convex.dev/auth/descope) on how to set up
+ * Convex with Descope.
+ *
+ * @public
+ */
+export function ConvexProviderWithDescope({
+ children,
+ client,
+}: {
+ children: ReactNode;
+ client: IConvexReactClient;
+}) {
+ return (
+
+ {children}
+
+ );
+}
+
+function useAuthFromDescope() {
+ const { isLoading, isAuthenticated } = useSession();
+ const sdk = useDescope();
+
+ const fetchAccessToken = useCallback(async ({ forceRefreshToken }: { forceRefreshToken: boolean }) => {
+ try {
+ if (forceRefreshToken) {
+ sdk.refresh();
+ }
+
+ const token = getSessionToken();
+ return token as string;
+ } catch (error) {
+ return null;
+ }
+ }, []);
+
+ return useMemo(
+ () => ({ isLoading, isAuthenticated, fetchAccessToken }),
+ [isLoading, isAuthenticated, fetchAccessToken],
+ );
+}
diff --git a/src/react-descope/index.ts b/src/react-descope/index.ts
new file mode 100644
index 0000000..d101f5e
--- /dev/null
+++ b/src/react-descope/index.ts
@@ -0,0 +1,6 @@
+/**
+ * React login component for use with Descope.
+ *
+ * @module
+ */
+export { ConvexProviderWithDescope } from "./ConvexProviderWithDescope.js";
diff --git a/src/react/ConvexAuthState.tsx b/src/react/ConvexAuthState.tsx
index a3c95ea..5c5329c 100644
--- a/src/react/ConvexAuthState.tsx
+++ b/src/react/ConvexAuthState.tsx
@@ -48,7 +48,7 @@ export function useConvexAuth(): {
if (authContext === undefined) {
throw new Error(
"Could not find `ConvexProviderWithAuth` (or `ConvexProviderWithClerk` " +
- "or `ConvexProviderWithAuth0`) " +
+ "or `ConvexProviderWithAuth0`" + "or `ConvexProviderWithDescope`) " +
"as an ancestor component. This component may be missing, or you " +
"might have two instances of the `convex/react` module loaded in your " +
"project.",