use-convex 1.3.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 (51) hide show
  1. package/README.md +104 -59
  2. package/dist/module.d.mts +62 -39
  3. package/dist/module.mjs +68 -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 -461
  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/useConvexQuery.js +6 -1
  15. package/dist/runtime/plugin.auth.client.d.ts +8 -3
  16. package/dist/runtime/plugin.auth.client.js +207 -31
  17. package/dist/runtime/plugin.server.js +8 -2
  18. package/dist/runtime/server/auth.d.ts +7 -2
  19. package/dist/runtime/server/auth.js +14 -3
  20. package/dist/runtime/server/convexAuth/refresh.d.ts +3 -0
  21. package/dist/runtime/server/convexAuth/refresh.js +5 -0
  22. package/dist/runtime/server/convexAuth/session.middleware.d.ts +10 -0
  23. package/dist/runtime/server/convexAuth/session.middleware.js +37 -0
  24. package/dist/runtime/server/convexAuth/setup.d.ts +21 -0
  25. package/dist/runtime/server/convexAuth/setup.js +90 -0
  26. package/dist/runtime/server/convexAuth/signin.d.ts +3 -0
  27. package/dist/runtime/server/convexAuth/signin.js +5 -0
  28. package/dist/runtime/server/convexAuth/signout.d.ts +3 -0
  29. package/dist/runtime/server/convexAuth/signout.js +5 -0
  30. package/dist/runtime/server/convexConfig.d.ts +2 -6
  31. package/dist/runtime/server/fetch.d.ts +2 -1
  32. package/dist/runtime/server/fetch.js +3 -8
  33. package/dist/runtime/utils/authClientOptions.d.ts +33 -40
  34. package/dist/runtime/utils/authClientOptions.js +1 -64
  35. package/dist/runtime/utils/authCookie.d.ts +8 -14
  36. package/dist/runtime/utils/authCookie.js +23 -27
  37. package/dist/runtime/utils/authStorage.d.ts +76 -79
  38. package/dist/runtime/utils/authStorage.js +60 -141
  39. package/dist/runtime/utils/context.d.ts +40 -3
  40. package/dist/types.d.mts +4 -2
  41. package/package.json +10 -5
  42. package/dist/runtime/server/api/convex/auth/session.d.ts +0 -36
  43. package/dist/runtime/server/api/convex/auth/session.js +0 -90
  44. package/dist/runtime/server/authCookies.d.ts +0 -9
  45. package/dist/runtime/server/authCookies.js +0 -64
  46. package/dist/runtime/server/sameOrigin.d.ts +0 -14
  47. package/dist/runtime/server/sameOrigin.js +0 -29
  48. package/dist/runtime/utils/authMutex.d.ts +0 -15
  49. package/dist/runtime/utils/authMutex.js +0 -47
  50. package/dist/runtime/utils/oauthRedirect.d.ts +0 -5
  51. 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
 
@@ -133,6 +133,12 @@ const { run, pending, error } = useConvexAction(api.tasks.shout)
133
133
  const shouted = await run({ text: 'hello' }) // browser-only
134
134
  ```
135
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
+
136
142
  For paginated lists:
137
143
 
138
144
  ```ts
@@ -190,54 +196,64 @@ That runs `generateUploadUrl` → `PUT` to the signed URL → `syncMetadata({ ke
190
196
 
191
197
  ## Auth
192
198
 
193
- ### Convex Auth
199
+ ### Convex Auth (v2)
194
200
 
195
- Opt in with `provider: 'convex-auth'`. The module hydrates tokens, finishes OAuth `?code=` callbacks, and wires `setAuth`. Forms only call `signIn` / `signOut`.
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`.
202
+
203
+ ```bash
204
+ npm i @convex-dev/auth
205
+ ```
196
206
 
197
207
  ```ts
198
208
  convex: {
199
209
  url: process.env.NUXT_PUBLIC_CONVEX_URL,
200
210
  auth: {
201
211
  provider: 'convex-auth',
202
- // cookie defaults to 'convex_jwt'
203
- // storageNamespace: 'myapp', // optional; default = deployment URL
204
- // storage: 'localStorage', // or 'inMemory' (verifier uses a short-lived cookie)
205
- // shouldHandleCode: true, // set false to ignore ?code=
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,
206
222
  },
207
223
  }
208
224
  ```
209
225
 
210
- For non-default OAuth / callback flows (JS router `replaceURL`, function `shouldHandleCode`, custom `TokenStorage`), call `configureConvexAuth` from a client plugin that runs before the auth plugin:
226
+ **Session models**
211
227
 
