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,36 +0,0 @@
1
- /**
2
- * Persist / read / refresh HttpOnly auth cookies.
3
- *
4
- * - GET → `{ hasSession }` only (no JWT in the response body)
5
- * - POST `{ getToken: true }` → `{ hasSession, token }` for ConvexClient.setAuth
6
- * - POST `{ refresh: true }` → refresh cookies, return `{ token }`
7
- * - POST `{ token, refreshToken }` → set cookies
8
- * - DELETE → clear cookies
9
- *
10
- * All methods require same-origin (fail-closed). Returning the JWT only via
11
- * POST reduces accidental leakage via prefetch/logs; XSS can still obtain a
12
- * token once ConvexClient needs it in memory — HttpOnly mitigates cookie theft,
13
- * not XSS session theft.
14
- */
15
- declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
16
- hasSession: boolean;
17
- ok?: undefined;
18
- token?: undefined;
19
- } | {
20
- ok: boolean;
21
- hasSession?: undefined;
22
- token?: undefined;
23
- } | {
24
- hasSession: boolean;
25
- token: string | null;
26
- ok?: undefined;
27
- } | {
28
- token: null;
29
- hasSession?: undefined;
30
- ok?: undefined;
31
- } | {
32
- token: string;
33
- hasSession?: undefined;
34
- ok?: undefined;
35
- }>>;
36
- export default _default;
@@ -1,90 +0,0 @@
1
- import { ConvexHttpClient } from "convex/browser";
2
- import { makeFunctionReference } from "convex/server";
3
- import { createError, defineEventHandler, readBody } from "h3";
4
- import { useRuntimeConfig } from "nitropack/runtime";
5
- import { readHttpOnlyJwt, readHttpOnlyRefresh, setAuthCookies } from "../../../authCookies.js";
6
- import { isHttpOnlyAuth } from "../../../../utils/authStorage.js";
7
- import { assertSameOrigin } from "../../../sameOrigin.js";
8
- import { MISSING_URL_HINT } from "../../../../utils/errors.js";
9
- const authSignIn = makeFunctionReference(
10
- "auth:signIn"
11
- );
12
- function convexUrl(event) {
13
- const config = useRuntimeConfig(event);
14
- const url = config.public?.convex?.url ?? process.env.NUXT_PUBLIC_CONVEX_URL;
15
- if (!url) {
16
- throw createError({
17
- statusCode: 500,
18
- message: MISSING_URL_HINT
19
- });
20
- }
21
- return url;
22
- }
23
- function requireHttpOnly(event) {
24
- const config = useRuntimeConfig(event);
25
- const auth = config.public?.convex?.auth;
26
- if (!isHttpOnlyAuth(auth)) {
27
- throw createError({
28
- statusCode: 404,
29
- message: "HttpOnly auth is not enabled"
30
- });
31
- }
32
- }
33
- export default defineEventHandler(async (event) => {
34
- requireHttpOnly(event);
35
- assertSameOrigin(event);
36
- if (event.method === "GET") {
37
- const token = readHttpOnlyJwt(event);
38
- return {
39
- hasSession: !!token
40
- };
41
- }
42
- if (event.method === "DELETE") {
43
- setAuthCookies(event, { token: null });
44
- return { ok: true };
45
- }
46
- if (event.method === "POST") {
47
- const body = await readBody(event);
48
- if (body?.getToken) {
49
- const token = readHttpOnlyJwt(event);
50
- return {
51
- hasSession: !!token,
52
- token: token ?? null
53
- };
54
- }
55
- if (body?.refresh) {
56
- const refreshToken = readHttpOnlyRefresh(event);
57
- if (!refreshToken) {
58
- setAuthCookies(event, { token: null });
59
- return { token: null };
60
- }
61
- const http = new ConvexHttpClient(convexUrl(event));
62
- try {
63
- const result = await http.action(authSignIn, { refreshToken });
64
- const tokens = result.tokens ?? null;
65
- if (!tokens) {
66
- setAuthCookies(event, { token: null });
67
- return { token: null };
68
- }
69
- setAuthCookies(event, {
70
- token: tokens.token,
71
- refreshToken: tokens.refreshToken
72
- });
73
- return { token: tokens.token };
74
- } catch {
75
- setAuthCookies(event, { token: null });
76
- return { token: null };
77
- }
78
- }
79
- if (!body?.token) {
80
- setAuthCookies(event, { token: null });
81
- return { ok: true };
82
- }
83
- setAuthCookies(event, {
84
- token: body.token,
85
- refreshToken: body.refreshToken ?? null
86
- });
87
- return { ok: true };
88
- }
89
- throw createError({ statusCode: 405, message: "Method not allowed" });
90
- });
@@ -1,9 +0,0 @@
1
- import type { H3Event } from 'h3';
2
- export interface AuthCookieSet {
3
- token: string | null;
4
- refreshToken?: string | null;
5
- }
6
- /** Persist or clear HttpOnly auth cookies + readable presence marker. */
7
- export declare function setAuthCookies(event: H3Event, tokens: AuthCookieSet): void;
8
- export declare function readHttpOnlyJwt(event: H3Event): string | undefined;
9
- export declare function readHttpOnlyRefresh(event: H3Event): string | undefined;
@@ -1,64 +0,0 @@
1
- import { deleteCookie, getCookie, getRequestHeader, setCookie } from "h3";
2
- import {
3
- AUTH_JWT_COOKIE_MAX_AGE,
4
- isHttpOnlyAuth,
5
- resolveAuthCookieName,
6
- resolveAuthPresentCookieName,
7
- resolveAuthRefreshCookieName
8
- } from "../utils/authStorage.js";
9
- import { readConvexConfig } from "./convexConfig.js";
10
- function readAuthConfig(event) {
11
- return readConvexConfig(event)?.auth;
12
- }
13
- function isLocalHost(event) {
14
- const host = getRequestHeader(event, "host") ?? "";
15
- return host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("[::1]");
16
- }
17
- function cookieOptions(event, httpOnly) {
18
- const local = isLocalHost(event);
19
- return {
20
- httpOnly,
21
- secure: !local,
22
- sameSite: "lax",
23
- path: "/",
24
- maxAge: AUTH_JWT_COOKIE_MAX_AGE
25
- };
26
- }
27
- export function setAuthCookies(event, tokens) {
28
- const auth = readAuthConfig(event);
29
- if (!isHttpOnlyAuth(auth)) {
30
- throw new Error("[use-convex] setAuthCookies requires convex.auth.httpOnly: true");
31
- }
32
- const jwtName = resolveAuthCookieName(auth);
33
- const refreshName = resolveAuthRefreshCookieName(auth);
34
- const presentName = resolveAuthPresentCookieName(auth);
35
- const httpOpts = cookieOptions(event, true);
36
- const presentOpts = cookieOptions(event, false);
37
- if (!tokens.token) {
38
- deleteCookie(event, jwtName, httpOpts);
39
- deleteCookie(event, refreshName, httpOpts);
40
- deleteCookie(event, presentName, presentOpts);
41
- return;
42
- }
43
- setCookie(event, jwtName, tokens.token, httpOpts);
44
- if (tokens.refreshToken) {
45
- setCookie(event, refreshName, tokens.refreshToken, httpOpts);
46
- }
47
- setCookie(event, presentName, "1", presentOpts);
48
- }
49
- export function readHttpOnlyJwt(event) {
50
- const auth = readAuthConfig(event);
51
- const name = resolveAuthCookieName(auth);
52
- if (!name) {
53
- return void 0;
54
- }
55
- return getCookie(event, name) || void 0;
56
- }
57
- export function readHttpOnlyRefresh(event) {
58
- const auth = readAuthConfig(event);
59
- const name = resolveAuthRefreshCookieName(auth);
60
- if (!name) {
61
- return void 0;
62
- }
63
- return getCookie(event, name) || void 0;
64
- }
@@ -1,14 +0,0 @@
1
- import { type H3Event } from 'h3';
2
- /**
3
- * Fail-closed same-origin guard for cookie-bearing auth routes.
4
- *
5
- * Accepts the request when:
6
- * - `Sec-Fetch-Site` is `same-origin` or `none` (user-initiated / same origin), or
7
- * - `Origin` matches the trusted request origin (scheme + host), using
8
- * `Host` and `getRequestProtocol` (honors `X-Forwarded-Proto` behind proxies).
9
- *
10
- * Rejects when `Sec-Fetch-Site` is `cross-site` / `same-site`, or when neither
11
- * signal can confirm same-origin. Missing both headers is rejected.
12
- * Scheme-mismatched Origins (e.g. `http://` vs HTTPS request) are rejected.
13
- */
14
- export declare function assertSameOrigin(event: H3Event): void;
@@ -1,29 +0,0 @@
1
- import { createError, getRequestHeader, getRequestProtocol } from "h3";
2
- export function assertSameOrigin(event) {
3
- const secFetchSite = getRequestHeader(event, "sec-fetch-site");
4
- if (secFetchSite === "same-origin" || secFetchSite === "none") {
5
- return;
6
- }
7
- if (secFetchSite === "cross-site" || secFetchSite === "same-site") {
8
- throw createError({
9
- statusCode: 403,
10
- message: "Forbidden"
11
- });
12
- }
13
- const origin = getRequestHeader(event, "origin");
14
- const host = getRequestHeader(event, "host");
15
- if (origin && host) {
16
- try {
17
- const originUrl = new URL(origin);
18
- const protocol = getRequestProtocol(event, { xForwardedProto: true });
19
- if (originUrl.protocol === `${protocol}:` && originUrl.host === host) {
20
- return;
21
- }
22
- } catch {
23
- }
24
- }
25
- throw createError({
26
- statusCode: 403,
27
- message: "Forbidden"
28
- });
29
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * Single-flight mutex for token refresh (mirrors @convex-dev/auth/react).
3
- * Prefers `navigator.locks` when available; falls back to an in-memory queue.
4
- */
5
- type MutexState = {
6
- currentlyRunning: Promise<void> | null;
7
- waiting: Array<() => Promise<void>>;
8
- };
9
- declare global {
10
- var __convexNuxtAuthMutexes: Record<string, MutexState> | undefined;
11
- }
12
- export declare function withRefreshMutex<T>(key: string, callback: () => Promise<T>): Promise<T>;
13
- /** Test helper — clear in-memory mutex state between tests. */
14
- export declare function resetAuthMutexesForTests(): void;
15
- export {};
@@ -1,47 +0,0 @@
1
- export async function withRefreshMutex(key, callback) {
2
- const lockManager = typeof navigator !== "undefined" ? navigator.locks : void 0;
3
- if (lockManager !== void 0) {
4
- return await lockManager.request(key, callback);
5
- }
6
- return await manualMutex(key, callback);
7
- }
8
- async function manualMutex(key, callback) {
9
- return await new Promise((resolve, reject) => {
10
- const wrapped = () => callback().then(resolve).catch(reject).then(() => void 0);
11
- void enqueueCallbackForMutex(key, wrapped);
12
- });
13
- }
14
- function getMutexValue(key) {
15
- if (globalThis.__convexNuxtAuthMutexes === void 0) {
16
- globalThis.__convexNuxtAuthMutexes = {};
17
- }
18
- let mutex = globalThis.__convexNuxtAuthMutexes[key];
19
- if (mutex === void 0) {
20
- mutex = { currentlyRunning: null, waiting: [] };
21
- globalThis.__convexNuxtAuthMutexes[key] = mutex;
22
- }
23
- return mutex;
24
- }
25
- async function enqueueCallbackForMutex(key, callback) {
26
- const mutex = getMutexValue(key);
27
- if (mutex.currentlyRunning === null) {
28
- globalThis.__convexNuxtAuthMutexes[key] = {
29
- currentlyRunning: callback().finally(() => {
30
- const next = getMutexValue(key).waiting.shift();
31
- getMutexValue(key).currentlyRunning = null;
32
- if (next !== void 0) {
33
- void enqueueCallbackForMutex(key, next);
34
- }
35
- }),
36
- waiting: []
37
- };
38
- } else {
39
- globalThis.__convexNuxtAuthMutexes[key] = {
40
- ...mutex,
41
- waiting: [...mutex.waiting, callback]
42
- };
43
- }
44
- }
45
- export function resetAuthMutexesForTests() {
46
- globalThis.__convexNuxtAuthMutexes = void 0;
47
- }
@@ -1,5 +0,0 @@
1
- /**
2
- * Validate an OAuth redirect from Convex `auth:signIn`.
3
- * Only http(s) absolute URLs are allowed (blocks javascript:/data: etc.).
4
- */
5
- export declare function parseOAuthRedirect(redirect: string): URL;
@@ -1,13 +0,0 @@
1
- import { useConvexError } from "./errors.js";
2
- export function parseOAuthRedirect(redirect) {
3
- let url;
4
- try {
5
- url = new URL(redirect);
6
- } catch {
7
- throw useConvexError("Invalid OAuth redirect URL");
8
- }
9
- if (url.protocol !== "http:" && url.protocol !== "https:") {
10
- throw useConvexError("OAuth redirect must be http(s)");
11
- }
12
- return url;
13
- }