Compare commits

...

2 Commits

Author SHA1 Message Date
Giordano Ricci
5077282da6 cleanup 2025-02-17 18:53:14 +00:00
Giordano Ricci
2199d84bea fix: fixes clerk auth flow 2025-02-17 18:53:00 +00:00
10 changed files with 75 additions and 12 deletions

View File

@@ -23,8 +23,25 @@ export class JazzClerkAuth {
private authSecretStorage: AuthSecretStorage,
) {}
/**
* Loads the Jazz auth data from the Clerk user and sets it in the auth secret storage.
*/
private loadClerkAuthData = (credentials: ClerkCredentials) => {
return this.authSecretStorage.set({
accountID: credentials.jazzAccountID,
accountSecret: credentials.jazzAccountSecret,
secretSeed: credentials.jazzAccountSeed
? Uint8Array.from(credentials.jazzAccountSeed)
: undefined,
provider: "clerk",
});
};
onClerkUserChange = async (clerkClient: Pick<MinimalClerkClient, "user">) => {
if (!clerkClient.user) return;
if (!clerkClient.user) {
await this.authSecretStorage.clear();
return;
}
const isAuthenticated = this.authSecretStorage.isAuthenticated;
@@ -76,13 +93,15 @@ export class JazzClerkAuth {
throw new Error("No credentials found");
}
const jazzAccountSeed = credentials.secretSeed
? Array.from(credentials.secretSeed)
: undefined;
await clerkClient.user?.update({
unsafeMetadata: {
jazzAccountID: credentials.accountID,
jazzAccountSecret: credentials.accountSecret,
jazzAccountSeed: credentials.secretSeed
? Array.from(credentials.secretSeed)
: undefined,
jazzAccountSeed,
} satisfies ClerkCredentials,
});
@@ -96,11 +115,10 @@ export class JazzClerkAuth {
currentAccount.profile.name = username;
}
await this.authSecretStorage.set({
accountID: credentials.accountID,
accountSecret: credentials.accountSecret,
secretSeed: credentials.secretSeed,
provider: "clerk",
await this.loadClerkAuthData({
jazzAccountID: credentials.accountID,
jazzAccountSecret: credentials.accountSecret,
jazzAccountSeed,
});
};
}

View File

@@ -26,6 +26,7 @@ export type JazzContextManagerProps<Acc extends Account> = {
export class JazzBrowserContextManager<
Acc extends Account,
> extends JazzContextManager<Acc, JazzContextManagerProps<Acc>> {
// TODO: When the storage changes, if the user is changed, update the context
getKvStore() {
if (typeof window === "undefined") {
// To handle running in SSR
@@ -39,6 +40,9 @@ export class JazzBrowserContextManager<
props: JazzContextManagerProps<Acc>,
authProps?: JazzContextManagerAuthProps,
) {
const { promise, resolve } = promiseWithResolvers<void>();
this.prevContextCreation = promise;
let currentContext;
// We need to store the props here to block the double effect execution
@@ -64,6 +68,7 @@ export class JazzBrowserContextManager<
}
this.updateContext(props, currentContext);
resolve();
}
propsChanged(props: JazzContextManagerProps<Acc>) {
@@ -78,3 +83,19 @@ export class JazzBrowserContextManager<
);
}
}
function promiseWithResolvers<R>() {
let resolve = (_: R) => {};
let reject = (_: unknown) => {};
const promise = new Promise<R>((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
return {
promise,
resolve,
reject,
};
}

View File

@@ -15,6 +15,8 @@ setupInspector();
export * from "./createBrowserContext.js";
export * from "./BrowserContextManager.js";
export { LocalStorageKVStore } from "./auth/LocalStorageKVStore.js";
/** @category Invite Links */
export function createInviteLink<C extends CoValue>(
value: C,

View File

@@ -6,6 +6,7 @@
"types": "src/index.tsx",
"license": "MIT",
"dependencies": {
"jazz-browser": "workspace:*",
"cojson": "workspace:*",
"jazz-auth-clerk": "workspace:*",
"jazz-react": "workspace:*",

View File

@@ -1,10 +1,12 @@
import { JazzClerkAuth, type MinimalClerkClient } from "jazz-auth-clerk";
import { LocalStorageKVStore } from "jazz-browser";
import {
JazzProvider,
JazzProviderProps,
useAuthSecretStorage,
useJazzContext,
} from "jazz-react";
import { InMemoryKVStore, KvStoreContext } from "jazz-tools";
import { useEffect, useMemo } from "react";
function useJazzClerkAuth(clerk: MinimalClerkClient) {
@@ -39,6 +41,8 @@ function RegisterClerkAuth(props: {
export const JazzProviderWithClerk = (
props: { clerk: MinimalClerkClient } & JazzProviderProps,
) => {
setupKvStore();
return (
<JazzProvider {...props} onLogOut={props.clerk.signOut}>
<RegisterClerkAuth clerk={props.clerk}>
@@ -47,3 +51,11 @@ export const JazzProviderWithClerk = (
</JazzProvider>
);
};
function setupKvStore() {
KvStoreContext.getInstance().initialize(
typeof window === "undefined"
? new InMemoryKVStore()
: new LocalStorageKVStore(),
);
}

View File

@@ -1,6 +1,6 @@
// @vitest-environment happy-dom
import { act, render, waitFor } from "@testing-library/react";
import { act, render } from "@testing-library/react";
import type { MinimalClerkClient } from "jazz-auth-clerk";
import { AuthSecretStorage, InMemoryKVStore, KvStoreContext } from "jazz-tools";
import { beforeEach, describe, expect, it, vi } from "vitest";

View File

@@ -57,6 +57,7 @@ export function useCoState<V extends CoValue, D>(
>(
React.useCallback(
(callback) => {
observable.reset();
if (!id) return () => {};
const agent = "me" in context ? context.me : context.guest;

View File

@@ -347,6 +347,9 @@ export function createCoValueObservable<V extends CoValue, Depth>(options?: {
const observable = {
getCurrentValue: () => currentValue,
reset() {
currentValue = undefined;
},
subscribe,
};

View File

@@ -14,7 +14,7 @@ export type JazzContextManagerAuthProps = {
export type JazzContextManagerBaseProps<Acc extends Account> = {
onAnonymousAccountDiscarded?: (anonymousAccount: Acc) => Promise<void>;
onLogOut?: () => void;
onLogOut?: () => void | Promise<void>;
};
type PlatformSpecificAuthContext<Acc extends Account> = {
@@ -44,6 +44,7 @@ export class JazzContextManager<
protected props: P | undefined;
protected authSecretStorage = new AuthSecretStorage();
protected authenticating = false;
protected prevContextCreation?: Promise<void>;
constructor() {
KvStoreContext.getInstance().initialize(this.getKvStore());
@@ -96,8 +97,8 @@ export class JazzContextManager<
return;
}
await this.props.onLogOut?.();
await this.context.logOut();
this.props.onLogOut?.();
return this.createContext(this.props);
};
@@ -110,6 +111,7 @@ export class JazzContextManager<
};
authenticate = async (credentials: AuthCredentials) => {
await this.prevContextCreation;
if (!this.props) {
throw new Error("Props required");
}

3
pnpm-lock.yaml generated
View File

@@ -1744,6 +1744,9 @@ importers:
jazz-auth-clerk:
specifier: workspace:*
version: link:../jazz-auth-clerk
jazz-browser:
specifier: workspace:*
version: link:../jazz-browser
jazz-react:
specifier: workspace:*
version: link:../jazz-react