212
- ```ts
213
- // plugins/convex-auth-options.client.ts
214
- export default defineNuxtPlugin({
215
- name: 'convex-auth-options',
216
- enforce: 'pre',
217
- setup() {
218
- const route = useRoute()
219
- configureConvexAuth({
220
- replaceURL: (url) => navigateTo(url, { replace: true }),
221
- shouldHandleCode: () => route.path === '/auth/callback',
222
- // storage: window.sessionStorage, // may be async; signIn waits before redirect
223
- // storageNamespace: 'myapp',
224
- })
225
- },
226
- })
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`).
228
234
 
229
- `storage: 'inMemory'` keeps JWTs in memory. The OAuth verifier is a short-lived cookie so the `?code=` callback still works after the IdP redirect. Custom storage may return Promises; `signIn` waits for writes before navigating.
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:
230
236
 
231
237
  ```vue
232
238
  <script setup lang="ts">
233
- 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
+ }
234
250
  </script>
235
251
 
236
252
  <template>
237
253
  <AuthLoading>Resolving…</AuthLoading>
238
254
  <AuthRefreshing>Refreshing session…</AuthRefreshing>
239
255
  <Authenticated>
240
- <!-- signed-in shell -->
256
+ <button @click="signOut()">Sign out</button>
241
257
  </Authenticated>
242
258
  <Unauthenticated>
243
259
  <!-- sign-in form -->
@@ -245,32 +261,56 @@ const { signIn, signOut } = useAuth()
245
261
  </template>
246
262
  ```
247
263
 
248
- 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`:
249
267
 
250
268
  ```ts
251
- await signIn('password', { email, password, flow: 'signIn' })
252
- await signIn('github') // OAuth redirect; the plugin finishes on ?code=
253
- 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
254
277
  ```
255
278
 
256
- 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.
257
280
 
258
- - `convex/auth.ts` — `convexAuth({ providers })` exporting `auth`, `signIn`, `signOut`
259
- - `convex/schema.ts` — spread `authTables`
260
- - `convex/http.ts` — `auth.addHttpRoutes(http)` (required for OAuth / magic links)
261
- - `convex/auth.config.ts` — JWT issuer (`CONVEX_SITE_URL`)
262
- - 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 }`.
263
282
 
264
- 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:
265
284
 
266
285
  ```ts
267
- auth: {
268
- provider: 'convex-auth',
269
- httpOnly: true, // JWT + refresh via Nitro `/api/convex/auth/session`
270
- }
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
+ })
271
297
  ```
272
298
 
273
- 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`.
274
314
 
275
315
  ### SSR query budget
276
316
 
@@ -321,7 +361,7 @@ auth: {
321
361
 
322
362
  ## Server routes
323
363
 
324
- 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):
325
365
 
326
366
  ```ts
327
367
  // server/api/tasks.get.ts
