use-convex 1.2.0 → 2.0.0

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.
Files changed (55) hide show
  1. package/README.md +120 -41
  2. package/dist/module.d.mts +73 -31
  3. package/dist/module.mjs +73 -17
  4. package/dist/runtime/auth/oauth.d.ts +96 -0
  5. package/dist/runtime/auth/oauth.js +170 -0
  6. package/dist/runtime/auth/sessionRoutes.d.ts +37 -0
  7. package/dist/runtime/auth/sessionRoutes.js +41 -0
  8. package/dist/runtime/composables/useAuth.d.ts +81 -48
  9. package/dist/runtime/composables/useAuth.js +69 -398
  10. package/dist/runtime/composables/useAuthToken.d.ts +2 -2
  11. package/dist/runtime/composables/useAuthToken.js +4 -0
  12. package/dist/runtime/composables/useConvexAuth.js +1 -1
  13. package/dist/runtime/composables/useConvexPaginatedQuery.js +6 -1
  14. package/dist/runtime/composables/useConvexQueries.d.ts +36 -7
  15. package/dist/runtime/composables/useConvexQueries.js +37 -13
  16. package/dist/runtime/composables/useConvexQuery.js +6 -1
  17. package/dist/runtime/plugin.auth.client.d.ts +8 -3
  18. package/dist/runtime/plugin.auth.client.js +208 -23
  19. package/dist/runtime/plugin.server.js +8 -2
  20. package/dist/runtime/server/auth.d.ts +7 -2
  21. package/dist/runtime/server/auth.js +14 -3
  22. package/dist/runtime/server/convexAuth/refresh.d.ts +3 -0
  23. package/dist/runtime/server/convexAuth/refresh.js +5 -0
  24. package/dist/runtime/server/convexAuth/session.middleware.d.ts +10 -0
  25. package/dist/runtime/server/convexAuth/session.middleware.js +37 -0
  26. package/dist/runtime/server/convexAuth/setup.d.ts +21 -0
  27. package/dist/runtime/server/convexAuth/setup.js +90 -0
  28. package/dist/runtime/server/convexAuth/signin.d.ts +3 -0
  29. package/dist/runtime/server/convexAuth/signin.js +5 -0
  30. package/dist/runtime/server/convexAuth/signout.d.ts +3 -0
  31. package/dist/runtime/server/convexAuth/signout.js +5 -0
  32. package/dist/runtime/server/convexConfig.d.ts +2 -6
  33. package/dist/runtime/server/fetch.d.ts +9 -1
  34. package/dist/runtime/server/fetch.js +7 -10
  35. package/dist/runtime/utils/authClientOptions.d.ts +54 -0
  36. package/dist/runtime/utils/authClientOptions.js +10 -0
  37. package/dist/runtime/utils/authCookie.d.ts +8 -14
  38. package/dist/runtime/utils/authCookie.js +23 -27
  39. package/dist/runtime/utils/authStorage.d.ts +80 -42
  40. package/dist/runtime/utils/authStorage.js +56 -66
  41. package/dist/runtime/utils/context.d.ts +42 -6
  42. package/dist/runtime/utils/http.d.ts +7 -0
  43. package/dist/runtime/utils/http.js +5 -2
  44. package/dist/types.d.mts +6 -2
  45. package/package.json +10 -5
  46. package/dist/runtime/server/api/convex/auth/session.d.ts +0 -36
  47. package/dist/runtime/server/api/convex/auth/session.js +0 -90
  48. package/dist/runtime/server/authCookies.d.ts +0 -9
  49. package/dist/runtime/server/authCookies.js +0 -64
  50. package/dist/runtime/server/sameOrigin.d.ts +0 -14
  51. package/dist/runtime/server/sameOrigin.js +0 -29
  52. package/dist/runtime/utils/authMutex.d.ts +0 -15
  53. package/dist/runtime/utils/authMutex.js +0 -47
  54. package/dist/runtime/utils/oauthRedirect.d.ts +0 -5
  55. package/dist/runtime/utils/oauthRedirect.js +0 -13
package/README.md CHANGED
@@ -75,7 +75,7 @@ Args accept the query's `FunctionArgs`, `'skip'`, or a `MaybeRefOrGetter` of eit
75
75
 
76
76
  - **`'skip'`** — gate on missing ids, feature flags, etc. Keeps the same payload key as empty args so SSR HTML survives.
77
77
  - **`authenticated: true`** — skip live subscribe until Convex confirms auth. SSR still snapshots when the JWT cookie is present. Prefer this over wrapping args yourself.
78
- - **`token`** — authenticated SSR for this call only. Never put JWTs in `runtimeConfig.public`. When `convex.auth.cookie` / HttpOnly auth is set, queries fall back to that cookie.
78
+ - **`token`** — authenticated SSR for this call only. Never put JWTs in `runtimeConfig.public`. Otherwise queries use the session token (Convex Auth SSR mode, or a BYO `convex.auth.cookie`).
79
79
 
80
80
  Warm a subscription before a screen mounts:
81
81
 
@@ -88,13 +88,20 @@ prewarmQuery(api.tasks.list, {})
88
88
  Browser-only live map (no SSR). Each value is `data | undefined` (loading) | `Error`. Combine with `useConvexQuery` when you need an SSR snapshot for known queries.
89
89
 
