jazz-tools 2.0.0-alpha.34 → 2.0.0-alpha.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/docs-index.db +0 -0
- package/bin/docs-index.txt +111 -59
- package/bin/native/jazz-tools-darwin-arm64 +0 -0
- package/bin/native/jazz-tools-darwin-x64 +0 -0
- package/bin/native/jazz-tools-linux-arm64 +0 -0
- package/bin/native/jazz-tools-linux-x64 +0 -0
- package/dist/backend/create-jazz-context.d.ts +4 -0
- package/dist/backend/create-jazz-context.d.ts.map +1 -1
- package/dist/backend/create-jazz-context.js +6 -5
- package/dist/backend/create-jazz-context.js.map +1 -1
- package/dist/backend/create-jazz-context.test.js +49 -3
- package/dist/backend/create-jazz-context.test.js.map +1 -1
- package/dist/backend/index.d.ts +1 -1
- package/dist/backend/index.d.ts.map +1 -1
- package/dist/backend/index.js.map +1 -1
- package/dist/backend/request-auth.d.ts +2 -0
- package/dist/backend/request-auth.d.ts.map +1 -1
- package/dist/backend/request-auth.js +96 -15
- package/dist/backend/request-auth.js.map +1 -1
- package/dist/backend/request-auth.test.js +34 -6
- package/dist/backend/request-auth.test.js.map +1 -1
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +83 -39
- package/dist/cli.js.map +1 -1
- package/dist/cli.test.js +199 -76
- package/dist/cli.test.js.map +1 -1
- package/dist/codegen/schema-reader.d.ts.map +1 -1
- package/dist/codegen/schema-reader.js +14 -0
- package/dist/codegen/schema-reader.js.map +1 -1
- package/dist/dev/dev-server.d.ts.map +1 -1
- package/dist/dev/dev-server.js +3 -0
- package/dist/dev/dev-server.js.map +1 -1
- package/dist/dev/expo.test.js +1 -1
- package/dist/dev/expo.test.js.map +1 -1
- package/dist/dev/inspector-link.d.ts +2 -0
- package/dist/dev/inspector-link.d.ts.map +1 -0
- package/dist/dev/inspector-link.js +10 -0
- package/dist/dev/inspector-link.js.map +1 -0
- package/dist/dev/managed-runtime.d.ts.map +1 -1
- package/dist/dev/managed-runtime.js +10 -1
- package/dist/dev/managed-runtime.js.map +1 -1
- package/dist/dev/next.d.ts.map +1 -1
- package/dist/dev/next.js +6 -0
- package/dist/dev/next.js.map +1 -1
- package/dist/dev/next.test.js +3 -1
- package/dist/dev/next.test.js.map +1 -1
- package/dist/dev/sveltekit.d.ts +9 -0
- package/dist/dev/sveltekit.d.ts.map +1 -1
- package/dist/dev/sveltekit.js +6 -0
- package/dist/dev/sveltekit.js.map +1 -1
- package/dist/dev/sveltekit.test.js +15 -1
- package/dist/dev/sveltekit.test.js.map +1 -1
- package/dist/dev/vite.d.ts.map +1 -1
- package/dist/dev/vite.js +2 -0
- package/dist/dev/vite.js.map +1 -1
- package/dist/dev/vite.test.js +4 -2
- package/dist/dev/vite.test.js.map +1 -1
- package/dist/dev-tools/extension-panel.d.ts.map +1 -1
- package/dist/dev-tools/extension-panel.js.map +1 -1
- package/dist/dev-tools/protocol.d.ts.map +1 -1
- package/dist/dev-tools/protocol.js +0 -1
- package/dist/dev-tools/protocol.js.map +1 -1
- package/dist/drivers/types.d.ts +2 -0
- package/dist/drivers/types.d.ts.map +1 -1
- package/dist/dsl.d.ts +8 -1
- package/dist/dsl.d.ts.map +1 -1
- package/dist/dsl.js +54 -0
- package/dist/dsl.js.map +1 -1
- package/dist/dsl.test.js +73 -0
- package/dist/dsl.test.js.map +1 -1
- package/dist/expo/auth-secret-store.d.ts +2 -1
- package/dist/expo/auth-secret-store.d.ts.map +1 -1
- package/dist/expo/auth-secret-store.js +1 -0
- package/dist/expo/auth-secret-store.js.map +1 -1
- package/dist/expo/index.d.ts +3 -0
- package/dist/expo/index.d.ts.map +1 -0
- package/dist/expo/index.js +3 -0
- package/dist/expo/index.js.map +1 -0
- package/dist/expo/use-local-first-auth.d.ts +3 -0
- package/dist/expo/use-local-first-auth.d.ts.map +1 -0
- package/dist/expo/use-local-first-auth.js +4 -0
- package/dist/expo/use-local-first-auth.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/build-index.test.js +1 -1
- package/dist/mcp/build-index.test.js.map +1 -1
- package/dist/permissions/index.d.ts +3 -0
- package/dist/permissions/index.d.ts.map +1 -1
- package/dist/permissions/index.js +2 -1
- package/dist/permissions/index.js.map +1 -1
- package/dist/permissions/type-inference.test.js +23 -2
- package/dist/permissions/type-inference.test.js.map +1 -1
- package/dist/react/create-jazz-client.integration.test.js +5 -2
- package/dist/react/create-jazz-client.integration.test.js.map +1 -1
- package/dist/react/create-jazz-client.test.js +6 -1
- package/dist/react/create-jazz-client.test.js.map +1 -1
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +2 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/provider.d.ts +2 -1
- package/dist/react/provider.d.ts.map +1 -1
- package/dist/react/provider.js +2 -2
- package/dist/react/provider.js.map +1 -1
- package/dist/react/use-local-first-auth.d.ts +3 -0
- package/dist/react/use-local-first-auth.d.ts.map +1 -0
- package/dist/react/use-local-first-auth.js +4 -0
- package/dist/react/use-local-first-auth.js.map +1 -0
- package/dist/react-core/index.d.ts +2 -0
- package/dist/react-core/index.d.ts.map +1 -1
- package/dist/react-core/index.js +2 -0
- package/dist/react-core/index.js.map +1 -1
- package/dist/react-core/provider.d.ts +7 -3
- package/dist/react-core/provider.d.ts.map +1 -1
- package/dist/react-core/provider.js +67 -51
- package/dist/react-core/provider.js.map +1 -1
- package/dist/react-core/provider.onjwtexpired.test.d.ts +2 -0
- package/dist/react-core/provider.onjwtexpired.test.d.ts.map +1 -0
- package/dist/react-core/provider.onjwtexpired.test.js +47 -0
- package/dist/react-core/provider.onjwtexpired.test.js.map +1 -0
- package/dist/react-core/test-utils.d.ts +20 -0
- package/dist/react-core/test-utils.d.ts.map +1 -0
- package/dist/react-core/test-utils.js +34 -0
- package/dist/react-core/test-utils.js.map +1 -0
- package/dist/react-core/use-auth-state.d.ts +10 -0
- package/dist/react-core/use-auth-state.d.ts.map +1 -0
- package/dist/react-core/use-auth-state.js +14 -0
- package/dist/react-core/use-auth-state.js.map +1 -0
- package/dist/react-core/use-auth-state.test.d.ts +2 -0
- package/dist/react-core/use-auth-state.test.d.ts.map +1 -0
- package/dist/react-core/use-auth-state.test.js +42 -0
- package/dist/react-core/use-auth-state.test.js.map +1 -0
- package/dist/react-core/use-local-first-auth.d.ts +9 -0
- package/dist/react-core/use-local-first-auth.d.ts.map +1 -0
- package/dist/react-core/use-local-first-auth.js +54 -0
- package/dist/react-core/use-local-first-auth.js.map +1 -0
- package/dist/react-core/use-local-first-auth.test.d.ts +2 -0
- package/dist/react-core/use-local-first-auth.test.d.ts.map +1 -0
- package/dist/react-core/use-local-first-auth.test.js +78 -0
- package/dist/react-core/use-local-first-auth.test.js.map +1 -0
- package/dist/react-native/create-jazz-client.test.js +4 -2
- package/dist/react-native/create-jazz-client.test.js.map +1 -1
- package/dist/react-native/create-jazz-rn-runtime.d.ts.map +1 -1
- package/dist/react-native/create-jazz-rn-runtime.js.map +1 -1
- package/dist/react-native/db.d.ts.map +1 -1
- package/dist/react-native/db.js +3 -4
- package/dist/react-native/db.js.map +1 -1
- package/dist/react-native/db.test.js +3 -6
- package/dist/react-native/db.test.js.map +1 -1
- package/dist/react-native/jazz-rn-runtime-adapter.d.ts +11 -11
- package/dist/react-native/jazz-rn-runtime-adapter.d.ts.map +1 -1
- package/dist/react-native/jazz-rn-runtime-adapter.js +8 -35
- package/dist/react-native/jazz-rn-runtime-adapter.js.map +1 -1
- package/dist/react-native/jazz-rn-runtime-adapter.test.js +10 -27
- package/dist/react-native/jazz-rn-runtime-adapter.test.js.map +1 -1
- package/dist/runtime/anonymous-write-denied-error.d.ts +13 -0
- package/dist/runtime/anonymous-write-denied-error.d.ts.map +1 -0
- package/dist/runtime/anonymous-write-denied-error.js +58 -0
- package/dist/runtime/anonymous-write-denied-error.js.map +1 -0
- package/dist/runtime/anonymous-write-denied-error.test.d.ts +2 -0
- package/dist/runtime/anonymous-write-denied-error.test.d.ts.map +1 -0
- package/dist/runtime/anonymous-write-denied-error.test.js +56 -0
- package/dist/runtime/anonymous-write-denied-error.test.js.map +1 -0
- package/dist/runtime/auth-secret-store.d.ts +2 -1
- package/dist/runtime/auth-secret-store.d.ts.map +1 -1
- package/dist/runtime/auth-secret-store.js +1 -0
- package/dist/runtime/auth-secret-store.js.map +1 -1
- package/dist/runtime/auth-state.d.ts +5 -14
- package/dist/runtime/auth-state.d.ts.map +1 -1
- package/dist/runtime/auth-state.js +39 -35
- package/dist/runtime/auth-state.js.map +1 -1
- package/dist/runtime/auth-state.test.js +36 -5
- package/dist/runtime/auth-state.test.js.map +1 -1
- package/dist/runtime/client-session.d.ts +1 -1
- package/dist/runtime/client-session.d.ts.map +1 -1
- package/dist/runtime/client-session.js +15 -9
- package/dist/runtime/client-session.js.map +1 -1
- package/dist/runtime/client-session.test.js +31 -9
- package/dist/runtime/client-session.test.js.map +1 -1
- package/dist/runtime/client.d.ts +66 -29
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.for-request.test.js +122 -20
- package/dist/runtime/client.for-request.test.js.map +1 -1
- package/dist/runtime/client.js +186 -56
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/client.mutations.test.js +50 -10
- package/dist/runtime/client.mutations.test.js.map +1 -1
- package/dist/runtime/client.test.js +2 -0
- package/dist/runtime/client.test.js.map +1 -1
- package/dist/runtime/context.d.ts +7 -3
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/db.anonymous.test.d.ts +2 -0
- package/dist/runtime/db.anonymous.test.d.ts.map +1 -0
- package/dist/runtime/db.anonymous.test.js +28 -0
- package/dist/runtime/db.anonymous.test.js.map +1 -0
- package/dist/runtime/db.auth-state.test.js +22 -25
- package/dist/runtime/db.auth-state.test.js.map +1 -1
- package/dist/runtime/db.browser-storage-scope.test.js +2 -5
- package/dist/runtime/db.browser-storage-scope.test.js.map +1 -1
- package/dist/runtime/db.d.ts +62 -19
- package/dist/runtime/db.d.ts.map +1 -1
- package/dist/runtime/db.js +173 -36
- package/dist/runtime/db.js.map +1 -1
- package/dist/runtime/db.local-first-auth.test.js +13 -3
- package/dist/runtime/db.local-first-auth.test.js.map +1 -1
- package/dist/runtime/db.persisted.test.js +1 -1
- package/dist/runtime/db.persisted.test.js.map +1 -1
- package/dist/runtime/db.schema-order.test.js +26 -9
- package/dist/runtime/db.schema-order.test.js.map +1 -1
- package/dist/runtime/db.transaction.test.js +106 -4
- package/dist/runtime/db.transaction.test.js.map +1 -1
- package/dist/runtime/db.transport.test.js +4 -5
- package/dist/runtime/db.transport.test.js.map +1 -1
- package/dist/runtime/file-storage.d.ts +2 -2
- package/dist/runtime/file-storage.d.ts.map +1 -1
- package/dist/runtime/file-storage.js +1 -1
- package/dist/runtime/file-storage.js.map +1 -1
- package/dist/runtime/file-storage.test.js +2 -1
- package/dist/runtime/file-storage.test.js.map +1 -1
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/introspection-fetch.d.ts +0 -1
- package/dist/runtime/introspection-fetch.d.ts.map +1 -1
- package/dist/runtime/introspection-fetch.js +2 -2
- package/dist/runtime/introspection-fetch.js.map +1 -1
- package/dist/runtime/napi.auth-failure.test.js +1 -1
- package/dist/runtime/napi.auth-failure.test.js.map +1 -1
- package/dist/runtime/napi.for-request.test.js +13 -3
- package/dist/runtime/napi.for-request.test.js.map +1 -1
- package/dist/runtime/napi.integration.test.js +5 -2
- package/dist/runtime/napi.integration.test.js.map +1 -1
- package/dist/runtime/permissions.repro.test.js +264 -5
- package/dist/runtime/permissions.repro.test.js.map +1 -1
- package/dist/runtime/recovery-phrase.integration.test.js +2 -2
- package/dist/runtime/recovery-phrase.integration.test.js.map +1 -1
- package/dist/runtime/runtime-config.d.ts.map +1 -1
- package/dist/runtime/runtime-config.js +8 -1
- package/dist/runtime/runtime-config.js.map +1 -1
- package/dist/runtime/schema-fetch.d.ts +9 -8
- package/dist/runtime/schema-fetch.d.ts.map +1 -1
- package/dist/runtime/schema-fetch.js +9 -10
- package/dist/runtime/schema-fetch.js.map +1 -1
- package/dist/runtime/schema-fetch.test.js +11 -8
- package/dist/runtime/schema-fetch.test.js.map +1 -1
- package/dist/runtime/sync-transport.d.ts +0 -8
- package/dist/runtime/sync-transport.d.ts.map +1 -1
- package/dist/runtime/sync-transport.js +0 -19
- package/dist/runtime/sync-transport.js.map +1 -1
- package/dist/runtime/url.d.ts +13 -6
- package/dist/runtime/url.d.ts.map +1 -1
- package/dist/runtime/url.js +51 -22
- package/dist/runtime/url.js.map +1 -1
- package/dist/runtime/url.test.js +20 -16
- package/dist/runtime/url.test.js.map +1 -1
- package/dist/runtime/worker-bridge.d.ts +0 -1
- package/dist/runtime/worker-bridge.d.ts.map +1 -1
- package/dist/runtime/worker-bridge.js +0 -1
- package/dist/runtime/worker-bridge.js.map +1 -1
- package/dist/runtime/worker-bridge.test.js +7 -3
- package/dist/runtime/worker-bridge.test.js.map +1 -1
- package/dist/schema.d.ts +3 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js.map +1 -1
- package/dist/subscriptions-orchestrator.integration.test.js +4 -4
- package/dist/subscriptions-orchestrator.integration.test.js.map +1 -1
- package/dist/subscriptions-orchestrator.test.js +4 -0
- package/dist/subscriptions-orchestrator.test.js.map +1 -1
- package/dist/svelte/create-jazz-client.test.js +2 -1
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -0
- package/dist/testing/index.test.js +5 -5
- package/dist/testing/index.test.js.map +1 -1
- package/dist/vue/create-jazz-client.integration.test.js +4 -1
- package/dist/vue/create-jazz-client.integration.test.js.map +1 -1
- package/dist/vue/create-jazz-client.test.js +2 -1
- package/dist/vue/create-jazz-client.test.js.map +1 -1
- package/dist/worker/jazz-worker.d.ts +1 -2
- package/dist/worker/jazz-worker.d.ts.map +1 -1
- package/dist/worker/jazz-worker.js +3 -4
- package/dist/worker/jazz-worker.js.map +1 -1
- package/dist/worker/jazz-worker.test.d.ts +1 -1
- package/dist/worker/jazz-worker.test.js +3 -3
- package/dist/worker/jazz-worker.test.js.map +1 -1
- package/dist/worker/jazz-worker.ts +3 -4
- package/dist/worker/worker-protocol.d.ts +0 -1
- package/dist/worker/worker-protocol.d.ts.map +1 -1
- package/package.json +13 -11
package/bin/docs-index.db
CHANGED
|
Binary file
|
package/bin/docs-index.txt
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
TITLE:Authentication
|
|
3
3
|
DESCRIPTION:"How and when to use Jazz's auth modes, and how to manage JWT auth over the lifetime of a live client."
|
|
4
4
|
|
|
5
|
-
Jazz has
|
|
5
|
+
Jazz has three auth modes: `anonymous`, `local-first`, and `external`. You pick the mode implicitly by what you pass in `DbConfig`: nothing for anonymous, `secret` for local-first, or `jwtToken` for external.
|
|
6
6
|
|
|
7
7
|
| Mode | What it does | When to use it |
|
|
8
8
|
| ------------- | ------------------------------------------------------ | ------------------------------------------------ |
|
|
9
|
+
| `anonymous` | Ephemeral read-only identity, no secret, no server | Public/marketing surfaces, try-before-anything |
|
|
9
10
|
| `local-first` | Stable identity from a device secret, no server needed | Production offline-first apps, try-before-signup |
|
|
10
11
|
| `external` | Validates a JWT from your auth provider via JWKS | Production apps with real user accounts |
|
|
11
12
|
|
|
13
|
+
Anonymous sessions can subscribe to queries but are **structurally denied writes** — every insert, update, and delete path checks the session's auth mode before any policy evaluation runs, and surfaces an `AnonymousWriteDeniedError` to the client. Gate reads the same way you gate any other access, via the permissions DSL (`session.where({ authMode: "anonymous" })`).
|
|
14
|
+
|
|
12
15
|
## Local-first auth
|
|
13
16
|
|
|
14
17
|
Local-first auth lets users start using your app without signing up. They can later upgrade to an external provider while keeping their identity. See [Local-first auth](/docs/auth/local-first-auth) for the full guide.
|
|
@@ -89,7 +92,9 @@ For sign-in and sign-out, recreate `JazzProvider` or `Db` with a new auth config
|
|
|
89
92
|
|
|
90
93
|
```tsx
|
|
91
94
|
const [jwtToken, setJwtToken] = useState<string | null>(readStoredJwt());
|
|
92
|
-
const secret =
|
|
95
|
+
const { secret, isLoading } = useLocalFirstAuth();
|
|
96
|
+
|
|
97
|
+
if (!jwtToken && (isLoading || !secret)) return <p>Loading…</p>;
|
|
93
98
|
|
|
94
99
|
const config = jwtToken
|
|
95
100
|
? {
|
|
@@ -100,7 +105,7 @@ const config = jwtToken
|
|
|
100
105
|
: {
|
|
101
106
|
appId: "my-app",
|
|
102
107
|
serverUrl: "https://sync.example.com",
|
|
103
|
-
|
|
108
|
+
secret: secret!,
|
|
104
109
|
};
|
|
105
110
|
|
|
106
111
|
;
|
|
@@ -120,14 +125,42 @@ recommended path for login, logout, and any principal change.
|
|
|
120
125
|
|
|
121
126
|
### Reacting to expiry and unauthenticated responses
|
|
122
127
|
|
|
123
|
-
|
|
128
|
+
Auth state is flat:
|
|
124
129
|
|
|
125
130
|
```ts
|
|
126
|
-
|
|
127
|
-
|
|
131
|
+
interface AuthState {
|
|
132
|
+
authMode: "anonymous" | "local-first" | "external";
|
|
133
|
+
session: Session | null;
|
|
134
|
+
error?: "expired" | "missing" | "invalid" | "disabled";
|
|
135
|
+
}
|
|
136
|
+
```
|
|
128
137
|
|
|
129
|
-
|
|
130
|
-
|
|
138
|
+
Presence of `error` indicates trouble; `authMode` and the last-known `session` are always available.
|
|
139
|
+
|
|
140
|
+
#### React: `onJWTExpired` prop
|
|
141
|
+
|
|
142
|
+
For JWT refresh, pass `onJWTExpired` to `JazzProvider`. Jazz calls it when the sync server rejects
|
|
143
|
+
the current token as expired, serialized so concurrent failed writes trigger a single refresh:
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
await fetchFreshJwt()}
|
|
147
|
+
>
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Read auth state from any component with `useAuthState()`:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
const { authMode, userId, claims, error } = useAuthState();
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### TypeScript: `db.onAuthChanged`
|
|
158
|
+
|
|
159
|
+
Outside React, subscribe to auth-state changes on `Db`:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
const stop = db.onAuthChanged(async (state) => {
|
|
163
|
+
if (state.error !== "expired") return;
|
|
131
164
|
|
|
132
165
|
const freshJwt = await getFreshJwtForCurrentUser().catch(() => null);
|
|
133
166
|
if (freshJwt) {
|
|
@@ -140,22 +173,19 @@ const stop = db.onAuthChanged(async (state) => {
|
|
|
140
173
|
});
|
|
141
174
|
```
|
|
142
175
|
|
|
143
|
-
|
|
176
|
+
Read the current snapshot synchronously with `db.getAuthState()`.
|
|
144
177
|
|
|
145
178
|
When Jazz receives a structured unauthenticated response from the sync server, it:
|
|
146
179
|
|
|
147
|
-
-
|
|
180
|
+
- sets `error` on the auth state
|
|
148
181
|
- preserves the last known `session`
|
|
149
182
|
- pauses authenticated sync and reconnects until the app either refreshes the same user's JWT with
|
|
150
183
|
`db.updateAuthToken(...)` or recreates the client with a new auth config
|
|
151
184
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
auth state is unauthenticated. That's intentional: it keeps user-scoped UI and local authorship
|
|
157
|
-
stable while your app refreshes or replaces the JWT. For signed-in vs signed-out UI, check
|
|
158
|
-
`db.getAuthState().status` instead of only checking whether a session exists.
|
|
185
|
+
`useSession()` and `db.getAuthState().session` can still return the last known session while
|
|
186
|
+
`error` is set. That's intentional: it keeps user-scoped UI and local authorship stable while your
|
|
187
|
+
app refreshes or replaces the JWT. For signed-in vs signed-out UI, check whether `error` is set
|
|
188
|
+
(or the `authMode` you expect) instead of only whether a session exists.
|
|
159
189
|
|
|
160
190
|
### Server configuration
|
|
161
191
|
|
|
@@ -186,7 +216,7 @@ When the server receives a request, it tries each auth method in priority order
|
|
|
186
216
|
|
|
187
217
|
Backend impersonation always takes precedence, so a backend service can reliably impersonate users even if the request also carries a JWT.
|
|
188
218
|
|
|
189
|
-
On TypeScript backends, `await createJazzContext(...).forRequest(req)` follows the same rule set. Configure `jwksUrl` on the backend context to verify external JWTs there too.
|
|
219
|
+
On TypeScript backends, `await createJazzContext(...).forRequest(req)` follows the same rule set. Configure `jwksUrl` or `jwtPublicKey` on the backend context to verify external JWTs there too, but not both.
|
|
190
220
|
|
|
191
221
|
### Upgrading to external auth
|
|
192
222
|
|
|
@@ -210,12 +240,14 @@ Local-first auth lets users start using your app immediately — no sign-up, no
|
|
|
210
240
|
|
|
211
241
|
## Client setup
|
|
212
242
|
|
|
213
|
-
Use `
|
|
243
|
+
Use `useLocalFirstAuth()` to manage the user's secret. On first load it generates a new identity; on subsequent loads it reuses the stored one. It also exposes `login` and `signOut` for switching or clearing the local identity.
|
|
214
244
|
|
|
215
245
|
```tsx
|
|
216
246
|
|
|
217
247
|
function App() {
|
|
218
|
-
const secret =
|
|
248
|
+
const { secret, isLoading } = useLocalFirstAuth();
|
|
249
|
+
|
|
250
|
+
if (isLoading || !secret) return <p>Loading…</p>;
|
|
219
251
|
|
|
220
252
|
return (
|
|
221
253
|
|
|
@@ -349,36 +381,54 @@ On the server, a BetterAuth middleware hook intercepts sign-up requests, verifie
|
|
|
349
381
|
The app switches between local-first auth and external JWT based on whether the user has a BetterAuth session:
|
|
350
382
|
|
|
351
383
|
```tsx
|
|
352
|
-
function
|
|
353
|
-
const { data
|
|
354
|
-
const [
|
|
384
|
+
function useBetterAuthJWT() {
|
|
385
|
+
const { data, isPending } = authClient.useSession();
|
|
386
|
+
const [jwt, setJwt] = useState<string | null>(null);
|
|
387
|
+
const [isFetching, setIsFetching] = useState(false);
|
|
355
388
|
|
|
356
389
|
useEffect(() => {
|
|
357
390
|
if (isPending) return;
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
appId: process.env.NEXT_PUBLIC_JAZZ_APP_ID!,
|
|
362
|
-
serverUrl: process.env.NEXT_PUBLIC_JAZZ_SERVER_URL!,
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
if (authSession?.session) {
|
|
366
|
-
// User is signed in with BetterAuth — use their JWT
|
|
367
|
-
const jwtToken = await getJwtFromBetterAuth();
|
|
368
|
-
setConfig({ ...sharedConfig, jwtToken: jwtToken! });
|
|
369
|
-
} else {
|
|
370
|
-
// No external session — use local-first auth
|
|
371
|
-
const secret = await BrowserAuthSecretStore.getOrCreateSecret();
|
|
372
|
-
setConfig({ ...sharedConfig, auth: { localFirstSecret: secret } });
|
|
373
|
-
}
|
|
391
|
+
if (!data?.session) {
|
|
392
|
+
setJwt(null);
|
|
393
|
+
return;
|
|
374
394
|
}
|
|
395
|
+
setIsFetching(true);
|
|
396
|
+
void getJwtFromBetterAuth().then((token) => {
|
|
397
|
+
setJwt(token ?? null);
|
|
398
|
+
setIsFetching(false);
|
|
399
|
+
});
|
|
400
|
+
}, [isPending, data?.session?.id]);
|
|
375
401
|
|
|
376
|
-
|
|
377
|
-
|
|
402
|
+
return {
|
|
403
|
+
isLoading: isPending || isFetching,
|
|
404
|
+
jwt,
|
|
405
|
+
getRefreshedJWT: () => getJwtFromBetterAuth(),
|
|
406
|
+
};
|
|
407
|
+
}
|
|
378
408
|
|
|
379
|
-
|
|
409
|
+
function App() {
|
|
410
|
+
const betterAuth = useBetterAuthJWT();
|
|
411
|
+
const { secret: localFirstSecret, isLoading: localFirstLoading } = useLocalFirstAuth();
|
|
412
|
+
|
|
413
|
+
// Only mint a local-first secret when there's no BetterAuth session.
|
|
414
|
+
const secret = !betterAuth.jwt ? (localFirstSecret ?? undefined) : undefined;
|
|
415
|
+
|
|
416
|
+
const config = useMemo(
|
|
417
|
+
() => ({
|
|
418
|
+
appId: process.env.NEXT_PUBLIC_JAZZ_APP_ID!,
|
|
419
|
+
serverUrl: process.env.NEXT_PUBLIC_JAZZ_SERVER_URL!,
|
|
420
|
+
jwtToken: betterAuth.jwt ?? undefined,
|
|
421
|
+
secret,
|
|
422
|
+
}),
|
|
423
|
+
[betterAuth.jwt, secret],
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
if (betterAuth.isLoading || (!betterAuth.jwt && localFirstLoading)) return <p>Loading auth…</p>;
|
|
380
427
|
|
|
381
428
|
return (
|
|
429
|
+
betterAuth.getRefreshedJWT()}
|
|
430
|
+
fallback={<p>Loading Jazz DB…</p>}
|
|
431
|
+
>
|
|
382
432
|
|
|
383
433
|
);
|
|
384
434
|
}
|
|
@@ -391,7 +441,7 @@ You can restrict what local-first users can do until they sign up. Define an `is
|
|
|
391
441
|
```ts
|
|
392
442
|
|
|
393
443
|
const isLocalFirstAuthMode = session.where({
|
|
394
|
-
|
|
444
|
+
authMode: "local-first",
|
|
395
445
|
});
|
|
396
446
|
|
|
397
447
|
// Everyone can read
|
|
@@ -409,7 +459,7 @@ The BetterAuth example above uses a framework-specific middleware hook. With any
|
|
|
409
459
|
1. Accepts `proofToken` alongside the sign-up credentials
|
|
410
460
|
2. Verifies it with `verifyLocalFirstIdentityProof` from `jazz-napi`
|
|
411
461
|
3. Stores the proven Jazz user ID linked to the new user account
|
|
412
|
-
4. Issues JWTs with
|
|
462
|
+
4. Issues JWTs with `sub: jazzId`
|
|
413
463
|
|
|
414
464
|
```ts title="server.ts"
|
|
415
465
|
|
|
@@ -427,8 +477,7 @@ app.post("/signup", async (req, res) => {
|
|
|
427
477
|
);
|
|
428
478
|
if (!ok) return res.status(400).json({ error });
|
|
429
479
|
|
|
430
|
-
// Store jazzUserId so you can
|
|
431
|
-
// or, for managed providers with provider-owned `sub`, as `jazz_principal_id`
|
|
480
|
+
// Store jazzUserId so you can mint future JWTs with it as `sub`.
|
|
432
481
|
const user = await db.users.create({ email, password, jazzId: jazzUserId });
|
|
433
482
|
|
|
434
483
|
res.json({ token: issueJwt({ sub: user.jazzId /* ... */ }) });
|
|
@@ -437,7 +486,7 @@ app.post("/signup", async (req, res) => {
|
|
|
437
486
|
|
|
438
487
|
The `audience` string on the client and server must match. `jazz-napi` normalizes it internally, so any consistent string works.
|
|
439
488
|
|
|
440
|
-
|
|
489
|
+
Jazz uses the JWT `sub` claim as `session.user_id`. If your provider keeps `sub` fixed to its own user ID, add a `/link-jazz-identity` endpoint that stores the Jazz ID and mint the JWT with `sub: <jazzId>` yourself — either via a custom `getSubject` hook on your provider or by issuing the JWT directly.
|
|
441
490
|
|
|
442
491
|
## Under the hood
|
|
443
492
|
|
|
@@ -906,6 +955,7 @@ edit metadata. Use the attribution helpers for this:
|
|
|
906
955
|
|
|
907
956
|
const syntheticSession = {
|
|
908
957
|
user_id: "user_123",
|
|
958
|
+
authMode: "external" as const,
|
|
909
959
|
claims: {},
|
|
910
960
|
};
|
|
911
961
|
|
|
@@ -1516,6 +1566,7 @@ Every backend needs an app ID, typed schema, and usually a permissions bundle. I
|
|
|
1516
1566
|
backendSecret,
|
|
1517
1567
|
adminSecret,
|
|
1518
1568
|
jwksUrl,
|
|
1569
|
+
jwtPublicKey,
|
|
1519
1570
|
allowLocalFirstAuth,
|
|
1520
1571
|
env: "dev",
|
|
1521
1572
|
userBranch: "main",
|
|
@@ -1551,7 +1602,7 @@ On the client side, queries automatically run as the logged-in user. On a server
|
|
|
1551
1602
|
|
|
1552
1603
|
In TypeScript backends, create the context once with both `app` and `permissions`, then use `context.asBackend()` for server-owned work and `await context.forRequest(req)` when you need a bearer-JWT-scoped high-level `Db`.
|
|
1553
1604
|
|
|
1554
|
-
`forRequest` reads authentication from standard HTTP headers, so `req` can be any object that exposes them: Express, Hono, Fastify, or a Web Fetch API `Request` all work. Configure `jwksUrl` on `createJazzContext(...)` if those bearer tokens come from an external IdP. Without
|
|
1605
|
+
`forRequest` reads authentication from standard HTTP headers, so `req` can be any object that exposes them: Express, Hono, Fastify, or a Web Fetch API `Request` all work. Configure `jwksUrl` or `jwtPublicKey` on `createJazzContext(...)` if those bearer tokens come from an external IdP, but do not set both at once. Without either one, backend `forRequest()` only accepts Jazz self-signed tokens; set `allowLocalFirstAuth: false` to disable those too.
|
|
1555
1606
|
|
|
1556
1607
|
For embedded or local-only runtime setups where your backend is not connected to a server, use
|
|
1557
1608
|
`context.db()` to get an unscoped, unauthenticated handle. This allows read/write access to the
|
|
@@ -2313,6 +2364,7 @@ const context = createJazzContext({
|
|
|
2313
2364
|
serverUrl: process.env.JAZZ_SERVER_URL,
|
|
2314
2365
|
backendSecret: process.env.JAZZ_BACKEND_SECRET,
|
|
2315
2366
|
jwksUrl: process.env.JAZZ_JWKS_URL,
|
|
2367
|
+
jwtPublicKey: process.env.JAZZ_JWT_PUBLIC_KEY,
|
|
2316
2368
|
allowLocalFirstAuth: process.env.JAZZ_ALLOW_LOCAL_FIRST_AUTH !== "false",
|
|
2317
2369
|
});
|
|
2318
2370
|
|
|
@@ -2323,7 +2375,7 @@ const api = new Hono();
|
|
|
2323
2375
|
- `app` is your typed schema export.
|
|
2324
2376
|
- `permissions` is the server-side policy bundle.
|
|
2325
2377
|
- `serverUrl` + `backendSecret` let request-scoped handles sync through a Jazz server.
|
|
2326
|
-
- `jwksUrl` verifies external JWTs inside `await context.forRequest(req)`. Without
|
|
2378
|
+
- `jwksUrl` or `jwtPublicKey` verifies external JWTs inside `await context.forRequest(req)`. Without either one, the backend only accepts Jazz self-signed tokens unless you set `allowLocalFirstAuth: false`.
|
|
2327
2379
|
- `dataPath` controls where local server state persists.
|
|
2328
2380
|
|
|
2329
2381
|
Each route handler awaits `context.forRequest(c.req)` to get a database handle with [permissions](/docs/auth/permissions) scoped to the request.
|
|
@@ -2435,8 +2487,8 @@ curl -X DELETE http://localhost:3000/api/todos/<id> \
|
|
|
2435
2487
|
|
|
2436
2488
|
`forRequest` verifies the caller's bearer token inside the backend context. The token above is a
|
|
2437
2489
|
Jazz self-signed dev token, which works because `allowLocalFirstAuth` defaults to `true`. If you
|
|
2438
|
-
want to accept external JWTs from your auth provider, also set `jwksUrl`
|
|
2439
|
-
|
|
2490
|
+
want to accept external JWTs from your auth provider, also set `jwksUrl` or `jwtPublicKey` so
|
|
2491
|
+
the backend can verify them.
|
|
2440
2492
|
|
|
2441
2493
|
## Authentication
|
|
2442
2494
|
|
|
@@ -3103,7 +3155,7 @@ Jazz supports external JWT-based authentication for production use. This recipe
|
|
|
3103
3155
|
1. The user signs in with your auth provider and gets a JWT.
|
|
3104
3156
|
2. Your client passes that JWT to Jazz.
|
|
3105
3157
|
3. The Jazz server validates the JWT signature against the provider's JWKS endpoint.
|
|
3106
|
-
4. On success, Jazz
|
|
3158
|
+
4. On success, Jazz uses the JWT's `sub` claim as `session.user_id`.
|
|
3107
3159
|
|
|
3108
3160
|
If you're unfamiliar with JWTs and JWKS, see the [explainer on the Authentication page](/docs/auth/authentication#external-auth-for-production).
|
|
3109
3161
|
|
|
@@ -3250,7 +3302,7 @@ Any provider that issues JWTs and exposes a JWKS endpoint will work. The key pat
|
|
|
3250
3302
|
| Auth0 | `https://<tenant>.auth0.com/.well-known/jwks.json` | `auth0\|<id>` |
|
|
3251
3303
|
| Firebase | `https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com` | Firebase UID |
|
|
3252
3304
|
|
|
3253
|
-
|
|
3305
|
+
Jazz uses the JWT `sub` claim as `session.user_id`. If your provider keeps `sub` fixed to its own user ID, mint the JWT with `sub` set to the Jazz user ID yourself — either via a custom `getSubject` hook on the provider or by issuing the JWT directly from your server.
|
|
3254
3306
|
|
|
3255
3307
|
Full working examples: [Better Auth chat](https://github.com/garden-co/jazz/tree/main/examples/auth-betterauth-chat), [WorkOS chat](https://github.com/garden-co/jazz/tree/main/examples/auth-workos-chat).
|
|
3256
3308
|
|
|
@@ -3732,11 +3784,11 @@ See [Writing Data](/docs/writing/writing-data#write-durability-tiers) for detail
|
|
|
3732
3784
|
|
|
3733
3785
|
Read durability controls when the **first result** of a query or subscription is delivered.
|
|
3734
3786
|
|
|
3735
|
-
| Option | Values
|
|
3736
|
-
| -------------- |
|
|
3787
|
+
| Option | Values | Default | What it does |
|
|
3788
|
+
| -------------- | ----------------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
3737
3789
|
| `tier` | `"local"` \| `"edge"` \| `"global"` | Same as writes | How far the first result must come from before the query delivers. |
|
|
3738
|
-
| `localUpdates` | `"immediate"` \| `"deferred"`
|
|
3739
|
-
| `propagation` | `"full"` \| `"local-only"`
|
|
3790
|
+
| `localUpdates` | `"immediate"` \| `"deferred"` | `"immediate"` | With `"immediate"`, your own local writes appear in the subscription while still waiting for the tier to confirm the initial snapshot. This only takes effect after the subscription has settled at least once. With `"deferred"`, all delivery is held until the tier confirms. |
|
|
3791
|
+
| `propagation` | `"full"` \| `"local-only"` | `"full"` | With `"full"`, the subscription is sent to upstream servers, which push matching data back. With `"local-only"`, only local storage is queried — no server communication. |
|
|
3740
3792
|
|
|
3741
3793
|
These options apply to:
|
|
3742
3794
|
|
|
@@ -5351,7 +5403,7 @@ When using `insertDurable`, `updateDurable`, or `deleteDurable`, you can pass a
|
|
|
5351
5403
|
|
|
5352
5404
|
| Tier | Resolves when | Default for |
|
|
5353
5405
|
| -------- | ----------------------------------- | -------------- |
|
|
5354
|
-
| `local`
|
|
5406
|
+
| `local` | Persisted to local OPFS | Browser/client |
|
|
5355
5407
|
| `edge` | Acknowledged by nearest sync server | Backend/server |
|
|
5356
5408
|
| `global` | Propagated to global core | — |
|
|
5357
5409
|
|
|
@@ -5445,4 +5497,4 @@ batch.update(app.todos, todoId, { done: true });
|
|
|
5445
5497
|
console.log(batch.batchId());
|
|
5446
5498
|
```
|
|
5447
5499
|
|
|
5448
|
-
Both builders can also issue persisted writes, expose `localBatchRecord()` / `localBatchRecords()`, and let you acknowledge replayable rejections by batch id.
|
|
5500
|
+
Both builders can also issue persisted writes, expose `localBatchRecord()` / `localBatchRecords()`, and let you acknowledge replayable rejections by batch id.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { JWK } from "jose";
|
|
1
2
|
import type { WasmSchema } from "../drivers/types.js";
|
|
2
3
|
import type { CompiledPermissions } from "../permissions/index.js";
|
|
3
4
|
import { type RequestLike } from "../runtime/client.js";
|
|
@@ -10,6 +11,7 @@ export interface BackendQuerySchemaSource {
|
|
|
10
11
|
_schema: WasmSchema;
|
|
11
12
|
}
|
|
12
13
|
export type BackendSchemaInput = WasmSchema | BackendSchemaSource | BackendQuerySchemaSource;
|
|
14
|
+
export type BackendJwtPublicKey = JWK | string;
|
|
13
15
|
export type BackendDriver = {
|
|
14
16
|
type: "persistent";
|
|
15
17
|
/** Path to the Fjall file used by the server runtime. */
|
|
@@ -33,6 +35,8 @@ export type BackendContextConfig = Omit<AppContext, "schema" | "driver" | "clien
|
|
|
33
35
|
tier?: "local" | "edge" | "global";
|
|
34
36
|
/** JWKS endpoint used to verify external bearer JWTs in `forRequest()`. */
|
|
35
37
|
jwksUrl?: string;
|
|
38
|
+
/** Single JWK object or PEM/JWK string used to verify external bearer JWTs in `forRequest()`. */
|
|
39
|
+
jwtPublicKey?: BackendJwtPublicKey;
|
|
36
40
|
/** Whether local-first bearer JWTs are accepted in `forRequest()`. Defaults to `true`. */
|
|
37
41
|
allowLocalFirstAuth?: boolean;
|
|
38
42
|
} & BackendContextSchemaConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-jazz-context.d.ts","sourceRoot":"","sources":["../../src/backend/create-jazz-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAsB,KAAK,EAAE,EAAiB,MAAM,kBAAkB,CAAC;AAI9E,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,mBAAmB,GAAG,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"create-jazz-context.d.ts","sourceRoot":"","sources":["../../src/backend/create-jazz-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,9 @@ function assertValidBackendConfig(config) {
|
|
|
8
8
|
if (config.driver.type === "memory" && !config.serverUrl) {
|
|
9
9
|
throw new Error("driver.type='memory' requires serverUrl.");
|
|
10
10
|
}
|
|
11
|
+
if (config.jwksUrl !== undefined && config.jwtPublicKey !== undefined) {
|
|
12
|
+
throw new Error("Backend auth config cannot set both jwksUrl and jwtPublicKey. Pick one external JWT verification mode.");
|
|
13
|
+
}
|
|
11
14
|
}
|
|
12
15
|
function isRecord(value) {
|
|
13
16
|
return typeof value === "object" && value !== null;
|
|
@@ -81,7 +84,6 @@ export class JazzContext {
|
|
|
81
84
|
appId: this.config.appId,
|
|
82
85
|
schema,
|
|
83
86
|
serverUrl: this.config.serverUrl,
|
|
84
|
-
serverPathPrefix: this.config.serverPathPrefix,
|
|
85
87
|
env: this.config.env,
|
|
86
88
|
userBranch: this.config.userBranch,
|
|
87
89
|
jwtToken: this.config.jwtToken,
|
|
@@ -97,7 +99,7 @@ export class JazzContext {
|
|
|
97
99
|
backend_secret: this.config.backendSecret,
|
|
98
100
|
admin_secret: this.config.adminSecret,
|
|
99
101
|
jwt_token: this.config.jwtToken,
|
|
100
|
-
}
|
|
102
|
+
});
|
|
101
103
|
}
|
|
102
104
|
return this.clientInstance;
|
|
103
105
|
}
|
|
@@ -106,7 +108,6 @@ export class JazzContext {
|
|
|
106
108
|
appId: this.config.appId,
|
|
107
109
|
driver: this.config.driver.type === "memory" ? { type: "memory" } : { type: "persistent" },
|
|
108
110
|
serverUrl: this.config.serverUrl,
|
|
109
|
-
serverPathPrefix: this.config.serverPathPrefix,
|
|
110
111
|
env: this.config.env,
|
|
111
112
|
userBranch: this.config.userBranch,
|
|
112
113
|
jwtToken: this.config.jwtToken,
|
|
@@ -116,8 +117,7 @@ export class JazzContext {
|
|
|
116
117
|
wrapDb(client, session, attribution, backendScoped = false) {
|
|
117
118
|
return createDbFromClient(this.buildDbConfig(), client, session, attribution, backendScoped
|
|
118
119
|
? {
|
|
119
|
-
|
|
120
|
-
transport: "backend",
|
|
120
|
+
authMode: session?.authMode ?? "external",
|
|
121
121
|
session: session ?? null,
|
|
122
122
|
}
|
|
123
123
|
: undefined);
|
|
@@ -176,6 +176,7 @@ export class JazzContext {
|
|
|
176
176
|
return await resolveRequestSession(request, {
|
|
177
177
|
appId: this.config.appId,
|
|
178
178
|
jwksUrl: this.config.jwksUrl,
|
|
179
|
+
jwtPublicKey: this.config.jwtPublicKey,
|
|
179
180
|
allowLocalFirstAuth: this.config.allowLocalFirstAuth,
|
|
180
181
|
});
|
|
181
182
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-jazz-context.js","sourceRoot":"","sources":["../../src/backend/create-jazz-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"create-jazz-context.js","sourceRoot":"","sources":["../../src/backend/create-jazz-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -150,6 +150,7 @@ describe("backend/create-jazz-context", () => {
|
|
|
150
150
|
mocks.resolveRequestSession.mockResolvedValue({
|
|
151
151
|
user_id: "u1",
|
|
152
152
|
claims: {},
|
|
153
|
+
authMode: "external",
|
|
153
154
|
});
|
|
154
155
|
});
|
|
155
156
|
it("BC-U01: lazily initializes runtime/client on first access", () => {
|
|
@@ -175,6 +176,21 @@ describe("backend/create-jazz-context", () => {
|
|
|
175
176
|
});
|
|
176
177
|
expect(mocks.runtimeCtor).toHaveBeenCalledWith(expect.any(String), "server-app", "dev", "main", "/tmp/jazz.db", "edge");
|
|
177
178
|
});
|
|
179
|
+
it("BC-U01b: rejects configuring both jwksUrl and jwtPublicKey", () => {
|
|
180
|
+
expect(() => createJazzContext({
|
|
181
|
+
appId: "server-app",
|
|
182
|
+
app: { wasmSchema: SCHEMA_A },
|
|
183
|
+
permissions: {},
|
|
184
|
+
driver: { type: "persistent", dataPath: "/tmp/jazz.db" },
|
|
185
|
+
jwksUrl: "https://issuer.example/.well-known/jwks.json",
|
|
186
|
+
jwtPublicKey: {
|
|
187
|
+
kty: "oct",
|
|
188
|
+
kid: "static-kid",
|
|
189
|
+
alg: "HS256",
|
|
190
|
+
k: "c3RhdGljLXNlY3JldA",
|
|
191
|
+
},
|
|
192
|
+
})).toThrow(/jwksUrl.*jwtPublicKey|jwtPublicKey.*jwksUrl/i);
|
|
193
|
+
});
|
|
178
194
|
it("BC-U02: supports high-level db/backend/request/session/attribution helpers", async () => {
|
|
179
195
|
const context = createJazzContext({
|
|
180
196
|
appId: "server-app",
|
|
@@ -187,7 +203,7 @@ describe("backend/create-jazz-context", () => {
|
|
|
187
203
|
const req = {
|
|
188
204
|
header: (name) => name === "authorization" ? `Bearer ${makeJwt({ sub: "u1" })}` : undefined,
|
|
189
205
|
};
|
|
190
|
-
const session = { user_id: "u1", claims: {} };
|
|
206
|
+
const session = { user_id: "u1", claims: {}, authMode: "external" };
|
|
191
207
|
const db = context.db();
|
|
192
208
|
const backendDb = context.asBackend();
|
|
193
209
|
const requestDb = await context.forRequest(req);
|
|
@@ -206,7 +222,7 @@ describe("backend/create-jazz-context", () => {
|
|
|
206
222
|
expect(requestDb).toEqual({
|
|
207
223
|
kind: "scoped-db",
|
|
208
224
|
client: mocks.clients[0],
|
|
209
|
-
session: { user_id: "u1", claims: {} },
|
|
225
|
+
session: { user_id: "u1", claims: {}, authMode: "external" },
|
|
210
226
|
});
|
|
211
227
|
expect(sessionDb).toEqual({
|
|
212
228
|
kind: "scoped-db",
|
|
@@ -253,7 +269,7 @@ describe("backend/create-jazz-context", () => {
|
|
|
253
269
|
const req = {
|
|
254
270
|
header: (name) => name === "authorization" ? `Bearer ${makeJwt({ sub: "u1" })}` : undefined,
|
|
255
271
|
};
|
|
256
|
-
const session = { user_id: "u1", claims: {} };
|
|
272
|
+
const session = { user_id: "u1", claims: {}, authMode: "external" };
|
|
257
273
|
await expect(context.forRequest(req)).resolves.toBeDefined();
|
|
258
274
|
expect(() => context.forSession(session)).not.toThrow();
|
|
259
275
|
expect(() => context.withAttribution("u2")).not.toThrow();
|
|
@@ -280,6 +296,36 @@ describe("backend/create-jazz-context", () => {
|
|
|
280
296
|
allowLocalFirstAuth: false,
|
|
281
297
|
});
|
|
282
298
|
});
|
|
299
|
+
it("BC-U03c: forwards jwtPublicKey into request session resolution", async () => {
|
|
300
|
+
const context = createJazzContext({
|
|
301
|
+
appId: "server-app",
|
|
302
|
+
app: { wasmSchema: SCHEMA_A },
|
|
303
|
+
permissions: {},
|
|
304
|
+
driver: { type: "persistent", dataPath: "/tmp/jazz.db" },
|
|
305
|
+
jwtPublicKey: {
|
|
306
|
+
kty: "oct",
|
|
307
|
+
kid: "static-kid",
|
|
308
|
+
alg: "HS256",
|
|
309
|
+
k: "c3RhdGljLXNlY3JldA",
|
|
310
|
+
},
|
|
311
|
+
allowLocalFirstAuth: false,
|
|
312
|
+
});
|
|
313
|
+
const req = {
|
|
314
|
+
header: (name) => name === "authorization" ? `Bearer ${makeJwt({ sub: "u1" })}` : undefined,
|
|
315
|
+
};
|
|
316
|
+
await context.forRequest(req);
|
|
317
|
+
expect(mocks.resolveRequestSession).toHaveBeenCalledWith(req, {
|
|
318
|
+
appId: "server-app",
|
|
319
|
+
jwksUrl: undefined,
|
|
320
|
+
jwtPublicKey: {
|
|
321
|
+
kty: "oct",
|
|
322
|
+
kid: "static-kid",
|
|
323
|
+
alg: "HS256",
|
|
324
|
+
k: "c3RhdGljLXNlY3JldA",
|
|
325
|
+
},
|
|
326
|
+
allowLocalFirstAuth: false,
|
|
327
|
+
});
|
|
328
|
+
});
|
|
283
329
|
it("BC-U04: merges compiled permissions into the runtime schema", () => {
|
|
284
330
|
const context = createJazzContext({
|
|
285
331
|
appId: "server-app",
|