@@ -351,6 +391,8 @@ export default defineEventHandler(async (event) => {
351
391
 
352
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.
353
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).
395
+
354
396
  ## Config
355
397
 
356
398
  ```ts
@@ -359,13 +401,17 @@ convex: {
359
401
  server: true, // SSR snapshots; override per query with { server }
360
402
  client: { unsavedChangesWarning: false }, // forwarded to new ConvexClient()
361
403
  auth: {
362
- provider: 'convex-auth',
363
- cookie: 'convex_jwt',
364
- httpOnly: false,
365
- presentCookie: 'convex_auth_present',
366
- storage: 'localStorage', // or 'inMemory'
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'
367
412
  // storageNamespace: 'myapp',
368
- // shouldHandleCode: true,
413
+ // verbose: false,
414
+ // cookie: 'convex_jwt', // BYO providers only
369
415
  },
370
416
  }
371
417
  ```
@@ -394,21 +440,20 @@ pnpm install
394
440
 
395
441
  # Terminal 1 — Convex backend (writes CONVEX_URL to playground/.env.local)
396
442
  pnpm run dev:backend
397
- # first time: npx @convex-dev/auth # JWT_PRIVATE_KEY + JWKS
398
443
 
399
444
  # Terminal 2 — Nuxt
400
445
  pnpm run dev
401
446
  ```
402
447
 
403
- | Route | What it shows |
404
- | --------- | ----------------------------------------------------- |
405
- | `/` | Shell session: features + composable call-shape demos |
406
- | `/live` | SSR snapshot + live overlay (sign up, CRUD todos) |
407
- | `/server` | Nitro `fetchQuery` / `fetchMutation` / `fetchAction` |
408
- | `/files` | `useConvexFileUpload` (upload, list, preview, delete) |
409
- | `/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 |
410
455
 
411
- 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.
412
457
 
413
458
  ## Releasing
414
459
 
package/dist/module.d.mts CHANGED
@@ -1,6 +1,6 @@
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';
@@ -13,71 +13,94 @@ 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 { ConvexAuthClientOptions, ConvexAuthStorageMode, ConvexAuthTokenStorage, configureConvexAuth } from '../dist/runtime/utils/authClientOptions.js';
16
+ export { ConvexAmbientSignIn, ConvexAuthClientOptions, ConvexAuthStorageMode, ConvexAuthTokenStorage, configureConvexAuth } from '../dist/runtime/utils/authClientOptions.js';
17
+ export { ConvexAuthServerRuntimeConfig } from '../dist/runtime/utils/authStorage.js';
17
18
 
18
19
  interface ModuleAuthOptions {
19
20
  /**
20
- * Opt-in first-party Convex Auth (`@convex-dev/auth`) client.
21
+ * Opt-in first-party Convex Auth v2 (`@convex-dev/auth@^2`).
21
22
  *
22
- * When set to `'convex-auth'`, the module registers a client plugin that
23
- * hydrates tokens, handles OAuth `?code=` callbacks, and wires
24
- * `useConvexAuth({ fetchToken })`. It also auto-imports `useAuth`,
25
- * `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.
26
28
  *
27
29
  * Omit for bring-your-own providers (Clerk, Auth0, custom) — wire
28
30
  * `useConvexAuth({ fetchToken })` yourself.
29
31
  */
30
32
  provider?: 'convex-auth';
31
33
  /**
32
- * Cookie name whose value is a JWT used for authenticated SSR queries.
33
- * When set, the server plugin reads this cookie into `ctx.ssrToken` and
34
- * `useConvexQuery` falls back to it when `options.token` is omitted.
35
- * `useAuth` / `useConvexAuth` expose the presence of this cookie as
36
- * `hasSsrSession`.
37
- * Nitro `fetch*` helpers also read this cookie when `{ event }` is passed.
34
+ * Convex Auth session model.
38
35
  *
39
- * Defaults to `'convex_jwt'` when `provider === 'convex-auth'` and
40
- * `httpOnly` is off. With `httpOnly: true`, defaults to `__convexAuthJWT`.
41
- * 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'
42
43
  */
43
- cookie?: string;
44
+ mode?: 'ssr' | 'spa';
44
45
  /**
45
- * Store JWT + refresh tokens in HttpOnly cookies via Nitro
46
- * (`/api/convex/auth/session`). Cookies are not readable via
47
- * `document.cookie`. Prefer `true` in production.
48
- *
49
- * Note: ConvexClient still needs the JWT in memory for the WebSocket;
50
- * the session route returns it only via same-origin POST `{ getToken: true }`.
51
- * HttpOnly mitigates cookie theft, not XSS session theft. The readable
52
- * presence cookie drives `hasSsrSession` / `showAuthedUi` (UI only — gate
53
- * private queries with `{ authenticated: true }`).
46
+ * Names of the session functions exported from your Convex auth file
47
+ * (the result of `setupConvexAuth`).
54
48
  *
55
- * @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`).
56
62
  */
57
- httpOnly?: boolean;
63
+ signIn?: string[];
58
64
  /**
59
- * Readable presence cookie when `httpOnly` is enabled.
60
- * @default 'convex_auth_present'
65
+ * SSR mode: path prefix for the Nitro auth routes
66
+ * (`<prefix>/signin`, `<prefix>/refresh`, `<prefix>/signout`).
67
+ * @default '/api/convex/auth'
61
68
  */
62
- presentCookie?: string;
69
+ routePrefix?: string;
63
70
  /**
64
- * Namespace for local token keys (React `storageNamespace` parity).
65
- * Non-alphanumeric characters are stripped. Defaults to the deployment URL.
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).
73
+ */
74
+ allowedOrigins?: string[];
75
+ /**
76
+ * SSR mode: auth cookie attributes. `httpOnly`, `sameSite` and lifetimes
77
+ * are fixed by Convex Auth. `secure` defaults to true except on localhost.
78
+ */
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.
66
87
  */
67
88
  storageNamespace?: string;
68
89
  /**
69
90
  * Client token storage. `'localStorage'` (default) or `'inMemory'`.
70
- * For a custom TokenStorage or `replaceURL` / function `shouldHandleCode`,
71
- * call `configureConvexAuth` from a client plugin with `enforce: 'pre'`.
72
- * Ignored when `httpOnly` is enabled.
91
+ * For a custom TokenStorage, call `configureConvexAuth({ storage })` from a
92
+ * client plugin with `enforce: 'pre'`.
73
93
  * @default 'localStorage'
74
94
  */
75
95
  storage?: 'localStorage' | 'inMemory';
96
+ /** Log Convex Auth refresh / lifecycle steps to the console. */
97
+ verbose?: boolean;
76
98
  /**
77
- * When `false`, do not consume `?code=` OAuth callbacks.
78
- * For a function gate, use `configureConvexAuth({ shouldHandleCode })`.
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'`.
79
102
  */
80
- shouldHandleCode?: boolean;
103
+ cookie?: string;
81
104
  }
82
105
  interface ModuleOptions {
83
106
  /**
package/dist/module.mjs CHANGED
@@ -1,6 +1,6 @@
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
6
  export { configureConvexAuth } from '../dist/runtime/utils/authClientOptions.js';
@@ -19,10 +19,35 @@ const module$1 = defineNuxtModule({
19
19
  },
20
20
  setup(options, nuxt) {
21
21
  const existing = nuxt.options.runtimeConfig.public.convex;
22
- const auth = options.auth ? {
23
- ...options.auth,
24
- cookie: resolveAuthCookieName(options.auth) ?? options.auth.cookie
25
- } : 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
+ );
26
51
  nuxt.options.runtimeConfig.public.convex = defu(existing ?? {}, {
27
52
  url: options.url,
28
53
  server: options.server,
@@ -50,7 +75,8 @@ const module$1 = defineNuxtModule({
50
75
  }
51
76
  const resolver = createResolver(import.meta.url);
52
77
  const runtimeDir = resolver.resolve("./runtime");
53
- const useConvexAuthProvider = auth?.provider === "convex-auth";
78
+ const useConvexAuthProvider = isConvexAuth(auth);
79
+ const useSsrConvexAuth = useConvexAuthProvider && resolveAuthMode(auth) === "ssr";
54
80
  nuxt.options.build.transpile.push(runtimeDir);
55
81
  nuxt.hooks.hook("nitro:config", (nitroConfig) => {
56
82
  nitroConfig.externals ||= {};
@@ -91,10 +117,24 @@ const module$1 = defineNuxtModule({
91
117
  });
92
118
  }
93
119
  addServerScanDir(resolver.resolve("./runtime/server"));
94
- addServerHandler({
95
- route: "/api/convex/auth/session",
96
- handler: resolver.resolve("./runtime/server/api/convex/auth/session")
97
- });
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
+ }
98
138
  if (nuxt.options.dev) {
99
139
  addServerHandler({
100
140
  route: "/__convex_devtools",
@@ -180,9 +220,21 @@ const module$1 = defineNuxtModule({
180
220
  from: resolver.resolve("./runtime/composables/useAuth")
181
221
  },
182
222
  {
183
- name: "signIn",
223
+ name: "useConvexSignIn",
224
+ from: resolver.resolve("./runtime/composables/useAuth")
225
+ },
226
+ {
227
+ name: "useOAuth",
184
228
  from: resolver.resolve("./runtime/composables/useAuth")
185
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
+ },
186
238
  {
187
239
  name: "signOut",
188
240
  from: resolver.resolve("./runtime/composables/useAuth")
@@ -229,6 +281,7 @@ const module$1 = defineNuxtModule({
229
281
  });
230
282
  }
231
283
  const contextTypes = resolver.resolve("./runtime/utils/context");
284
+ const authTypes = resolver.resolve("./runtime/utils/authStorage");
232
285
  addTypeTemplate({
233
286
  filename: "types/convex-nuxt.d.ts",
234
287
  getContents: () => `// Generated by use-convex
@@ -240,14 +293,12 @@ declare module '@nuxt/schema' {
240
293
  url?: string
241
294
  server?: boolean
242
295
  client?: import('convex/browser').ConvexClientOptions
243
- auth?: {
244
- provider?: 'convex-auth'
245
- cookie?: string
246
- httpOnly?: boolean
247
- presentCookie?: string
248
- }
296
+ auth?: import('${authTypes}').ConvexAuthConfig
249
297
  }
250
298
  }
299
+ interface RuntimeConfig {
300
+ convexAuth: import('${authTypes}').ConvexAuthServerRuntimeConfig
301
+ }
251
302
  }
252
303
 
253
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 {};