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.
- package/README.md +104 -59
- package/dist/module.d.mts +62 -39
- package/dist/module.mjs +68 -17
- package/dist/runtime/auth/oauth.d.ts +96 -0
- package/dist/runtime/auth/oauth.js +170 -0
- package/dist/runtime/auth/sessionRoutes.d.ts +37 -0
- package/dist/runtime/auth/sessionRoutes.js +41 -0
- package/dist/runtime/composables/useAuth.d.ts +81 -48
- package/dist/runtime/composables/useAuth.js +69 -461
- package/dist/runtime/composables/useAuthToken.d.ts +2 -2
- package/dist/runtime/composables/useAuthToken.js +4 -0
- package/dist/runtime/composables/useConvexAuth.js +1 -1
- package/dist/runtime/composables/useConvexPaginatedQuery.js +6 -1
- package/dist/runtime/composables/useConvexQuery.js +6 -1
- package/dist/runtime/plugin.auth.client.d.ts +8 -3
- package/dist/runtime/plugin.auth.client.js +207 -31
- package/dist/runtime/plugin.server.js +8 -2
- package/dist/runtime/server/auth.d.ts +7 -2
- package/dist/runtime/server/auth.js +14 -3
- package/dist/runtime/server/convexAuth/refresh.d.ts +3 -0
- package/dist/runtime/server/convexAuth/refresh.js +5 -0
- package/dist/runtime/server/convexAuth/session.middleware.d.ts +10 -0
- package/dist/runtime/server/convexAuth/session.middleware.js +37 -0
- package/dist/runtime/server/convexAuth/setup.d.ts +21 -0
- package/dist/runtime/server/convexAuth/setup.js +90 -0
- package/dist/runtime/server/convexAuth/signin.d.ts +3 -0
- package/dist/runtime/server/convexAuth/signin.js +5 -0
- package/dist/runtime/server/convexAuth/signout.d.ts +3 -0
- package/dist/runtime/server/convexAuth/signout.js +5 -0
- package/dist/runtime/server/convexConfig.d.ts +2 -6
- package/dist/runtime/server/fetch.d.ts +2 -1
- package/dist/runtime/server/fetch.js +3 -8
- package/dist/runtime/utils/authClientOptions.d.ts +33 -40
- package/dist/runtime/utils/authClientOptions.js +1 -64
- package/dist/runtime/utils/authCookie.d.ts +8 -14
- package/dist/runtime/utils/authCookie.js +23 -27
- package/dist/runtime/utils/authStorage.d.ts +76 -79
- package/dist/runtime/utils/authStorage.js +60 -141
- package/dist/runtime/utils/context.d.ts +40 -3
- package/dist/types.d.mts +4 -2
- package/package.json +10 -5
- package/dist/runtime/server/api/convex/auth/session.d.ts +0 -36
- package/dist/runtime/server/api/convex/auth/session.js +0 -90
- package/dist/runtime/server/authCookies.d.ts +0 -9
- package/dist/runtime/server/authCookies.js +0 -64
- package/dist/runtime/server/sameOrigin.d.ts +0 -14
- package/dist/runtime/server/sameOrigin.js +0 -29
- package/dist/runtime/utils/authMutex.d.ts +0 -15
- package/dist/runtime/utils/authMutex.js +0 -47
- package/dist/runtime/utils/oauthRedirect.d.ts +0 -5
- 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`.
|
|
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
|
-
|
|
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
|
-
//
|
|
203
|
-
|
|
204
|
-
//
|
|
205
|
-
//
|
|
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
|
-
|
|
226
|
+
**Session models**
|
|
211
227
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
269
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
-
//
|
|
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
|
|
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 {
|
|
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`)
|
|
21
|
+
* Opt-in first-party Convex Auth v2 (`@convex-dev/auth@^2`).
|
|
21
22
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* `
|
|
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
|
-
*
|
|
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
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
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
|
-
|
|
44
|
+
mode?: 'ssr' | 'spa';
|
|
44
45
|
/**
|
|
45
|
-
*
|
|
46
|
-
* (
|
|
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
|
|
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
|
-
|
|
63
|
+
signIn?: string[];
|
|
58
64
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
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
|
-
|
|
69
|
+
routePrefix?: string;
|
|
63
70
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
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
|
|
71
|
-
*
|
|
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
|
-
*
|
|
78
|
-
*
|
|
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
|
-
|
|
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 {
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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: "
|
|
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 {};
|