90
90
  ```ts
91
- const results = useConvexQueries(() => ({
92
- tasks: { query: api.tasks.list, args: {} },
93
- files: showFiles.value ? { query: api.files.list, args: {} } : 'skip',
94
- }))
91
+ const results = useConvexQueries(
92
+ () => ({
93
+ tasks: { query: api.tasks.list, args: {} },
94
+ files: showFiles.value ? { query: api.files.list, args: {} } : 'skip',
95
+ // Per-entry gate (overrides the options bag when set):
96
+ // admin: { query: api.admin.stats, args: {}, authenticated: true },
97
+ }),
98
+ { authenticated: true }, // wait for Convex auth before any subscribe
99
+ )
95
100
  // results.value.tasks
96
101
  ```
97
102
 
103
+ `'skip'` still wins for missing ids / feature flags. Prefer `{ authenticated: true }` over wrapping every private entry yourself.
104
+
98
105
  ### Pagination
99
106
 
100
107
  ```ts
@@ -126,6 +133,12 @@ const { run, pending, error } = useConvexAction(api.tasks.shout)
126
133
  const shouted = await run({ text: 'hello' }) // browser-only
127
134
  ```
128
135
 
136
+ `useConvexAction` calls `ConvexClient.action`. Actions from one client run in parallel with each other and with mutations. If a later call depends on an earlier one, await the earlier call before starting the next.
137
+
138
+ A failed action stays failed: Convex leaves retry to the caller, because the action may already have performed a side effect. `run` throws and sets `error`. Retry from your code when repeating the action is safe.
139
+
140
+ Use `useConvexAction` (and `fetchAction`) for work the user is waiting on. For durable work, write the user's intent in a mutation and schedule an `internalAction` from that mutation.
141
+
129
142
  For paginated lists:
130
143
 
