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
@@ -1,3 +1,4 @@
1
+ import { sessionKeyFromToken } from "../utils/authStorage.js";
1
2
  import { convexToJson, jsonToConvex } from "convex/values";
2
3
  import { useAsyncData, useRuntimeConfig } from "nuxt/app";
3
4
  import {
@@ -47,7 +48,11 @@ export async function useConvexQuery(query, args = {}, options = {}) {
47
48
  () => toValue(args),
48
49
  // Re-run HttpClient snapshot when the SSR JWT cookie / token changes
49
50
  // (e.g. after sign-in with live:false, or Refresh).
50
- () => options.token ?? ctx.ssrToken.value,
51
+ // Session identity (JWT iss + sub), not the raw token: Convex Auth
52
+ // rotates the access token every minute without changing session,
53
+ // but switching accounts must re-run the snapshot (`live: false`
54
+ // and BYO cookies included).
55
+ () => sessionKeyFromToken(options.token ?? ctx.ssrToken.value),
51
56
  // Re-run when auth confirms *and* a JWT is available for HttpClient
52
57
  // (readable cookie / SSR). Avoids anonymous client refetches with HttpOnly.
53
58
  () => requireAuth && ctx.auth.isAuthenticated.value && !!(options.token ?? ctx.ssrToken.value)
@@ -1,8 +1,13 @@
1
1
  /**
2
- * First-party Convex Auth wiring (opt-in via `convex.auth.provider: 'convex-auth'`).
2
+ * First-party Convex Auth v2 wiring (`convex.auth.provider: 'convex-auth'`).
3
3
  *
4
- * Must stay synchronous through `useConvexAuth` / `inject` — awaiting first
5
- * drops Vue setup context and caused the "plugin did not start" 500.
4
+ * Builds an `AuthClient` from `@convex-dev/auth/browser` in SSR mode (refresh
5
+ * token in an httpOnly cookie, sign-ins through the Nitro auth proxy) or SPA
6
+ * mode (tokens in browser storage, sign-ins straight to the deployment),
7
+ * then hands its token to `useConvexAuth` and watches `isAuthenticated` so a
8
+ * session revoked server-side signs the client out.
9
+ *
10
+ * Setup stays synchronous so `useConvexAuth` keeps the Nuxt app context.
6
11
  */
7
12
  declare const _default: import("nuxt/app").Plugin<Record<string, unknown>> & import("nuxt/app").ObjectPlugin<Record<string, unknown>>;
8
13
  export default _default;
@@ -1,41 +1,226 @@
1
+ import { AuthClient, InMemoryStorage, defaultStorage } from "@convex-dev/auth/browser";
2
+ import { ConvexHttpClient } from "convex/browser";
3
+ import { makeFunctionReference } from "convex/server";
1
4
  import { defineNuxtPlugin, useRuntimeConfig } from "nuxt/app";
5
+ import { readonly, shallowRef, watch } from "vue";
2
6
  import { useConvexAuth } from "./composables/useConvexAuth.js";
3
7
  import {
4
- consumeOAuthCodeFromUrl,
5
- getAuthToken,
6
- hasPendingOAuthCallback,
7
- hydrateAuthFromStorage,
8
- useAuthProviderState
9
- } from "./composables/useAuth.js";
10
- import { tryUseConvexContext } from "./utils/context.js";
8
+ OAUTH_CODE_PARAM,
9
+ OAUTH_ERROR_PARAM,
10
+ convexOAuth
11
+ } from "./auth/oauth.js";
12
+ import { postAuth, retryingTokenFetcher } from "./auth/sessionRoutes.js";
13
+ import { getConvexAuthClientOptions } from "./utils/authClientOptions.js";
14
+ import { useConvexAuthTokenState } from "./utils/authCookie.js";
15
+ import {
16
+ isConvexAuth,
17
+ resolveAuthApiNames,
18
+ resolveAuthMode,
19
+ resolveAuthRoutes
20
+ } from "./utils/authStorage.js";
21
+ import {
22
+ tryUseConvexContext
23
+ } from "./utils/context.js";
24
+ function sessionStorageOrUndefined() {
25
+ try {
26
+ return typeof window !== "undefined" ? window.sessionStorage : void 0;
27
+ } catch {
28
+ return void 0;
29
+ }
30
+ }
11
31
  export default defineNuxtPlugin({
12
32
  name: "convex-nuxt-auth",
13
33
  dependsOn: ["convex-nuxt-client"],
14
- setup() {
34
+ setup(nuxtApp) {
15
35
  const config = useRuntimeConfig();
16
36
  const convexConfig = config.public.convex;
17
- if (!convexConfig?.url) {
37
+ const auth = convexConfig?.auth;
38
+ if (!convexConfig?.url || !isConvexAuth(auth)) {
18
39
  return;
19
40
  }
20
- if (!tryUseConvexContext()) {
41
+ const ctx = tryUseConvexContext();
42
+ const convex = ctx?.client;
43
+ if (!ctx || !convex) {
21
44
  return;
22
45
  }
23
- const pendingOAuth = hasPendingOAuthCallback();
24
- if (!pendingOAuth) {
25
- hydrateAuthFromStorage();
26
- }
27
- const { isLoading, hasSession } = useAuthProviderState();
28
- useConvexAuth({
29
- fetchToken: ({ forceRefreshToken }) => getAuthToken({ forceRefreshToken }),
30
- isLoading,
31
- isAuthenticated: hasSession
46
+ const mode = resolveAuthMode(auth);
47
+ const apiNames = resolveAuthApiNames(auth);
48
+ const baseURL = config.app?.baseURL ?? "/";
49
+ const routes = resolveAuthRoutes(auth);
50
+ const route = (path) => baseURL.replace(/\/+$/, "") + path;
51
+ const options = getConvexAuthClientOptions();
52
+ const tokenState = useConvexAuthTokenState();
53
+ const storage = options.storage ?? (auth?.storage === "inMemory" ? new InMemoryStorage() : defaultStorage());
54
+ const storageNamespace = auth?.storageNamespace ?? ctx.url;
55
+ const replaceUrl = options.replaceUrl ?? ((url) => {
56
+ window.history.replaceState(window.history.state, "", url);
57
+ const router = nuxtApp.$router;
58
+ void router?.isReady().then(() => {
59
+ const current = router.currentRoute.value;
60
+ if (!(OAUTH_CODE_PARAM in current.query) && !(OAUTH_ERROR_PARAM in current.query)) {
61
+ return;
62
+ }
63
+ const query = { ...current.query };
64
+ delete query[OAUTH_CODE_PARAM];
65
+ delete query[OAUTH_ERROR_PARAM];
66
+ void router.replace({ path: current.path, query, hash: current.hash });
67
+ });
32
68
  });
33
- if (pendingOAuth) {
34
- void consumeOAuthCodeFromUrl().then((ok) => {
35
- if (!ok) {
36
- hydrateAuthFromStorage();
69
+ const flowStorage = !options.storage && auth?.storage === "inMemory" ? sessionStorageOrUndefined() : void 0;
70
+ const signIns = options.ambientSignIns ?? [
71
+ convexOAuth(flowStorage ? { flowStorage, namespace: storageNamespace } : {})
72
+ ];
73
+ const convexMutationApi = {
74
+ mutation: (fn, args) => convex.mutation(fn, args)
75
+ };
76
+ let authClient;
77
+ let signInApi;
78
+ if (mode === "ssr") {
79
+ const proxy = new ConvexHttpClient(`${route(routes.signIn)}?path=`, {
80
+ skipConvexDeploymentUrlCheck: true
81
+ });
82
+ const withAuth = () => {
83
+ const token = authClient.getAccessToken();
84
+ if (token !== null) {
85
+ proxy.setAuth(token);
86
+ } else {
87
+ proxy.clearAuth();
88
+ }
89
+ return proxy;
90
+ };
91
+ signInApi = {
92
+ mutation: (fn, args) => withAuth().mutation(fn, args),
93
+ action: (fn, args) => withAuth().action(fn, args)
94
+ };
95
+ authClient = new AuthClient({
96
+ mode: "ssr",
97
+ authApi: {
98
+ refreshSession: async () => (await postAuth(route(routes.refresh))).tokens,
99
+ signOut: async () => {
100
+ await postAuth(route(routes.signOut));
101
+ }
102
+ },
103
+ storage,
104
+ storageNamespace,
105
+ // The server refreshed on our behalf, so this is the freshest token.
106
+ initialAccessToken: tokenState.value,
107
+ verbose: auth?.verbose,
108
+ ambientSignIns: {
109
+ signIns,
110
+ signInApi,
111
+ convex: convexMutationApi,
112
+ replaceUrl
37
113
  }
38
114
  });
115
+ } else {
116
+ const http = new ConvexHttpClient(ctx.url);
117
+ const refreshSession = makeFunctionReference(apiNames.refreshSession);
118
+ const signOutRef = makeFunctionReference(apiNames.signOut);
119
+ signInApi = {
120
+ mutation: (fn, args) => convex.mutation(fn, args),
121
+ action: (fn, args) => convex.action(fn, args)
122
+ };
123
+ authClient = new AuthClient({
124
+ mode: "spa",
125
+ authApi: {
126
+ refreshSession: (refreshToken) => http.mutation(refreshSession, { refreshToken }),
127
+ signOut: async (refreshToken) => {
128
+ await http.mutation(signOutRef, { refreshToken });
129
+ }
130
+ },
131
+ storage,
132
+ storageNamespace,
133
+ verbose: auth?.verbose,
134
+ ambientSignIns: {
135
+ signIns,
136
+ signInApi,
137
+ convex: convexMutationApi,
138
+ replaceUrl
139
+ }
140
+ });
141
+ }
142
+ const state = shallowRef(authClient.getSnapshot());
143
+ authClient.subscribe(() => {
144
+ state.value = authClient.getSnapshot();
145
+ });
146
+ const syncTokenState = () => {
147
+ if (!state.value.isLoading) {
148
+ tokenState.value = state.value.token;
149
+ }
150
+ };
151
+ if (nuxtApp.isHydrating) {
152
+ nuxtApp.hooks.hookOnce("app:suspense:resolve", () => {
153
+ syncTokenState();
154
+ watch(state, syncTokenState);
155
+ });
156
+ } else {
157
+ watch(state, syncTokenState, { immediate: true });
39
158
  }
159
+ const ambientCache = /* @__PURE__ */ new Map();
160
+ const ambientValue = (id, key) => {
161
+ const cacheKey = `${id}:${key}`;
162
+ let cached = ambientCache.get(cacheKey);
163
+ if (!cached) {
164
+ const reader = authClient.ambientSignInValues(id);
165
+ const value = shallowRef(reader.get(key));
166
+ reader.subscribe(key, () => {
167
+ value.value = reader.get(key);
168
+ });
169
+ cached = readonly(value);
170
+ ambientCache.set(cacheKey, cached);
171
+ }
172
+ return cached;
173
+ };
174
+ const binding = {
175
+ mode,
176
+ state,
177
+ signInApi,
178
+ setSession: (session) => authClient.setSession(session),
179
+ signOut: () => authClient.signOut(),
180
+ ambientValue
181
+ };
182
+ ctx.convexAuth = binding;
183
+ const convexAuth = useConvexAuth({
184
+ // A refresh that fails (outage, proxy error) is retried with backoff
185
+ // rather than thrown into ConvexClient or treated as signed out.
186
+ fetchToken: retryingTokenFetcher((args) => authClient.fetchAccessToken(args), {
187
+ hasSession: () => authClient.getAccessToken() !== null,
188
+ onRetry: (error, delayMs) => {
189
+ if (import.meta.dev || auth?.verbose) {
190
+ console.warn(`[use-convex] Session refresh failed; retrying in ${delayMs}ms.`, error);
191
+ }
192
+ }
193
+ }),
194
+ isLoading: () => state.value.isLoading,
195
+ isAuthenticated: () => state.value.isAuthenticated
196
+ });
197
+ const isAuthenticatedRef = makeFunctionReference(
198
+ apiNames.isAuthenticated
199
+ );
200
+ let stopWatching = null;
201
+ watch(
202
+ () => convexAuth.isAuthenticated.value,
203
+ (authenticated) => {
204
+ stopWatching?.();
205
+ stopWatching = null;
206
+ if (!authenticated) {
207
+ return;
208
+ }
209
+ const unsubscribe = convex.onUpdate(
210
+ isAuthenticatedRef,
211
+ {},
212
+ (hasLiveSession) => {
213
+ if (hasLiveSession === false) {
214
+ void authClient.signOut();
215
+ }
216
+ },
217
+ () => {
218
+ }
219
+ );
220
+ stopWatching = () => unsubscribe();
221
+ },
222
+ { immediate: true }
223
+ );
224
+ void authClient.init();
40
225
  }
41
226
  });
@@ -1,5 +1,6 @@
1
- import { defineNuxtPlugin, useRuntimeConfig } from "nuxt/app";
2
- import { useSsrTokenRef } from "./utils/authCookie.js";
1
+ import { defineNuxtPlugin, useRequestEvent, useRuntimeConfig } from "nuxt/app";
2
+ import { useConvexAuthTokenState, useSsrTokenRef } from "./utils/authCookie.js";
3
+ import { isSsrConvexAuth } from "./utils/authStorage.js";
3
4
  import { createHttpClient } from "./utils/http.js";
4
5
  import { convexNuxtKey, createAuthContext } from "./utils/context.js";
5
6
  import { warnMissingConvexUrl } from "./utils/errors.js";
@@ -15,6 +16,11 @@ export default defineNuxtPlugin({
15
16
  }
16
17
  return;
17
18
  }
19
+ if (isSsrConvexAuth(convexConfig?.auth)) {
20
+ const event = useRequestEvent();
21
+ const token = event?.context?.convexAuthToken;
22
+ useConvexAuthTokenState().value = token ?? null;
23
+ }
18
24
  const ssrToken = useSsrTokenRef();
19
25
  const auth = createAuthContext();
20
26
  if (ssrToken.value) {
@@ -1,7 +1,12 @@
1
1
  import type { H3Event } from 'h3';
2
2
  /**
3
- * Read the Convex JWT from the configured auth cookie on this request.
4
- * Returns `undefined` when the cookie is missing or auth cookies are disabled.
3
+ * Read the Convex JWT for this request.
4
+ *
5
+ * - Convex Auth (SSR mode): the access token the session middleware
6
+ * refreshed for this request, else the access-token cookie while valid.
7
+ * - BYO providers: the configured `convex.auth.cookie`.
8
+ *
9
+ * Returns `undefined` when there is no session or auth cookies are disabled.
5
10
  */
6
11
  export declare function getConvexToken(event: H3Event): string | undefined;
7
12
  /**
@@ -1,16 +1,27 @@
1
1
  import { createError, getCookie } from "h3";
2
- import { resolveAuthCookieName } from "../utils/authStorage.js";
2
+ import { isJwtExpiring, isSsrConvexAuth, resolveAuthCookieName } from "../utils/authStorage.js";
3
3
  import { resolveFetchToken } from "../utils/fetchToken.js";
4
4
  import { readConvexConfig } from "./convexConfig.js";
5
5
  export function getConvexToken(event) {
6
- const cookieName = resolveAuthCookieName(readConvexConfig(event)?.auth);
6
+ const auth = readConvexConfig(event)?.auth;
7
+ if (isSsrConvexAuth(auth)) {
8
+ const fromMiddleware = event.context.convexAuthToken;
9
+ if (fromMiddleware !== void 0) {
10
+ return fromMiddleware ?? void 0;
11
+ }
12
+ }
13
+ const cookieName = resolveAuthCookieName(auth);
7
14
  if (!cookieName) {
8
15
  return void 0;
9
16
  }
10
- return resolveFetchToken({
17
+ const token = resolveFetchToken({
11
18
  cookieName,
12
19
  cookieValue: getCookie(event, cookieName)
13
20
  });
21
+ if (token && isSsrConvexAuth(auth) && isJwtExpiring(token, 0)) {
22
+ return void 0;
23
+ }
24
+ return token;
14
25
  }
15
26
  export function requireConvexAuth(event) {
16
27
  const token = getConvexToken(event);
@@ -0,0 +1,3 @@
1
+ /** Convex Auth v2 `refreshHandler` from `@convex-dev/auth/ssr`. */
2
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
3
+ export default _default;
@@ -0,0 +1,5 @@
1
+ import { defineEventHandler } from "h3";
2
+ import { runWebHandler, useConvexAuthServer } from "./setup.js";
3
+ export default defineEventHandler(
4
+ (event) => runWebHandler(event, useConvexAuthServer(event).refreshHandler)
5
+ );
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Resolve the Convex Auth access token for this request (SSR mode).
3
+ *
4
+ * Access tokens live ~60s, so most page loads find an expired one: refresh
5
+ * from the httpOnly refresh cookie, rotate both cookies on the response, and
6
+ * leave the fresh token on `event.context.convexAuthToken` for SSR queries,
7
+ * `getConvexToken`, and the Nitro `fetch*` helpers.
8
+ */
9
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
10
+ export default _default;
@@ -0,0 +1,37 @@
1
+ import { defineEventHandler, getCookie, getRequestURL } from "h3";
2
+ import { useRuntimeConfig } from "nitropack/runtime";
3
+ import {
4
+ CONVEX_AUTH_JWT_COOKIE,
5
+ CONVEX_AUTH_REFRESH_COOKIE,
6
+ DEFAULT_AUTH_ROUTE_PREFIX,
7
+ isJwtExpiring
8
+ } from "../../utils/authStorage.js";
9
+ import { createServerAuthSession } from "./setup.js";
10
+ export default defineEventHandler(async (event) => {
11
+ const context = event.context;
12
+ if (context.convexAuthToken !== void 0) {
13
+ return;
14
+ }
15
+ const pathname = getRequestURL(event).pathname;
16
+ const config = useRuntimeConfig(event);
17
+ const baseURL = (config.app?.baseURL ?? "/").replace(/\/+$/, "");
18
+ const auth = config.public?.convex?.auth;
19
+ const prefix = baseURL + (auth?.routePrefix ?? DEFAULT_AUTH_ROUTE_PREFIX).replace(/\/+$/, "");
20
+ if (pathname.startsWith(prefix + "/") || // Build assets and dev tooling never need a session. Other paths with a
21
+ // dot (`/users/jane.doe`, `/api/report.json`) are pages and endpoints
22
+ // that may. Files in `public/` are served by Nitro before middleware.
23
+ pathname.startsWith(`${baseURL}/_nuxt/`) || pathname.startsWith(`${baseURL}/__nuxt_devtools__`) || pathname.startsWith(`${baseURL}/__convex_devtools`) || pathname === `${baseURL}/favicon.ico`) {
24
+ return;
25
+ }
26
+ const jwt = getCookie(event, CONVEX_AUTH_JWT_COOKIE) || null;
27
+ const refresh = getCookie(event, CONVEX_AUTH_REFRESH_COOKIE) || null;
28
+ if (refresh === null) {
29
+ context.convexAuthToken = jwt !== null && !isJwtExpiring(jwt, 0) ? jwt : null;
30
+ return;
31
+ }
32
+ try {
33
+ context.convexAuthToken = await createServerAuthSession(event).getToken();
34
+ } catch {
35
+ context.convexAuthToken = jwt !== null && !isJwtExpiring(jwt, 0) ? jwt : null;
36
+ }
37
+ });
@@ -0,0 +1,21 @@
1
+ import { ServerAuthSession, setupConvexAuthServer, type AuthCookieOptions, type CookieStore } from '@convex-dev/auth/ssr';
2
+ import type { H3Event } from 'h3';
3
+ import { type ConvexAuthServerRuntimeConfig } from '../../utils/authStorage.js';
4
+ export type { ConvexAuthServerRuntimeConfig };
5
+ export declare function resolveCookieOptions(event: H3Event, server: ConvexAuthServerRuntimeConfig): AuthCookieOptions;
6
+ type Handlers = ReturnType<typeof setupConvexAuthServer>;
7
+ /**
8
+ * `setupConvexAuthServer` built from runtime config, cached per
9
+ * deployment + cookie attributes (`secure` differs on localhost).
10
+ */
11
+ export declare function useConvexAuthServer(event: H3Event): Handlers;
12
+ /** Run a WHATWG handler from `@convex-dev/auth/ssr` on an H3 event. */
13
+ export declare function runWebHandler(event: H3Event, handler: (request: Request) => Promise<Response>): Promise<void>;
14
+ /** `CookieStore` over H3 request/response cookies. */
15
+ export declare function h3CookieStore(event: H3Event): CookieStore;
16
+ /**
17
+ * A `ServerAuthSession` for this request: reads the access/refresh cookies,
18
+ * refreshes when the access token is missing or near expiry, and writes the
19
+ * rotated cookies onto the response.
20
+ */
21
+ export declare function createServerAuthSession(event: H3Event): ServerAuthSession;
@@ -0,0 +1,90 @@
1
+ import {
2
+ ServerAuthSession,
3
+ setupConvexAuthServer
4
+ } from "@convex-dev/auth/ssr";
5
+ import { ConvexHttpClient } from "convex/browser";
6
+ import { makeFunctionReference } from "convex/server";
7
+ import {
8
+ createError,
9
+ deleteCookie,
10
+ getCookie,
11
+ getRequestHeader,
12
+ sendWebResponse,
13
+ setCookie,
14
+ toWebRequest
15
+ } from "h3";
16
+ import { useRuntimeConfig } from "nitropack/runtime";
17
+ import { MISSING_URL_HINT } from "../../utils/errors.js";
18
+ import {
19
+ resolveAuthApiNames
20
+ } from "../../utils/authStorage.js";
21
+ function resolveConfig(event) {
22
+ const config = useRuntimeConfig(event);
23
+ const pub = config.public?.convex;
24
+ const convexUrl = pub?.url ?? process.env.NUXT_PUBLIC_CONVEX_URL;
25
+ if (!convexUrl) {
26
+ throw createError({ statusCode: 500, message: MISSING_URL_HINT });
27
+ }
28
+ return {
29
+ convexUrl,
30
+ auth: pub?.auth,
31
+ server: config.convexAuth ?? {}
32
+ };
33
+ }
34
+ function isLocalHost(event) {
35
+ const host = getRequestHeader(event, "host") ?? "";
36
+ return host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]");
37
+ }
38
+ export function resolveCookieOptions(event, server) {
39
+ return {
40
+ secure: server.cookie?.secure ?? !isLocalHost(event),
41
+ ...server.cookie?.domain ? { domain: server.cookie.domain } : {},
42
+ ...server.cookie?.path ? { path: server.cookie.path } : {}
43
+ };
44
+ }
45
+ const cache = /* @__PURE__ */ new Map();
46
+ export function useConvexAuthServer(event) {
47
+ const { convexUrl, auth, server } = resolveConfig(event);
48
+ const cookieOptions = resolveCookieOptions(event, server);
49
+ const api = resolveAuthApiNames(auth);
50
+ const key = JSON.stringify([convexUrl, api, server, cookieOptions]);
51
+ let handlers = cache.get(key);
52
+ if (!handlers) {
53
+ handlers = setupConvexAuthServer({
54
+ convexUrl,
55
+ refreshSession: makeFunctionReference(api.refreshSession),
56
+ signOut: makeFunctionReference(api.signOut),
57
+ signIn: (server.signIn ?? []).map((name) => makeFunctionReference(name)),
58
+ cookieOptions,
59
+ ...server.allowedOrigins?.length ? { allowedOrigins: server.allowedOrigins } : {}
60
+ });
61
+ cache.set(key, handlers);
62
+ }
63
+ return handlers;
64
+ }
65
+ export async function runWebHandler(event, handler) {
66
+ const response = await handler(toWebRequest(event));
67
+ return sendWebResponse(event, response);
68
+ }
69
+ export function h3CookieStore(event) {
70
+ return {
71
+ get: (name) => getCookie(event, name) || void 0,
72
+ set: (name, value, options) => {
73
+ setCookie(event, name, value, options);
74
+ },
75
+ delete: (name, options) => {
76
+ deleteCookie(event, name, options);
77
+ }
78
+ };
79
+ }
80
+ export function createServerAuthSession(event) {
81
+ const { convexUrl, auth, server } = resolveConfig(event);
82
+ const api = resolveAuthApiNames(auth);
83
+ const http = new ConvexHttpClient(convexUrl);
84
+ const refreshSession = makeFunctionReference(api.refreshSession);
85
+ return new ServerAuthSession({
86
+ refreshSession: (refreshToken) => http.mutation(refreshSession, { refreshToken }),
87
+ cookies: h3CookieStore(event),
88
+ cookieOptions: resolveCookieOptions(event, server)
89
+ });
90
+ }
@@ -0,0 +1,3 @@
1
+ /** Convex Auth v2 `convexProxyHandler` from `@convex-dev/auth/ssr`. */
2
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
3
+ export default _default;
@@ -0,0 +1,5 @@
1
+ import { defineEventHandler } from "h3";
2
+ import { runWebHandler, useConvexAuthServer } from "./setup.js";
3
+ export default defineEventHandler(
4
+ (event) => runWebHandler(event, useConvexAuthServer(event).convexProxyHandler)
5
+ );
@@ -0,0 +1,3 @@
1
+ /** Convex Auth v2 `signOutHandler` from `@convex-dev/auth/ssr`. */
2
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
3
+ export default _default;
@@ -0,0 +1,5 @@
1
+ import { defineEventHandler } from "h3";
2
+ import { runWebHandler, useConvexAuthServer } from "./setup.js";
3
+ export default defineEventHandler(
4
+ (event) => runWebHandler(event, useConvexAuthServer(event).signOutHandler)
5
+ );
@@ -1,12 +1,8 @@
1
1
  import type { H3Event } from 'h3';
2
+ import type { ConvexAuthConfig } from '../utils/authStorage.js';
2
3
  export interface ConvexPublicConfig {
3
4
  url?: string;
4
- auth?: {
5
- provider?: string;
6
- cookie?: string;
7
- httpOnly?: boolean;
8
- presentCookie?: string;
9
- };
5
+ auth?: ConvexAuthConfig;
10
6
  }
11
7
  /** Read `runtimeConfig.public.convex`, or `undefined` outside a Nitro request. */
12
8
  export declare function readConvexConfig(event?: H3Event): ConvexPublicConfig | undefined;
@@ -8,9 +8,17 @@ export interface ConvexFetchOptions {
8
8
  url?: string;
9
9
  /**
10
10
  * JWT for this call. Prefer per-request tokens — never put JWTs in public config.
11
- * When omitted and `event` is passed, falls back to `convex.auth.cookie` if set.
11
+ * When omitted and `event` is passed, falls back to the request's session
12
+ * (Convex Auth SSR mode) or `convex.auth.cookie` (BYO).
13
+ * Ignored when {@link adminToken} is set (admin auth clears user JWT).
12
14
  */
13
15
  token?: string;
16
+ /**
17
+ * Deploy / admin key for privileged server tooling (Next.js `fetchQuery`
18
+ * parity). Calls `ConvexHttpClient.setAdminAuth`. Never put this in public
19
+ * runtime config — pass per request from a server secret.
20
+ */
21
+ adminToken?: string;
14
22
  /**
15
23
  * Nitro/H3 event. Used to resolve runtimeConfig and the optional auth cookie.
16
24
  */
@@ -1,6 +1,5 @@
1
- import { getCookie } from "h3";
2
1
  import { createHttpClient } from "../utils/http.js";
3
- import { resolveAuthCookieName } from "../utils/authStorage.js";
2
+ import { getConvexToken } from "./auth.js";
4
3
  import { resolveFetchToken } from "../utils/fetchToken.js";
5
4
  import { missingConvexUrlError } from "../utils/errors.js";
6
5
  import { readConvexConfig } from "./convexConfig.js";
@@ -22,19 +21,17 @@ export function resolveToken(options) {
22
21
  if (options.token !== void 0) {
23
22
  return resolveFetchToken({ token: options.token });
24
23
  }
25
- const cookieName = resolveAuthCookieName(readConvexConfig(options.event)?.auth);
26
- if (!cookieName || !options.event) {
24
+ if (!options.event) {
27
25
  return void 0;
28
26
  }
29
- return resolveFetchToken({
30
- cookieName,
31
- cookieValue: getCookie(options.event, cookieName)
32
- });
27
+ return getConvexToken(options.event);
33
28
  }
34
29
  function setupClient(options = {}) {
30
+ const token = options.adminToken ? void 0 : resolveToken(options);
35
31
  return createHttpClient(resolveUrl(options), {
36
- token: resolveToken(options),
37
- skipConvexDeploymentUrlCheck: options.skipConvexDeploymentUrlCheck
32
+ ...token ? { token } : {},
33
+ ...options.adminToken ? { adminToken: options.adminToken } : {},
34
+ ...options.skipConvexDeploymentUrlCheck !== void 0 ? { skipConvexDeploymentUrlCheck: options.skipConvexDeploymentUrlCheck } : {}
38
35
  });
39
36
  }
40
37
  export async function fetchQuery(query, args, options) {