convex-zen 0.0.1
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/dist/cli/generate.d.ts +14 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +297 -0
- package/dist/cli/generate.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +111 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/client/index.d.ts +300 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +434 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/plugins/admin.d.ts +92 -0
- package/dist/client/plugins/admin.d.ts.map +1 -0
- package/dist/client/plugins/admin.js +165 -0
- package/dist/client/plugins/admin.js.map +1 -0
- package/dist/client/primitives.d.ts +57 -0
- package/dist/client/primitives.d.ts.map +1 -0
- package/dist/client/primitives.js +64 -0
- package/dist/client/primitives.js.map +1 -0
- package/dist/client/providers.d.ts +14 -0
- package/dist/client/providers.d.ts.map +1 -0
- package/dist/client/providers.js +25 -0
- package/dist/client/providers.js.map +1 -0
- package/dist/client/react.d.ts +23 -0
- package/dist/client/react.d.ts.map +1 -0
- package/dist/client/react.js +48 -0
- package/dist/client/react.js.map +1 -0
- package/dist/client/tanstack-start-client-plugins.d.ts +34 -0
- package/dist/client/tanstack-start-client-plugins.d.ts.map +1 -0
- package/dist/client/tanstack-start-client-plugins.js +32 -0
- package/dist/client/tanstack-start-client-plugins.js.map +1 -0
- package/dist/client/tanstack-start-client.d.ts +52 -0
- package/dist/client/tanstack-start-client.d.ts.map +1 -0
- package/dist/client/tanstack-start-client.js +130 -0
- package/dist/client/tanstack-start-client.js.map +1 -0
- package/dist/client/tanstack-start-plugins.d.ts +27 -0
- package/dist/client/tanstack-start-plugins.d.ts.map +1 -0
- package/dist/client/tanstack-start-plugins.js +145 -0
- package/dist/client/tanstack-start-plugins.js.map +1 -0
- package/dist/client/tanstack-start.d.ts +130 -0
- package/dist/client/tanstack-start.d.ts.map +1 -0
- package/dist/client/tanstack-start.js +331 -0
- package/dist/client/tanstack-start.js.map +1 -0
- package/dist/component/_generated/api.d.ts +50 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/dist/component/_generated/api.js +31 -0
- package/dist/component/_generated/api.js.map +1 -0
- package/dist/component/_generated/component.d.ts +92 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +46 -0
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +121 -0
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/dist/component/_generated/server.js +78 -0
- package/dist/component/_generated/server.js.map +1 -0
- package/dist/component/convex.config.d.ts +3 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +4 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/core/sessions.d.ts +33 -0
- package/dist/component/core/sessions.d.ts.map +1 -0
- package/dist/component/core/sessions.js +186 -0
- package/dist/component/core/sessions.js.map +1 -0
- package/dist/component/core/users.d.ts +19 -0
- package/dist/component/core/users.d.ts.map +1 -0
- package/dist/component/core/users.js +154 -0
- package/dist/component/core/users.js.map +1 -0
- package/dist/component/core/verifications.d.ts +34 -0
- package/dist/component/core/verifications.d.ts.map +1 -0
- package/dist/component/core/verifications.js +135 -0
- package/dist/component/core/verifications.js.map +1 -0
- package/dist/component/gateway.d.ts +16 -0
- package/dist/component/gateway.d.ts.map +1 -0
- package/dist/component/gateway.js +229 -0
- package/dist/component/gateway.js.map +1 -0
- package/dist/component/lib/crypto.d.ts +24 -0
- package/dist/component/lib/crypto.d.ts.map +1 -0
- package/dist/component/lib/crypto.js +57 -0
- package/dist/component/lib/crypto.js.map +1 -0
- package/dist/component/lib/rateLimit.d.ts +26 -0
- package/dist/component/lib/rateLimit.d.ts.map +1 -0
- package/dist/component/lib/rateLimit.js +96 -0
- package/dist/component/lib/rateLimit.js.map +1 -0
- package/dist/component/lib/validators.d.ts +19 -0
- package/dist/component/lib/validators.d.ts.map +1 -0
- package/dist/component/lib/validators.js +12 -0
- package/dist/component/lib/validators.js.map +1 -0
- package/dist/component/plugins/admin.d.ts +72 -0
- package/dist/component/plugins/admin.d.ts.map +1 -0
- package/dist/component/plugins/admin.js +152 -0
- package/dist/component/plugins/admin.js.map +1 -0
- package/dist/component/providers/emailPassword.d.ts +49 -0
- package/dist/component/providers/emailPassword.d.ts.map +1 -0
- package/dist/component/providers/emailPassword.js +316 -0
- package/dist/component/providers/emailPassword.js.map +1 -0
- package/dist/component/providers/oauth.d.ts +33 -0
- package/dist/component/providers/oauth.d.ts.map +1 -0
- package/dist/component/providers/oauth.js +256 -0
- package/dist/component/providers/oauth.js.map +1 -0
- package/dist/component/schema.d.ts +132 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +82 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/types.d.ts +67 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +121 -0
- package/src/cli/generate.ts +360 -0
- package/src/cli/index.ts +133 -0
- package/src/client/index.ts +707 -0
- package/src/client/plugins/admin.ts +205 -0
- package/src/client/primitives.ts +100 -0
- package/src/client/providers.ts +35 -0
- package/src/client/react.ts +97 -0
- package/src/client/tanstack-start-client-plugins.ts +113 -0
- package/src/client/tanstack-start-client.ts +259 -0
- package/src/client/tanstack-start-plugins.ts +203 -0
- package/src/client/tanstack-start.ts +535 -0
- package/src/component/_generated/api.ts +70 -0
- package/src/component/_generated/component.ts +184 -0
- package/src/component/_generated/dataModel.ts +60 -0
- package/src/component/_generated/server.ts +156 -0
- package/src/component/convex.config.ts +5 -0
- package/src/component/core/sessions.ts +228 -0
- package/src/component/core/users.ts +199 -0
- package/src/component/core/verifications.ts +173 -0
- package/src/component/gateway.ts +321 -0
- package/src/component/lib/crypto.ts +63 -0
- package/src/component/lib/internalApi.ts +66 -0
- package/src/component/lib/rateLimit.ts +111 -0
- package/src/component/lib/validators.ts +12 -0
- package/src/component/plugins/admin.ts +178 -0
- package/src/component/providers/emailPassword.ts +374 -0
- package/src/component/providers/oauth.ts +324 -0
- package/src/component/schema.ts +88 -0
- package/src/types.ts +68 -0
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
import { ConvexHttpClient } from "convex/browser";
|
|
2
|
+
import type {
|
|
3
|
+
FunctionArgs,
|
|
4
|
+
FunctionReference,
|
|
5
|
+
FunctionReturnType,
|
|
6
|
+
} from "convex/server";
|
|
7
|
+
import type {
|
|
8
|
+
deleteCookie as deleteCookieFn,
|
|
9
|
+
setCookie as setCookieFn,
|
|
10
|
+
} from "@tanstack/react-start/server";
|
|
11
|
+
import {
|
|
12
|
+
createSessionPrimitives,
|
|
13
|
+
type SessionInfo,
|
|
14
|
+
type SessionPrimitives,
|
|
15
|
+
type SignInInput,
|
|
16
|
+
type SignInOutput,
|
|
17
|
+
} from "./primitives";
|
|
18
|
+
|
|
19
|
+
type SetCookieOptions = Exclude<Parameters<typeof setCookieFn>[2], undefined>;
|
|
20
|
+
type DeleteCookieOptions = Exclude<
|
|
21
|
+
Parameters<typeof deleteCookieFn>[1],
|
|
22
|
+
undefined
|
|
23
|
+
>;
|
|
24
|
+
|
|
25
|
+
export interface TanStackStartAuthOptions {
|
|
26
|
+
primitives: SessionPrimitives;
|
|
27
|
+
cookieName?: string;
|
|
28
|
+
cookieOptions?: Partial<SetCookieOptions>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface TanStackStartConvexActions {
|
|
32
|
+
/** Email/password sign-in action. */
|
|
33
|
+
signInWithEmail: FunctionReference<"action", "public">;
|
|
34
|
+
validateSession: FunctionReference<"action", "public">;
|
|
35
|
+
signOut: FunctionReference<"action", "public">;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface TanStackStartConvexAuthOptions {
|
|
39
|
+
convexUrl: string;
|
|
40
|
+
actions: TanStackStartConvexActions;
|
|
41
|
+
cookieName?: string;
|
|
42
|
+
cookieOptions?: Partial<SetCookieOptions>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface TanStackStartConvexReactStartOptions
|
|
46
|
+
extends TanStackStartConvexAuthOptions {
|
|
47
|
+
authApiBasePath?: string;
|
|
48
|
+
plugins?: readonly TanStackStartAuthApiPluginFactory[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface AuthenticatedSession {
|
|
52
|
+
token: string;
|
|
53
|
+
session: SessionInfo;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface TanStackStartAuth {
|
|
57
|
+
getSession: () => Promise<SessionInfo | null>;
|
|
58
|
+
getToken: () => Promise<string | null>;
|
|
59
|
+
signIn: (input: SignInInput) => Promise<SessionInfo>;
|
|
60
|
+
signOut: () => Promise<void>;
|
|
61
|
+
requireSession: () => Promise<AuthenticatedSession>;
|
|
62
|
+
withSession: <T>(
|
|
63
|
+
handler: (auth: AuthenticatedSession) => Promise<T>
|
|
64
|
+
) => Promise<T>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface TanStackStartSessionHandlers {
|
|
68
|
+
getSession: () => Promise<SessionInfo | null>;
|
|
69
|
+
getToken: () => Promise<string | null>;
|
|
70
|
+
requireSession: () => Promise<AuthenticatedSession>;
|
|
71
|
+
withSession: <T>(
|
|
72
|
+
handler: (auth: AuthenticatedSession) => Promise<T>
|
|
73
|
+
) => Promise<T>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface TanStackStartAuthHandlers {
|
|
77
|
+
signInWithEmail: (input: SignInInput) => Promise<SessionInfo>;
|
|
78
|
+
signOut: () => Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface TanStackStartAuthApiHandlerOptions {
|
|
82
|
+
tanstackAuth: Pick<TanStackStartAuth, "getSession" | "signIn" | "signOut">;
|
|
83
|
+
basePath?: string;
|
|
84
|
+
plugins?: readonly TanStackStartAuthApiPlugin[];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface TanStackStartAuthApiPluginContext {
|
|
88
|
+
request: Request;
|
|
89
|
+
method: string;
|
|
90
|
+
action: string;
|
|
91
|
+
readJson: () => Promise<unknown>;
|
|
92
|
+
json: (data: unknown, status?: number) => Response;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface TanStackStartAuthApiPlugin {
|
|
96
|
+
id: string;
|
|
97
|
+
handle: (
|
|
98
|
+
context: TanStackStartAuthApiPluginContext
|
|
99
|
+
) => Promise<Response | null> | Response | null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface TanStackStartAuthApiPluginFactoryContext {
|
|
103
|
+
tanstackAuth: TanStackAuthForPluginFactory;
|
|
104
|
+
fetchers: TanStackStartConvexFetchers;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
type TanStackAuthForPluginFactory = Pick<
|
|
108
|
+
TanStackStartAuth,
|
|
109
|
+
"getSession" | "getToken" | "signIn" | "signOut" | "requireSession" | "withSession"
|
|
110
|
+
>;
|
|
111
|
+
|
|
112
|
+
export interface TanStackStartAuthApiPluginFactory {
|
|
113
|
+
id: string;
|
|
114
|
+
create: (
|
|
115
|
+
context: TanStackStartAuthApiPluginFactoryContext
|
|
116
|
+
) => TanStackStartAuthApiPlugin;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface TanStackStartConvexFetchers {
|
|
120
|
+
fetchAuthQuery: <Query extends FunctionReference<"query", "public">>(
|
|
121
|
+
fn: Query,
|
|
122
|
+
args: FunctionArgs<Query>
|
|
123
|
+
) => Promise<FunctionReturnType<Query>>;
|
|
124
|
+
fetchAuthMutation: <Mutation extends FunctionReference<"mutation", "public">>(
|
|
125
|
+
fn: Mutation,
|
|
126
|
+
args: FunctionArgs<Mutation>
|
|
127
|
+
) => Promise<FunctionReturnType<Mutation>>;
|
|
128
|
+
fetchAuthAction: <Action extends FunctionReference<"action", "public">>(
|
|
129
|
+
fn: Action,
|
|
130
|
+
args: FunctionArgs<Action>
|
|
131
|
+
) => Promise<FunctionReturnType<Action>>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface TanStackStartConvexFetchersOptions {
|
|
135
|
+
tanstackAuth: Pick<TanStackStartAuth, "requireSession">;
|
|
136
|
+
convexUrl: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface TanStackStartConvexReactStart
|
|
140
|
+
extends TanStackStartAuth,
|
|
141
|
+
TanStackStartConvexFetchers {
|
|
142
|
+
handler: (request: Request) => Promise<Response>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function resolveCookieOptions(
|
|
146
|
+
options?: Partial<SetCookieOptions>
|
|
147
|
+
): SetCookieOptions {
|
|
148
|
+
return {
|
|
149
|
+
path: "/",
|
|
150
|
+
httpOnly: true,
|
|
151
|
+
sameSite: "lax",
|
|
152
|
+
secure: false,
|
|
153
|
+
...options,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
type AuthApiErrorPayload = { error?: string };
|
|
158
|
+
|
|
159
|
+
function json(data: unknown, status = 200): Response {
|
|
160
|
+
return new Response(JSON.stringify(data), {
|
|
161
|
+
status,
|
|
162
|
+
headers: { "content-type": "application/json; charset=utf-8" },
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function normalizeBasePath(path: string): string {
|
|
167
|
+
const normalized = path.trim();
|
|
168
|
+
if (normalized === "") {
|
|
169
|
+
return "/api/auth";
|
|
170
|
+
}
|
|
171
|
+
const withLeadingSlash = normalized.startsWith("/")
|
|
172
|
+
? normalized
|
|
173
|
+
: `/${normalized}`;
|
|
174
|
+
if (withLeadingSlash.length > 1 && withLeadingSlash.endsWith("/")) {
|
|
175
|
+
return withLeadingSlash.slice(0, -1);
|
|
176
|
+
}
|
|
177
|
+
return withLeadingSlash;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function resolveActionFromPath(pathname: string, basePath: string): string | null {
|
|
181
|
+
if (pathname === basePath) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const expectedPrefix = `${basePath}/`;
|
|
185
|
+
if (!pathname.startsWith(expectedPrefix)) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
const action = pathname.slice(expectedPrefix.length);
|
|
189
|
+
return action.length > 0 ? action : null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function readClientIp(request: Request): string | undefined {
|
|
193
|
+
const forwarded = request.headers.get("x-forwarded-for");
|
|
194
|
+
if (typeof forwarded === "string" && forwarded.length > 0) {
|
|
195
|
+
const [first] = forwarded.split(",");
|
|
196
|
+
const ip = first?.trim();
|
|
197
|
+
if (ip) {
|
|
198
|
+
return ip;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const realIp = request.headers.get("x-real-ip");
|
|
202
|
+
return realIp && realIp.length > 0 ? realIp : undefined;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function toErrorMessage(error: unknown): string {
|
|
206
|
+
if (error instanceof Error) {
|
|
207
|
+
return error.message;
|
|
208
|
+
}
|
|
209
|
+
if (
|
|
210
|
+
error &&
|
|
211
|
+
typeof error === "object" &&
|
|
212
|
+
"error" in error &&
|
|
213
|
+
typeof (error as AuthApiErrorPayload).error === "string"
|
|
214
|
+
) {
|
|
215
|
+
return (error as AuthApiErrorPayload).error as string;
|
|
216
|
+
}
|
|
217
|
+
return "Authentication request failed";
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* TanStack Start adapter.
|
|
222
|
+
*
|
|
223
|
+
* Exposes server-side auth handlers that read/write an HttpOnly session cookie
|
|
224
|
+
* and delegate auth logic to framework-agnostic SessionPrimitives.
|
|
225
|
+
*
|
|
226
|
+
* These handlers are meant to be wrapped by route-local `createServerFn(...)`
|
|
227
|
+
* so TanStack's server function transform runs in application code.
|
|
228
|
+
*/
|
|
229
|
+
export function createTanStackStartAuth(
|
|
230
|
+
options: TanStackStartAuthOptions
|
|
231
|
+
): TanStackStartAuth {
|
|
232
|
+
const cookieName = options.cookieName ?? "cz_session";
|
|
233
|
+
const cookieOptions = resolveCookieOptions(options.cookieOptions);
|
|
234
|
+
const clearCookieOptions: DeleteCookieOptions = {
|
|
235
|
+
path: cookieOptions.path ?? "/",
|
|
236
|
+
};
|
|
237
|
+
const unauthorizedError = () => new Error("Unauthorized");
|
|
238
|
+
const getCookieToken = async () => {
|
|
239
|
+
const { getCookie } = await import("@tanstack/react-start/server");
|
|
240
|
+
return getCookie(cookieName);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const resolveAuthenticatedSession = async (): Promise<AuthenticatedSession | null> => {
|
|
244
|
+
const { deleteCookie } = await import("@tanstack/react-start/server");
|
|
245
|
+
const token = await getCookieToken();
|
|
246
|
+
if (!token) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const session = await options.primitives.getSessionFromToken(token);
|
|
251
|
+
if (!session) {
|
|
252
|
+
deleteCookie(cookieName, clearCookieOptions);
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return { token, session };
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const getSession = async () => {
|
|
260
|
+
return (await resolveAuthenticatedSession())?.session ?? null;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const getToken = async () => {
|
|
264
|
+
return (await resolveAuthenticatedSession())?.token ?? null;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const signIn = async (input: SignInInput) => {
|
|
268
|
+
const { setCookie } = await import("@tanstack/react-start/server");
|
|
269
|
+
const established = await options.primitives.signInAndResolveSession(input);
|
|
270
|
+
setCookie(cookieName, established.sessionToken, cookieOptions);
|
|
271
|
+
return established.session;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const signOut = async () => {
|
|
275
|
+
const { deleteCookie } = await import("@tanstack/react-start/server");
|
|
276
|
+
const token = await getCookieToken();
|
|
277
|
+
try {
|
|
278
|
+
await options.primitives.signOutByToken(token);
|
|
279
|
+
} finally {
|
|
280
|
+
deleteCookie(cookieName, clearCookieOptions);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const requireSession = async (): Promise<AuthenticatedSession> => {
|
|
285
|
+
const authenticated = await resolveAuthenticatedSession();
|
|
286
|
+
if (!authenticated) {
|
|
287
|
+
throw unauthorizedError();
|
|
288
|
+
}
|
|
289
|
+
return authenticated;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const withSession = async <T>(
|
|
293
|
+
handler: (auth: AuthenticatedSession) => Promise<T>
|
|
294
|
+
): Promise<T> => {
|
|
295
|
+
return handler(await requireSession());
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
getSession,
|
|
300
|
+
getToken,
|
|
301
|
+
signIn,
|
|
302
|
+
signOut,
|
|
303
|
+
requireSession,
|
|
304
|
+
withSession,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** Build route-friendly session handlers from a TanStack auth instance. */
|
|
309
|
+
export function createTanStackStartSessionHandlers(
|
|
310
|
+
tanstackAuth: TanStackStartAuth
|
|
311
|
+
): TanStackStartSessionHandlers {
|
|
312
|
+
return {
|
|
313
|
+
getSession: async () => tanstackAuth.getSession(),
|
|
314
|
+
getToken: async () => tanstackAuth.getToken(),
|
|
315
|
+
requireSession: async () => tanstackAuth.requireSession(),
|
|
316
|
+
withSession: async <T>(handler: (auth: AuthenticatedSession) => Promise<T>) =>
|
|
317
|
+
tanstackAuth.withSession(handler),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** Build route-friendly auth mutation handlers from a TanStack auth instance. */
|
|
322
|
+
export function createTanStackStartAuthHandlers(
|
|
323
|
+
tanstackAuth: TanStackStartAuth
|
|
324
|
+
): TanStackStartAuthHandlers {
|
|
325
|
+
return {
|
|
326
|
+
signInWithEmail: async (input) => tanstackAuth.signIn(input),
|
|
327
|
+
signOut: async () => tanstackAuth.signOut(),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Build a Request handler for `/api/auth/*` routes in TanStack Start.
|
|
333
|
+
*
|
|
334
|
+
* Supported actions:
|
|
335
|
+
* - `GET /api/auth/session`
|
|
336
|
+
* - `POST /api/auth/sign-in-with-email`
|
|
337
|
+
* - `POST /api/auth/sign-out`
|
|
338
|
+
*/
|
|
339
|
+
export function createTanStackStartAuthApiHandler(
|
|
340
|
+
options: TanStackStartAuthApiHandlerOptions
|
|
341
|
+
): (request: Request) => Promise<Response> {
|
|
342
|
+
const basePath = normalizeBasePath(options.basePath ?? "/api/auth");
|
|
343
|
+
const plugins = options.plugins ?? [];
|
|
344
|
+
|
|
345
|
+
return async (request: Request): Promise<Response> => {
|
|
346
|
+
const action = resolveActionFromPath(new URL(request.url).pathname, basePath);
|
|
347
|
+
if (!action) {
|
|
348
|
+
return json({ error: "Not found" }, 404);
|
|
349
|
+
}
|
|
350
|
+
let parsedBody: Promise<unknown> | null = null;
|
|
351
|
+
const readRequestBody = async (): Promise<unknown> => {
|
|
352
|
+
if (!parsedBody) {
|
|
353
|
+
parsedBody = request.json().catch(() => null);
|
|
354
|
+
}
|
|
355
|
+
return parsedBody;
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
try {
|
|
359
|
+
if (request.method === "GET" && action === "session") {
|
|
360
|
+
const session = await options.tanstackAuth.getSession();
|
|
361
|
+
return json({ session });
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (request.method === "POST" && action === "sign-in-with-email") {
|
|
365
|
+
const body = (await readRequestBody()) as {
|
|
366
|
+
email?: unknown;
|
|
367
|
+
password?: unknown;
|
|
368
|
+
ipAddress?: unknown;
|
|
369
|
+
userAgent?: unknown;
|
|
370
|
+
};
|
|
371
|
+
if (
|
|
372
|
+
typeof body?.email !== "string" ||
|
|
373
|
+
typeof body?.password !== "string"
|
|
374
|
+
) {
|
|
375
|
+
return json({ error: "Invalid request body" }, 400);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const signInInput: SignInInput = {
|
|
379
|
+
email: body.email,
|
|
380
|
+
password: body.password,
|
|
381
|
+
};
|
|
382
|
+
if (typeof body.ipAddress === "string" && body.ipAddress.length > 0) {
|
|
383
|
+
signInInput.ipAddress = body.ipAddress;
|
|
384
|
+
} else {
|
|
385
|
+
const requestIp = readClientIp(request);
|
|
386
|
+
if (requestIp) {
|
|
387
|
+
signInInput.ipAddress = requestIp;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (typeof body.userAgent === "string" && body.userAgent.length > 0) {
|
|
391
|
+
signInInput.userAgent = body.userAgent;
|
|
392
|
+
} else {
|
|
393
|
+
const headerUserAgent = request.headers.get("user-agent");
|
|
394
|
+
if (headerUserAgent) {
|
|
395
|
+
signInInput.userAgent = headerUserAgent;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const session = await options.tanstackAuth.signIn(signInInput);
|
|
400
|
+
return json({ session });
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (request.method === "POST" && action === "sign-out") {
|
|
404
|
+
await options.tanstackAuth.signOut();
|
|
405
|
+
return json({ ok: true });
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const pluginContext: TanStackStartAuthApiPluginContext = {
|
|
409
|
+
request,
|
|
410
|
+
method: request.method,
|
|
411
|
+
action,
|
|
412
|
+
readJson: readRequestBody,
|
|
413
|
+
json,
|
|
414
|
+
};
|
|
415
|
+
for (const plugin of plugins) {
|
|
416
|
+
const response = await plugin.handle(pluginContext);
|
|
417
|
+
if (response) {
|
|
418
|
+
return response;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return json({ error: "Not found" }, 404);
|
|
423
|
+
} catch (error) {
|
|
424
|
+
return json({ error: toErrorMessage(error) }, 400);
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Build authenticated Convex fetch helpers for TanStack Start server functions.
|
|
431
|
+
* Each call resolves auth from cookies via `requireSession`.
|
|
432
|
+
*/
|
|
433
|
+
export function createTanStackStartConvexFetchers(
|
|
434
|
+
options: TanStackStartConvexFetchersOptions
|
|
435
|
+
): TanStackStartConvexFetchers {
|
|
436
|
+
const withAuthenticatedConvexClient = async <T>(
|
|
437
|
+
runner: (client: ConvexHttpClient) => Promise<T>
|
|
438
|
+
): Promise<T> => {
|
|
439
|
+
const { token } = await options.tanstackAuth.requireSession();
|
|
440
|
+
const convex = new ConvexHttpClient(options.convexUrl);
|
|
441
|
+
convex.setAuth(token);
|
|
442
|
+
return runner(convex);
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
return {
|
|
446
|
+
fetchAuthQuery: async (fn, args) => {
|
|
447
|
+
return withAuthenticatedConvexClient((convex) => convex.query(fn, args));
|
|
448
|
+
},
|
|
449
|
+
fetchAuthMutation: async (fn, args) => {
|
|
450
|
+
return withAuthenticatedConvexClient((convex) => convex.mutation(fn, args));
|
|
451
|
+
},
|
|
452
|
+
fetchAuthAction: async (fn, args) => {
|
|
453
|
+
return withAuthenticatedConvexClient((convex) => convex.action(fn, args));
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Build TanStack Start auth handlers directly from Convex public action refs.
|
|
460
|
+
*
|
|
461
|
+
* This removes transport boilerplate in app code while keeping session logic
|
|
462
|
+
* fully based on Convex functions.
|
|
463
|
+
*/
|
|
464
|
+
export function createTanStackStartConvexAuth(
|
|
465
|
+
options: TanStackStartConvexAuthOptions
|
|
466
|
+
): TanStackStartAuth {
|
|
467
|
+
const convex = new ConvexHttpClient(options.convexUrl);
|
|
468
|
+
const primitives = createSessionPrimitives({
|
|
469
|
+
signIn: async (input) => {
|
|
470
|
+
return (await convex.action(options.actions.signInWithEmail, input)) as SignInOutput;
|
|
471
|
+
},
|
|
472
|
+
validateSession: async (token) => {
|
|
473
|
+
return (await convex.action(options.actions.validateSession, {
|
|
474
|
+
token,
|
|
475
|
+
})) as SessionInfo | null;
|
|
476
|
+
},
|
|
477
|
+
signOut: async (token) => {
|
|
478
|
+
await convex.action(options.actions.signOut, { token });
|
|
479
|
+
},
|
|
480
|
+
});
|
|
481
|
+
const adapterOptions: TanStackStartAuthOptions = { primitives };
|
|
482
|
+
if (options.cookieName !== undefined) {
|
|
483
|
+
adapterOptions.cookieName = options.cookieName;
|
|
484
|
+
}
|
|
485
|
+
if (options.cookieOptions !== undefined) {
|
|
486
|
+
adapterOptions.cookieOptions = options.cookieOptions;
|
|
487
|
+
}
|
|
488
|
+
return createTanStackStartAuth(adapterOptions);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Better-auth-style TanStack Start helper for convex-zen.
|
|
493
|
+
*
|
|
494
|
+
* Returns a single object containing:
|
|
495
|
+
* - auth route `handler` (`/api/auth/*`)
|
|
496
|
+
* - token/session helpers (`getSession`, `getToken`, ...)
|
|
497
|
+
* - authenticated Convex fetchers (`fetchAuthQuery`, `fetchAuthMutation`, `fetchAuthAction`)
|
|
498
|
+
*/
|
|
499
|
+
export function convexZenReactStart(
|
|
500
|
+
options: TanStackStartConvexReactStartOptions
|
|
501
|
+
): TanStackStartConvexReactStart {
|
|
502
|
+
const tanstackAuth = createTanStackStartConvexAuth(options);
|
|
503
|
+
const fetchers = createTanStackStartConvexFetchers({
|
|
504
|
+
tanstackAuth,
|
|
505
|
+
convexUrl: options.convexUrl,
|
|
506
|
+
});
|
|
507
|
+
const authApiPlugins =
|
|
508
|
+
options.plugins?.map((plugin) =>
|
|
509
|
+
plugin.create({
|
|
510
|
+
tanstackAuth,
|
|
511
|
+
fetchers,
|
|
512
|
+
})
|
|
513
|
+
) ?? [];
|
|
514
|
+
const authApiHandlerOptions: TanStackStartAuthApiHandlerOptions = {
|
|
515
|
+
tanstackAuth,
|
|
516
|
+
plugins: authApiPlugins,
|
|
517
|
+
};
|
|
518
|
+
if (options.authApiBasePath !== undefined) {
|
|
519
|
+
authApiHandlerOptions.basePath = options.authApiBasePath;
|
|
520
|
+
}
|
|
521
|
+
const handler = createTanStackStartAuthApiHandler(authApiHandlerOptions);
|
|
522
|
+
|
|
523
|
+
return {
|
|
524
|
+
getSession: tanstackAuth.getSession,
|
|
525
|
+
getToken: tanstackAuth.getToken,
|
|
526
|
+
signIn: tanstackAuth.signIn,
|
|
527
|
+
signOut: tanstackAuth.signOut,
|
|
528
|
+
requireSession: tanstackAuth.requireSession,
|
|
529
|
+
withSession: tanstackAuth.withSession,
|
|
530
|
+
fetchAuthQuery: fetchers.fetchAuthQuery,
|
|
531
|
+
fetchAuthMutation: fetchers.fetchAuthMutation,
|
|
532
|
+
fetchAuthAction: fetchers.fetchAuthAction,
|
|
533
|
+
handler,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* Generated `api` utility.
|
|
4
|
+
*
|
|
5
|
+
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
6
|
+
*
|
|
7
|
+
* To regenerate, run `npx convex dev`.
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type * as core_sessions from "../core/sessions.js";
|
|
12
|
+
import type * as core_users from "../core/users.js";
|
|
13
|
+
import type * as core_verifications from "../core/verifications.js";
|
|
14
|
+
import type * as gateway from "../gateway.js";
|
|
15
|
+
import type * as lib_crypto from "../lib/crypto.js";
|
|
16
|
+
import type * as lib_internalApi from "../lib/internalApi.js";
|
|
17
|
+
import type * as lib_rateLimit from "../lib/rateLimit.js";
|
|
18
|
+
import type * as lib_validators from "../lib/validators.js";
|
|
19
|
+
import type * as plugins_admin from "../plugins/admin.js";
|
|
20
|
+
import type * as providers_emailPassword from "../providers/emailPassword.js";
|
|
21
|
+
import type * as providers_oauth from "../providers/oauth.js";
|
|
22
|
+
|
|
23
|
+
import type {
|
|
24
|
+
ApiFromModules,
|
|
25
|
+
FilterApi,
|
|
26
|
+
FunctionReference,
|
|
27
|
+
} from "convex/server";
|
|
28
|
+
import { anyApi, componentsGeneric } from "convex/server";
|
|
29
|
+
|
|
30
|
+
const fullApi: ApiFromModules<{
|
|
31
|
+
"core/sessions": typeof core_sessions;
|
|
32
|
+
"core/users": typeof core_users;
|
|
33
|
+
"core/verifications": typeof core_verifications;
|
|
34
|
+
gateway: typeof gateway;
|
|
35
|
+
"lib/crypto": typeof lib_crypto;
|
|
36
|
+
"lib/internalApi": typeof lib_internalApi;
|
|
37
|
+
"lib/rateLimit": typeof lib_rateLimit;
|
|
38
|
+
"lib/validators": typeof lib_validators;
|
|
39
|
+
"plugins/admin": typeof plugins_admin;
|
|
40
|
+
"providers/emailPassword": typeof providers_emailPassword;
|
|
41
|
+
"providers/oauth": typeof providers_oauth;
|
|
42
|
+
}> = anyApi as any;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A utility for referencing Convex functions in your app's public API.
|
|
46
|
+
*
|
|
47
|
+
* Usage:
|
|
48
|
+
* ```js
|
|
49
|
+
* const myFunctionReference = api.myModule.myFunction;
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export const api: FilterApi<
|
|
53
|
+
typeof fullApi,
|
|
54
|
+
FunctionReference<any, "public">
|
|
55
|
+
> = anyApi as any;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A utility for referencing Convex functions in your app's internal API.
|
|
59
|
+
*
|
|
60
|
+
* Usage:
|
|
61
|
+
* ```js
|
|
62
|
+
* const myFunctionReference = internal.myModule.myFunction;
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export const internal: FilterApi<
|
|
66
|
+
typeof fullApi,
|
|
67
|
+
FunctionReference<any, "internal">
|
|
68
|
+
> = anyApi as any;
|
|
69
|
+
|
|
70
|
+
export const components = componentsGeneric() as unknown as {};
|