131
144
  ```ts
@@ -183,30 +196,64 @@ That runs `generateUploadUrl` → `PUT` to the signed URL → `syncMetadata({ ke
183
196
 
184
197
  ## Auth
185
198
 
186
- ### Convex Auth
199
+ ### Convex Auth (v2)
200
+
201
+ First-party support for [Convex Auth v2](https://docs.convex.dev/auth/convex-auth/) (`@convex-dev/auth@^2`), built on its framework-agnostic `AuthClient` (`@convex-dev/auth/browser`) and SSR handlers (`@convex-dev/auth/ssr`). Requires `convex@^1.48`.
187
202
 
188
- Opt in with `provider: 'convex-auth'`. The module hydrates tokens, finishes OAuth `?code=` callbacks, and wires `setAuth`. Forms only call `signIn` / `signOut`.
203
+ ```bash
204
+ npm i @convex-dev/auth
205
+ ```
189
206
 
190
207
  ```ts
191
208
  convex: {
192
209
  url: process.env.NUXT_PUBLIC_CONVEX_URL,
193
210
  auth: {
194
211
  provider: 'convex-auth',
195
- // cookie defaults to 'convex_jwt'
212
+ // Every Convex function that can issue a session. The SSR auth proxy refuses the rest.
213
+ signIn: ['auth:signInWithUsernamePassword', 'auth:signUpWithUsernamePassword'],
214
+ // mode: 'ssr', // default; or 'spa'
215
+ // api: { refreshSession, signOut, isAuthenticated }, // default 'auth:<name>'
216
+ // routePrefix: '/api/convex/auth',
217
+ // cookieOptions: { secure, domain, path },
218
+ // allowedOrigins: ['https://app.example.com'],
219
+ // storage: 'localStorage', // or 'inMemory'
220
+ // storageNamespace: 'myapp', // default = deployment URL
221
+ // verbose: false,
196
222
  },
197
223
  }
198
224
  ```
199
225
 
226
+ **Session models**
227
+
228
+ | `mode` | Refresh token | Sign-in calls | SSR |
229
+ | --------------- | ---------------------------------- | ----------------------------------- | --------------------------------------------------- |
230
+ | `ssr` (default) | httpOnly cookie, set by Nitro | `POST <prefix>/signin` (auth proxy) | Authenticated; access token refreshed by middleware |
231
+ | `spa` | `localStorage` (or custom storage) | Straight to Convex | Signed out until the client restores |
232
+
233
+ In SSR mode the module mounts `<prefix>/signin`, `<prefix>/refresh` and `<prefix>/signout` (Convex Auth's `setupConvexAuthServer` handlers, with their CSRF origin check) and a Nitro middleware. Access tokens live 60 seconds, so the middleware refreshes an expiring token from the refresh cookie before SSR queries run, rotates both cookies, and hands the fresh token to the client payload. `signIn`, `allowedOrigins` and `cookieOptions` are server-only (`runtimeConfig.convexAuth`).
234
+
235
+ **Sign in.** Each v2 method exports its own typed functions. `useConvexSignIn` runs any of them through the right transport and adopts the session it returns:
236
+
200
237
  ```vue
201
238
  <script setup lang="ts">
202
- const { signIn, signOut } = useAuth()
239
+ import { api } from '~~/convex/_generated/api'
240
+
241
+ const { run: signIn, pending } = useConvexSignIn(api.auth.signInWithUsernamePassword)
242
+ const { signOut, showAuthedUi } = useAuth()
243
+
244
+ async function submit() {
245
+ const result = await signIn({ username, password })
246
+ if (result.status === 'error') {
247
+ // result.userError.error: 'INVALID_CREDENTIALS' | 'RATE_LIMITED' | … | 'OTHER_ERROR'
248
+ }
249
+ }
203
250
  </script>
204
251
 
205
252
  <template>
206
253
  <AuthLoading>Resolving…</AuthLoading>
207
254
  <AuthRefreshing>Refreshing session…</AuthRefreshing>
208
255
  <Authenticated>
209
- <!-- signed-in shell -->
256
+ <button @click="signOut()">Sign out</button>
210
257
  </Authenticated>
211
258
  <Unauthenticated>
212
259
  <!-- sign-in form -->
@@ -214,32 +261,56 @@ const { signIn, signOut } = useAuth()
214
261
  </template>
215
262
  ```
216
263
 
217
- Prefer `<Authenticated>` (or `showAuthedUi`) so SSR HTML does not flash the sign-in form. Gate private queries with `{ authenticated: true }`. `<AuthRefreshing>` shows only while an authenticated session is refreshing a rejected token (same as React).
264
+ Pass `{ kind: 'action' }` when a sign-in function is an action (function kinds are type-only).
265
+
266
+ **OAuth** (GitHub, Google, Apple). Starting a flow navigates to the provider. The callback (`?convexAuthCode=`) is finished on whichever page it lands, and the params are stripped from the URL and from `vue-router`:
218
267
 
219
268
  ```ts
220
- await signIn('password', { email, password, flow: 'signIn' })
221
- await signIn('github') // OAuth redirect; the plugin finishes on ?code=
222
- await signOut()
269
+ const { signIn } = useOAuthSignIn({
270
+ providerName: 'github',
271
+ startSignIn: api.auth.startSignInWithGithub,
272
+ completeSignIn: api.auth.completeSignInWithGithub, // list it in `signIn` for SSR mode
273
+ })
274
+ await signIn() // or signIn({ redirectTo: '/after-login' })
275
+
276
+ const { flowError } = useOAuth() // ACCESS_DENIED | EXPIRED | INVALID_FLOW | OTHER_ERROR
223
277
  ```
224
278
 
225
- You still own the Convex backend:
279
+ `useAuth()` returns `isLoading`, `isAuthenticated`, `isRefreshing`, `hasSsrSession`, `showAuthedUi`, `token`, `setSession(bundle)` and `signOut()`. While signed in, the client subscribes to your `isAuthenticated` query, so a session revoked on the server signs the browser out right away.
226
280
 
227
- - `convex/auth.ts` — `convexAuth({ providers })` exporting `auth`, `signIn`, `signOut`
228
- - `convex/schema.ts` — spread `authTables`
229
- - `convex/http.ts` — `auth.addHttpRoutes(http)` (required for OAuth / magic links)
230
- - `convex/auth.config.ts` — JWT issuer (`CONVEX_SITE_URL`)
231
- - Env: `JWT_PRIVATE_KEY` + `JWKS` (`npx @convex-dev/auth`), plus `SITE_URL` for OAuth
281
+ Prefer `<Authenticated>` (or `showAuthedUi`) so SSR HTML does not flash the sign-in form. Gate private queries with `{ authenticated: true }`.
232
282
 
233
- By default the JWT is a readable cookie (`convex_jwt`) and the refresh token lives in `localStorage`. For production Convex Auth apps, prefer HttpOnly dual cookies:
283
+ For a custom `TokenStorage`, extra ambient sign-ins, or a custom `replaceUrl`, call `configureConvexAuth` from a client plugin that runs first:
234
284
 
235
285
  ```ts
236
- auth: {
237
- provider: 'convex-auth',
238
- httpOnly: true, // JWT + refresh via Nitro `/api/convex/auth/session`
239
- }
286
+ // plugins/convex-auth-options.client.ts
287
+ export default defineNuxtPlugin({
288
+ name: 'convex-auth-options',
289
+ enforce: 'pre',
290
+ setup() {
291
+ configureConvexAuth({
292
+ storage: { getItem, setItem, removeItem }, // may be async
293
+ // ambientSignIns: [convexOAuth()], // [] disables OAuth callback handling
294
+ })
295
+ },
296
+ })
240
297
  ```
241
298
 
242
- A readable `convex_auth_present` marker drives `hasSsrSession` / `showAuthedUi` (UI shell only — **not** authorization). Always gate private live queries with `{ authenticated: true }` (or equivalent). Tokens are HttpOnly cookies; the session API returns the JWT only via same-origin `POST { getToken: true }` for `ConvexClient.setAuth`. That mitigates cookie theft, **not** XSS (any XSS that can call your origin can still obtain a token once the client needs it in memory).
299
+ You own the Convex backend (see the [v2 docs](https://docs.convex.dev/auth/convex-auth/) and `playground/convex`):
300
+
301
+ - `convex/convex.config.ts`: `app.use(auth)` plus each method's components (e.g. `authPassword`, `authUsername`)
302
+ - `convex/auth.config.ts`: `providers: [convexAuthProvider()]`
303
+ - `convex/auth.ts`: `setupConvexAuth({ component: components.auth })` exporting `signOut`, `refreshSession`, `isAuthenticated`, plus each method's sign-in functions
304
+ - Your own `users` table and a `createUser` callback. No `authTables`, `JWT_PRIVATE_KEY` or `JWKS`.
305
+
306
+ #### Migrating from v1 (`use-convex` 0.x)
307
+
308
+ - Backend: follow Convex's [v1 → v2 guide](https://docs.convex.dev/auth/convex-auth/migrating/from-convex-auth-v1).
309
+ - `signIn(provider, params)` is gone. Use `useConvexSignIn(api.auth.<fn>)` or `useOAuthSignIn(refs)`.
310
+ - `useAuth()` no longer returns `signIn`, `pending` or `error`. Sign-in results come back as `{ status, userError }`.
311
+ - Options removed: `httpOnly`, `presentCookie`, `shouldHandleCode` (and `configureConvexAuth({ replaceURL, shouldHandleCode, storageNamespace })`). SSR mode is the default and keeps the refresh token in an httpOnly cookie. `replaceURL` is now `replaceUrl`.
312
+ - `cookie` applies only to bring-your-own providers. Convex Auth uses `__convexAuthJWT` / `__convexAuthRefreshToken`.
313
+ - `/api/convex/auth/session` is replaced by `<prefix>/signin|refresh|signout`. List every session-issuing function in `convex.auth.signIn`.
243
314
 
244
315
  ### SSR query budget
245
316
 
@@ -290,7 +361,7 @@ auth: {
290
361
 
291
362
  ## Server routes
292
363
 
293
- Nitro helpers are auto-imported in `server/`. Pass the H3 `event` so they resolve the deployment URL and, when a cookie is configured, the JWT:
364
+ Nitro helpers are auto-imported in `server/`. Pass the H3 `event` so they resolve the deployment URL and the session JWT (Convex Auth SSR mode or a BYO cookie):
294
365
 
295
366
  ```ts
296
367
  // server/api/tasks.get.ts
@@ -318,7 +389,9 @@ export default defineEventHandler(async (event) => {
318
389
  })
319
390
  ```
320
391
 
321
- `fetchAction` uses the same options. Each helper builds a fresh `ConvexHttpClient`. Override with `{ token }` when you already have a JWT. `getConvexToken(event)` reads the cookie without throwing.
392
+ `fetchAction` uses the same options. Each helper builds a fresh `ConvexHttpClient`. Override with `{ token }` when you already have a JWT. Pass `{ adminToken }` (deploy key / admin key) for privileged server tooling — same as Next.js `fetchQuery` (user `token` is ignored when `adminToken` is set). `getConvexToken(event)` reads the cookie without throwing.
393
+
394
+ `fetchAction` throws on failure. Convex leaves retry to the caller, because the action may already have performed a side effect. Retry in the route when repeating the action is safe. Use `fetchAction` for work the request is waiting on. For durable work, write the intent in a mutation and schedule an `internalAction` from that mutation. Browser actions run in parallel with each other and with mutations; see [Mutations and actions](#mutations-and-actions).
322
395
 
323
396
  ## Config
324
397
 
@@ -328,10 +401,17 @@ convex: {
328
401
  server: true, // SSR snapshots; override per query with { server }
329
402
  client: { unsavedChangesWarning: false }, // forwarded to new ConvexClient()
330
403
  auth: {
331
- provider: 'convex-auth',
332
- cookie: 'convex_jwt',
333
- httpOnly: false,
334
- presentCookie: 'convex_auth_present',
404
+ provider: 'convex-auth', // omit for BYO providers
405
+ mode: 'ssr', // or 'spa'
406
+ signIn: ['auth:signInWithUsernamePassword'], // SSR auth proxy allowlist (server-only)
407
+ // api: { refreshSession: 'auth:refreshSession', signOut: 'auth:signOut', isAuthenticated: 'auth:isAuthenticated' },
408
+ // routePrefix: '/api/convex/auth',
409
+ // cookieOptions: { secure: true, domain: undefined, path: '/' },
410
+ // allowedOrigins: [],
411
+ // storage: 'localStorage', // or 'inMemory'
412
+ // storageNamespace: 'myapp',
413
+ // verbose: false,
414
+ // cookie: 'convex_jwt', // BYO providers only
335
415
  },
336
416
  }
337
417
  ```
@@ -360,21 +440,20 @@ pnpm install
360
440
 
361
441
  # Terminal 1 — Convex backend (writes CONVEX_URL to playground/.env.local)
362
442
  pnpm run dev:backend
363
- # first time: npx @convex-dev/auth # JWT_PRIVATE_KEY + JWKS
364
443
 
365
444
  # Terminal 2 — Nuxt
366
445
  pnpm run dev
367
446
  ```
368
447
 
369
- | Route | What it shows |
370
- | --------- | ----------------------------------------------------- |
371
- | `/` | Shell session: features + composable call-shape demos |
372
- | `/live` | SSR snapshot + live overlay (sign up, CRUD todos) |
373
- | `/server` | Nitro `fetchQuery` / `fetchMutation` / `fetchAction` |
374
- | `/files` | `useConvexFileUpload` (upload, list, preview, delete) |
375
- | `/extras` | `live: false`, pagination, action, connection state |
448
+ | Route | What it shows |
449
+ | --------- | --------------------------------------------------------------------- |
450
+ | `/` | Shell session: features + composable call-shape demos |
451
+ | `/live` | SSR snapshot + live overlay (username + password sign up, CRUD todos) |
452
+ | `/server` | Nitro `fetchQuery` / `fetchMutation` / `fetchAction` |
453
+ | `/files` | `useConvexFileUpload` (upload, list, preview, delete) |
454
+ | `/extras` | `live: false`, pagination, action, connection state |
376
455
 
377
- On `/server`: `GET /api/health` is public; `GET`/`POST /api/tasks` use the cookie JWT; `POST /api/shout` is a public `fetchAction` demo.
456
+ On `/server`: `GET /api/health` is public; `GET`/`POST /api/tasks` and `POST /api/shout` use the session JWT (refreshed by the Convex Auth middleware). `POST /api/shout` is an authenticated `fetchAction` demo.
378
457
 
379
458
  ## Releasing
380
459
 
package/dist/module.d.mts CHANGED
@@ -1,64 +1,106 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
2
  import * as convex_browser from 'convex/browser';
3
- export { AuthTokens, SignInResult, UseAuthReturn } from '../dist/runtime/composables/useAuth.js';
3
+ export { ConvexAuthSession, ConvexSignInResult, OAuthCompleteResult, OAuthFlowError, OAuthProviderRefs, OAuthSignInOptions, OAuthStartResult, UseAuthReturn } from '../dist/runtime/composables/useAuth.js';
4
4
  export { ConvexQueryArgs, UseConvexQueryOptions, UseConvexQueryReturn } from '../dist/runtime/composables/useConvexQuery.js';
5
5
  export { AuthTokenFetcher, UseConvexAuthReturn, UseConvexAuthSetupOptions } from '../dist/runtime/composables/useConvexAuth.js';
6
6
  export { OptimisticUpdate, UseConvexMutationOptions } from '../dist/runtime/composables/useConvexMutation.js';
7
7
  export { ConvexFileUploadExtraArgs, ConvexFileUploadMeta, UseConvexFileUploadOptions } from '../dist/runtime/composables/useConvexFileUpload.js';
8
8
  export { ConvexR2UploadApi, ConvexR2UploadProgress } from '../dist/runtime/composables/useConvexR2Upload.js';
9
9
  export { PaginatedQueryArgs, PaginatedQueryItem, PaginatedQueryReference, PaginationStatus, UseConvexPaginatedQueryOptions, UseConvexPaginatedQueryReturn } from '../dist/runtime/composables/useConvexPaginatedQuery.js';
10
- export { ConvexQueriesRequest, ConvexQueriesResult, ConvexQueryRequestEntry } from '../dist/runtime/composables/useConvexQueries.js';
10
+ export { ConvexQueriesRequest, ConvexQueriesResult, ConvexQueryRequestEntry, UseConvexQueriesOptions } from '../dist/runtime/composables/useConvexQueries.js';
11
11
  export { ConvexFetchOptions } from '../dist/runtime/server/fetch.js';
12
12
  export { ConvexRef } from '../dist/runtime/utils/functionReference.js';
13
13
  export { ConvexAuthContext, ConvexNuxtContext } from '../dist/runtime/utils/context.js';
14
14
  export { ConnectionState } from '../dist/runtime/composables/useConvexConnectionState.js';
15
15
  export { ConvexGate } from '../dist/runtime/composables/useConvexGate.js';
16
+ export { ConvexAmbientSignIn, ConvexAuthClientOptions, ConvexAuthStorageMode, ConvexAuthTokenStorage, configureConvexAuth } from '../dist/runtime/utils/authClientOptions.js';
17
+ export { ConvexAuthServerRuntimeConfig } from '../dist/runtime/utils/authStorage.js';
16
18
 
17
19
  interface ModuleAuthOptions {
18
20
  /**
19
- * Opt-in first-party Convex Auth (`@convex-dev/auth`) client.
21
+ * Opt-in first-party Convex Auth v2 (`@convex-dev/auth@^2`).
20
22
  *
21
- * When set to `'convex-auth'`, the module registers a client plugin that
22
- * hydrates tokens, handles OAuth `?code=` callbacks, and wires
23
- * `useConvexAuth({ fetchToken })`. It also auto-imports `useAuth`,
24
- * `signIn`, and `signOut` so apps only need forms.
23
+ * Registers a client plugin built on `AuthClient` from
24
+ * `@convex-dev/auth/browser` (session restore, refresh, cross-tab sync,
25
+ * OAuth callbacks, server-side revocation), auto-imports `useAuth`,
26
+ * `useConvexSignIn`, `useOAuth`, `useOAuthSignIn` and `signOut`, and in
27
+ * SSR mode mounts the auth routes from `@convex-dev/auth/ssr` in Nitro.
25
28
  *
26
29
  * Omit for bring-your-own providers (Clerk, Auth0, custom) — wire
27
30
  * `useConvexAuth({ fetchToken })` yourself.
28
31
  */
29
32
  provider?: 'convex-auth';
30
33
  /**
31
- * Cookie name whose value is a JWT used for authenticated SSR queries.
32
- * When set, the server plugin reads this cookie into `ctx.ssrToken` and
33
- * `useConvexQuery` falls back to it when `options.token` is omitted.
34
- * `useAuth` / `useConvexAuth` expose the presence of this cookie as
35
- * `hasSsrSession`.
36
- * Nitro `fetch*` helpers also read this cookie when `{ event }` is passed.
34
+ * Convex Auth session model.
37
35
  *
38
- * Defaults to `'convex_jwt'` when `provider === 'convex-auth'` and
39
- * `httpOnly` is off. With `httpOnly: true`, defaults to `__convexAuthJWT`.
40
- * Otherwise off — BYO apps must opt in and write the JWT after sign-in.
36
+ * - `'ssr'` (default): the refresh token lives in an httpOnly cookie set by
37
+ * Nitro; sign-ins go through the auth proxy route; SSR renders
38
+ * authenticated. Requires {@link signIn}.
39
+ * - `'spa'`: tokens live in browser storage and sign-ins go straight to
40
+ * Convex. SSR renders signed out.
41
+ *
42
+ * @default 'ssr'
41
43
  */
42
- cookie?: string;
44
+ mode?: 'ssr' | 'spa';
43
45
  /**
44
- * Store JWT + refresh tokens in HttpOnly cookies via Nitro
45
- * (`/api/convex/auth/session`). Cookies are not readable via
46
- * `document.cookie`. Prefer `true` in production.
47
- *
48
- * Note: ConvexClient still needs the JWT in memory for the WebSocket;
49
- * the session route returns it only via same-origin POST `{ getToken: true }`.
50
- * HttpOnly mitigates cookie theft, not XSS session theft. The readable
51
- * presence cookie drives `hasSsrSession` / `showAuthedUi` (UI only — gate
52
- * private queries with `{ authenticated: true }`).
46
+ * Names of the session functions exported from your Convex auth file
47
+ * (the result of `setupConvexAuth`).
53
48
  *
54
- * @default false
49
+ * @default { refreshSession: 'auth:refreshSession', signOut: 'auth:signOut', isAuthenticated: 'auth:isAuthenticated' }
50
+ */
51
+ api?: {
52
+ refreshSession?: string;
53
+ signOut?: string;
54
+ isAuthenticated?: string;
55
+ };
56
+ /**
57
+ * SSR mode: every Convex function that can issue a session (sign-in,
58
+ * sign-up, OAuth `completeSignIn*`, …), e.g.
59
+ * `['auth:signInWithUsernamePassword', 'auth:signUpWithUsernamePassword']`.
60
+ * The auth proxy refuses anything not listed. Stored in private runtime
61
+ * config (`runtimeConfig.convexAuth.signIn`).
62
+ */
63
+ signIn?: string[];
64
+ /**
65
+ * SSR mode: path prefix for the Nitro auth routes
66
+ * (`<prefix>/signin`, `<prefix>/refresh`, `<prefix>/signout`).
67
+ * @default '/api/convex/auth'
68
+ */
69
+ routePrefix?: string;
70
+ /**
71
+ * SSR mode: origins trusted by the auth routes' CSRF check besides the
72
+ * request's own Host (e.g. behind a proxy that rewrites Host).
55
73
  */
56
- httpOnly?: boolean;
74
+ allowedOrigins?: string[];
57
75
  /**
58
- * Readable presence cookie when `httpOnly` is enabled.
59
- * @default 'convex_auth_present'
76
+ * SSR mode: auth cookie attributes. `httpOnly`, `sameSite` and lifetimes
77
+ * are fixed by Convex Auth. `secure` defaults to true except on localhost.
60
78
  */
61
- presentCookie?: string;
79
+ cookieOptions?: {
80
+ secure?: boolean;
81
+ domain?: string;
82
+ path?: string;
83
+ };
84
+ /**
85
+ * Namespace for token storage keys. Non-alphanumeric characters are
86
+ * ignored. Defaults to the deployment URL.
87
+ */
88
+ storageNamespace?: string;
89
+ /**
90
+ * Client token storage. `'localStorage'` (default) or `'inMemory'`.
91
+ * For a custom TokenStorage, call `configureConvexAuth({ storage })` from a
92
+ * client plugin with `enforce: 'pre'`.
93
+ * @default 'localStorage'
94
+ */
95
+ storage?: 'localStorage' | 'inMemory';
96
+ /** Log Convex Auth refresh / lifecycle steps to the console. */
97
+ verbose?: boolean;
98
+ /**
99
+ * BYO providers only: cookie whose value is a JWT used for authenticated
100
+ * SSR queries and Nitro `fetch*` helpers. The app writes it after sign-in.
101
+ * Ignored with `provider: 'convex-auth'`.
102
+ */
103
+ cookie?: string;
62
104
  }
63
105
  interface ModuleOptions {
64
106
  /**
package/dist/module.mjs CHANGED
@@ -1,8 +1,9 @@
1
1
  import { defineNuxtModule, createResolver, addPlugin, addComponent, addServerScanDir, addServerHandler, addImports, addServerImports, addTypeTemplate } from '@nuxt/kit';
2
2
  import { defu } from 'defu';
3
- import { resolveAuthCookieName } from '../dist/runtime/utils/authStorage.js';
3
+ import { isConvexAuth, resolveAuthMode, resolveAuthRoutes } from '../dist/runtime/utils/authStorage.js';
4
4
  import { suggestedCloudUrlFromMismatch } from '../dist/runtime/utils/convexDashboard.js';
5
5
  import { warnStaleLocalConvexUrl } from '../dist/runtime/utils/errors.js';
6
+ export { configureConvexAuth } from '../dist/runtime/utils/authClientOptions.js';
6
7
 
7
8
  const module$1 = defineNuxtModule({
8
9
  meta: {
@@ -18,10 +19,35 @@ const module$1 = defineNuxtModule({
18
19
  },
19
20
  setup(options, nuxt) {
20
21
  const existing = nuxt.options.runtimeConfig.public.convex;
21
- const auth = options.auth ? {
22
- ...options.auth,
23
- cookie: resolveAuthCookieName(options.auth) ?? options.auth.cookie
24
- } : options.auth;
22
+ const {
23
+ signIn: authSignIn,
24
+ allowedOrigins: authAllowedOrigins,
25
+ cookieOptions: authCookieOptions,
26
+ ...publicAuth
27
+ } = options.auth ?? {};
28
+ const auth = options.auth ? publicAuth : void 0;
29
+ if (auth && isConvexAuth(auth)) {
30
+ for (const legacy of ["httpOnly", "presentCookie", "shouldHandleCode"]) {
31
+ if (legacy in auth) {
32
+ console.warn(
33
+ `[use-convex] convex.auth.${legacy} was removed with Convex Auth v2 support. See the README migration notes.`
34
+ );
35
+ }
36
+ }
37
+ if (resolveAuthMode(auth) === "ssr" && !authSignIn?.length) {
38
+ console.warn(
39
+ "[use-convex] convex.auth.signIn is empty: the SSR auth proxy will refuse every sign-in. List your session-issuing functions, e.g. ['auth:signInWithUsernamePassword']."
40
+ );
41
+ }
42
+ }
43
+ nuxt.options.runtimeConfig.convexAuth = defu(
44
+ nuxt.options.runtimeConfig.convexAuth ?? {},
45
+ {
46
+ signIn: authSignIn ?? [],
47
+ allowedOrigins: authAllowedOrigins ?? [],
48
+ cookie: authCookieOptions ?? {}
49
+ }
50
+ );
25
51
  nuxt.options.runtimeConfig.public.convex = defu(existing ?? {}, {
26
52
  url: options.url,
27
53
  server: options.server,
@@ -49,7 +75,8 @@ const module$1 = defineNuxtModule({
49
75
  }
50
76
  const resolver = createResolver(import.meta.url);
51
77
  const runtimeDir = resolver.resolve("./runtime");
52
- const useConvexAuthProvider = auth?.provider === "convex-auth";
78
+ const useConvexAuthProvider = isConvexAuth(auth);
79
+ const useSsrConvexAuth = useConvexAuthProvider && resolveAuthMode(auth) === "ssr";
53
80
  nuxt.options.build.transpile.push(runtimeDir);
54
81
  nuxt.hooks.hook("nitro:config", (nitroConfig) => {
55
82
  nitroConfig.externals ||= {};
@@ -90,10 +117,24 @@ const module$1 = defineNuxtModule({
90
117
  });
91
118
  }
92
119
  addServerScanDir(resolver.resolve("./runtime/server"));
93
- addServerHandler({
94
- route: "/api/convex/auth/session",
95
- handler: resolver.resolve("./runtime/server/api/convex/auth/session")
96
- });
120
+ if (useSsrConvexAuth) {
121
+ const routes = resolveAuthRoutes(auth);
122
+ for (const [route, file] of [
123
+ [routes.signIn, "signin"],
124
+ [routes.refresh, "refresh"],
125
+ [routes.signOut, "signout"]
126
+ ]) {
127
+ addServerHandler({
128
+ route,
129
+ method: "post",
130
+ handler: resolver.resolve(`./runtime/server/convexAuth/${file}`)
131
+ });
132
+ }
133
+ addServerHandler({
134
+ middleware: true,
135
+ handler: resolver.resolve("./runtime/server/convexAuth/session.middleware")
136
+ });
137
+ }
97
138
  if (nuxt.options.dev) {
98
139
  addServerHandler({
99
140
  route: "/__convex_devtools",
@@ -179,12 +220,28 @@ const module$1 = defineNuxtModule({
179
220
  from: resolver.resolve("./runtime/composables/useAuth")
180
221
  },
181
222
  {
182
- name: "signIn",
223
+ name: "useConvexSignIn",
183
224
  from: resolver.resolve("./runtime/composables/useAuth")
184
225
  },
226
+ {
227
+ name: "useOAuth",
228
+ from: resolver.resolve("./runtime/composables/useAuth")
229
+ },
230
+ {
231
+ name: "useOAuthSignIn",
232
+ from: resolver.resolve("./runtime/composables/useAuth")
233
+ },
234
+ {
235
+ name: "convexOAuth",
236
+ from: resolver.resolve("./runtime/auth/oauth")
237
+ },
185
238
  {
186
239
  name: "signOut",
187
240
  from: resolver.resolve("./runtime/composables/useAuth")
241
+ },
242
+ {
243
+ name: "configureConvexAuth",
244
+ from: resolver.resolve("./runtime/utils/authClientOptions")
188
245
  }
189
246
  ] : []
190
247
  ]);
@@ -224,6 +281,7 @@ const module$1 = defineNuxtModule({
224
281
  });
225
282
  }
226
283
  const contextTypes = resolver.resolve("./runtime/utils/context");
284
+ const authTypes = resolver.resolve("./runtime/utils/authStorage");
227
285
  addTypeTemplate({
228
286
  filename: "types/convex-nuxt.d.ts",
229
287
  getContents: () => `// Generated by use-convex
@@ -235,14 +293,12 @@ declare module '@nuxt/schema' {
235
293
  url?: string
236
294
  server?: boolean
237
295
  client?: import('convex/browser').ConvexClientOptions
238
- auth?: {
239
- provider?: 'convex-auth'
240
- cookie?: string
241
- httpOnly?: boolean
242
- presentCookie?: string
243
- }
296
+ auth?: import('${authTypes}').ConvexAuthConfig
244
297
  }
245
298
  }
299
+ interface RuntimeConfig {
300
+ convexAuth: import('${authTypes}').ConvexAuthServerRuntimeConfig
301
+ }
246
302
  }
247
303
 
248
304
  declare module 'nuxt/app' {
@@ -0,0 +1,96 @@
1
+ /**
2
+ * OAuth ambient sign-in for Convex Auth v2.
3
+ *
4
+ * Port of `@convex-dev/auth`'s framework-agnostic OAuth client, which the
5
+ * package only ships behind its React entry point. Same wire contract:
6
+ * `startSignIn({ redirectTo })` → `{ redirect, state }`; the state is kept in
7
+ * the sign-in's storage, and the callback's `convexAuthCode` is redeemed with
8
+ * `completeSignIn({ code, state })` through the sign-in API (the auth proxy
9
+ * under SSR).
10
+ */
11
+ import type { AuthClientConfig } from '@convex-dev/auth/browser';
12
+ import { type FunctionReference } from 'convex/server';
13
+ type AmbientSignIns = NonNullable<AuthClientConfig['ambientSignIns']>;
14
+ export type AmbientSignInClient = AmbientSignIns['signIns'][number];
15
+ export type AmbientSignInContext = Parameters<AmbientSignInClient['setup']>[0];
16
+ /** Callback params written by the Convex Auth OAuth component. */
17
+ export declare const OAUTH_CODE_PARAM = "convexAuthCode";
18
+ export declare const OAUTH_ERROR_PARAM = "convexAuthError";
19
+ export declare const OAUTH_SETUP_ID = "oauth";
20
+ export declare const OAUTH_ACTIONS_KEY = "actions";
21
+ export declare const OAUTH_FLOW_ERROR_KEY = "flowError";
22
+ /** A provider's `startSignIn*` / `completeSignIn*` mutations plus its name. */
23
+ export type OAuthProviderRefs = {
24
+ /** Label only, e.g. `'github'`. */
25
+ providerName: string;
26
+ startSignIn: FunctionReference<'mutation', 'public', {
27
+ redirectTo: string;
28
+ }, {
29
+ redirect: string;
30
+ state: string;
31
+ }>;
32
+ completeSignIn: FunctionReference<'mutation', 'public', {
33
+ code: string;
34
+ state: string;
35
+ }, any>;
36
+ };
37
+ /**
38
+ * Why a flow failed after the identity provider sent the user back.
39
+ * Switch on `error` and supply your own copy.
40
+ */
41
+ export type OAuthFlowError = {
42
+ error: 'ACCESS_DENIED';
43
+ } | {
44
+ error: 'EXPIRED';
45
+ } | {
46
+ error: 'INVALID_FLOW';
47
+ } | {
48
+ error: 'OTHER_ERROR';
49
+ cause: unknown;
50
+ };
51
+ export type OAuthStartResult = {
52
+ status: 'redirect';
53
+ redirect: URL;
54
+ } | {
55
+ status: 'error';
56
+ userError: {
57
+ error: 'OTHER_ERROR';
58
+ cause: unknown;
59
+ };
60
+ };
61
+ export type OAuthCompleteResult = {
62
+ status: 'complete';
63
+ } | {
64
+ status: 'error';
65
+ userError: OAuthFlowError;
66
+ };
67
+ export type OAuthSignInOptions = {
68
+ /** Where the provider sends the user back. Defaults to the current URL. */
69
+ redirectTo?: string;
70
+ /** Finish a flow started elsewhere instead of starting one. */
71
+ code?: string;
72
+ };
73
+ export type OAuthActions = {
74
+ signIn: (refs: OAuthProviderRefs, options?: OAuthSignInOptions) => Promise<OAuthStartResult | OAuthCompleteResult>;
75
+ };
76
+ /**
77
+ * The OAuth ambient sign-in. Registered by default; pass it explicitly via
78
+ * `configureConvexAuth({ ambientSignIns: [convexOAuth(), …] })` when adding
79
+ * others.
80
+ */
81
+ export type ConvexOAuthOptions = {
82
+ /**
83
+ * Where to keep the pending flow across the provider redirect, when the
84
+ * session token storage does not survive navigation (e.g. in-memory
85
+ * tokens). Defaults to the client's token storage.
86
+ */
87
+ flowStorage?: {
88
+ getItem: (key: string) => string | null | undefined | Promise<string | null | undefined>;
89
+ setItem: (key: string, value: string) => void | Promise<void>;
90
+ removeItem: (key: string) => void | Promise<void>;
91
+ };
92
+ /** Key namespace for `flowStorage`, typically the deployment URL. */
93
+ namespace?: string;
94
+ };
95
+ export declare function convexOAuth(options?: ConvexOAuthOptions): AmbientSignInClient;
96
+ export {};