vinextauth 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/context.tsx","../../src/react/provider.tsx","../../src/react/hooks.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["\"use client\";\n\nimport React, { createContext, useContext } from \"react\";\nimport type { SessionContextValue, Session } from \"../types.js\";\n\nexport const SessionContext = createContext<SessionContextValue>({\n data: null,\n status: \"loading\",\n update: async () => null,\n});\n\nexport function useSessionContext(): SessionContextValue {\n return useContext(SessionContext);\n}\n\n// Module-level fetch deduplication\nlet fetchPromise: Promise<Session | null> | null = null;\n\nexport async function fetchSession(basePath = \"/api/auth\"): Promise<Session | null> {\n if (fetchPromise) return fetchPromise;\n\n fetchPromise = fetch(`${basePath}/session`, {\n credentials: \"same-origin\",\n headers: { \"Content-Type\": \"application/json\" },\n })\n .then(async (res) => {\n if (!res.ok) return null;\n const data = await res.json() as Record<string, unknown>;\n if (!data || !data.user) return null;\n return data as Session;\n })\n .catch(() => null)\n .finally(() => {\n fetchPromise = null;\n });\n\n return fetchPromise;\n}\n\nexport async function fetchCsrfToken(basePath = \"/api/auth\"): Promise<string> {\n const res = await fetch(`${basePath}/csrf`, { credentials: \"same-origin\" });\n const data = await res.json() as { csrfToken: string };\n return data.csrfToken ?? \"\";\n}\n","\"use client\";\n\nimport { useState, useEffect, useCallback, type ReactNode } from \"react\";\nimport type { Session, SessionContextValue } from \"../types.js\";\nimport { SessionContext, fetchSession } from \"./context.js\";\n\ninterface SessionProviderProps {\n children: ReactNode;\n /** Pass server-side session to avoid client waterfall */\n session?: Session | null;\n basePath?: string;\n refetchInterval?: number; // seconds\n refetchOnWindowFocus?: boolean;\n}\n\nexport function SessionProvider({\n children,\n session: initialSession,\n basePath = \"/api/auth\",\n refetchInterval,\n refetchOnWindowFocus = true,\n}: SessionProviderProps) {\n const [session, setSession] = useState<Session | null>(initialSession ?? null);\n const [status, setStatus] = useState<SessionContextValue[\"status\"]>(\n initialSession !== undefined ? (initialSession ? \"authenticated\" : \"unauthenticated\") : \"loading\"\n );\n\n const loadSession = useCallback(async () => {\n setStatus(\"loading\");\n const data = await fetchSession(basePath);\n setSession(data);\n setStatus(data ? \"authenticated\" : \"unauthenticated\");\n }, [basePath]);\n\n const update = useCallback(async (data?: Partial<Session>): Promise<Session | null> => {\n if (data) {\n const updated = session ? { ...session, ...data } : null;\n setSession(updated);\n return updated;\n }\n await loadSession();\n return session;\n }, [session, loadSession]);\n\n useEffect(() => {\n if (initialSession !== undefined) {\n setSession(initialSession);\n setStatus(initialSession ? \"authenticated\" : \"unauthenticated\");\n return;\n }\n loadSession();\n }, [initialSession, loadSession]);\n\n // Refetch on window focus\n useEffect(() => {\n if (!refetchOnWindowFocus) return;\n\n const onFocus = () => {\n if (document.visibilityState === \"visible\") {\n loadSession();\n }\n };\n\n document.addEventListener(\"visibilitychange\", onFocus);\n return () => document.removeEventListener(\"visibilitychange\", onFocus);\n }, [refetchOnWindowFocus, loadSession]);\n\n // Refetch on interval\n useEffect(() => {\n if (!refetchInterval) return;\n const interval = setInterval(loadSession, refetchInterval * 1000);\n return () => clearInterval(interval);\n }, [refetchInterval, loadSession]);\n\n return (\n <SessionContext.Provider value={{ data: session, status, update }}>\n {children}\n </SessionContext.Provider>\n );\n}\n\nexport default SessionProvider;\n","\"use client\";\n\nimport { useSessionContext, fetchCsrfToken } from \"./context.js\";\nimport type { SessionContextValue, SignInOptions, SignOutOptions } from \"../types.js\";\n\nexport function useSession(): SessionContextValue {\n return useSessionContext();\n}\n\n/**\n * signIn — redirect to OAuth provider or sign-in page.\n *\n * Usage (identical to NextAuth v4):\n * ```ts\n * signIn(\"google\")\n * signIn(\"google\", { callbackUrl: \"/dashboard\" })\n * signIn() // goes to /api/auth/signin (provider list)\n * ```\n */\nexport function signIn(\n provider?: string,\n options?: SignInOptions,\n basePath = \"/api/auth\"\n): void {\n const callbackUrl = options?.callbackUrl ?? window.location.href;\n\n if (provider) {\n const url = `${basePath}/signin/${provider}?callbackUrl=${encodeURIComponent(callbackUrl)}`;\n window.location.href = url;\n } else {\n window.location.href = `${basePath}/signin?callbackUrl=${encodeURIComponent(callbackUrl)}`;\n }\n}\n\n/**\n * signOut — POST to signout endpoint, then redirect.\n *\n * Usage (identical to NextAuth v4):\n * ```ts\n * signOut()\n * signOut({ callbackUrl: \"/login\" })\n * ```\n */\nexport async function signOut(\n options?: SignOutOptions,\n basePath = \"/api/auth\"\n): Promise<void> {\n const callbackUrl = options?.callbackUrl ?? window.location.origin;\n\n const csrfToken = await fetchCsrfToken(basePath);\n\n await fetch(`${basePath}/signout`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"same-origin\",\n body: JSON.stringify({ csrfToken, callbackUrl }),\n });\n\n window.location.href = callbackUrl;\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import { S as Session, V as VinextAuthConfig } from '../types-G_m6Z3Iz.js';
2
+
3
+ /**
4
+ * getServerSession — drop-in for NextAuth v4's getServerSession.
5
+ *
6
+ * Usage (identical to NextAuth v4):
7
+ * ```ts
8
+ * const session = await getServerSession(authOptions)
9
+ * ```
10
+ *
11
+ * Works in:
12
+ * - Next.js App Router Server Components
13
+ * - Server Actions
14
+ * - Route Handlers
15
+ * - Vinext/Cloudflare Workers
16
+ */
17
+ declare function getServerSession(config?: VinextAuthConfig): Promise<Session | null>;
18
+ /**
19
+ * auth — zero-argument alias for Auth.js v5 style usage.
20
+ * Requires VinextAuth() to have been called first (e.g., in the route handler).
21
+ */
22
+ declare function auth(): Promise<Session | null>;
23
+
24
+ export { auth, getServerSession, getServerSession as getSession };
@@ -0,0 +1,245 @@
1
+ // src/cookies/strategy.ts
2
+ var SESSION_TOKEN_COOKIE = "vinextauth.session-token";
3
+ var CALLBACK_URL_COOKIE = "vinextauth.callback-url";
4
+ var CSRF_TOKEN_COOKIE = "vinextauth.csrf-token";
5
+ var STATE_COOKIE = "vinextauth.state";
6
+ var NONCE_COOKIE = "vinextauth.nonce";
7
+ var SECURE_PREFIX = "__Secure-";
8
+ function buildCookieNames(useSecure) {
9
+ const prefix = useSecure ? SECURE_PREFIX : "";
10
+ return {
11
+ sessionToken: {
12
+ name: `${prefix}${SESSION_TOKEN_COOKIE}`,
13
+ options: {
14
+ httpOnly: true,
15
+ sameSite: "lax",
16
+ path: "/",
17
+ secure: useSecure
18
+ }
19
+ },
20
+ callbackUrl: {
21
+ name: `${prefix}${CALLBACK_URL_COOKIE}`,
22
+ options: {
23
+ httpOnly: true,
24
+ sameSite: "lax",
25
+ path: "/",
26
+ secure: useSecure
27
+ }
28
+ },
29
+ csrfToken: {
30
+ name: `${CSRF_TOKEN_COOKIE}`,
31
+ options: {
32
+ httpOnly: true,
33
+ sameSite: "lax",
34
+ path: "/",
35
+ secure: useSecure
36
+ }
37
+ },
38
+ state: {
39
+ name: `${prefix}${STATE_COOKIE}`,
40
+ options: {
41
+ httpOnly: true,
42
+ sameSite: "lax",
43
+ path: "/",
44
+ secure: useSecure,
45
+ maxAge: 60 * 15
46
+ // 15 minutes
47
+ }
48
+ },
49
+ nonce: {
50
+ name: `${prefix}${NONCE_COOKIE}`,
51
+ options: {
52
+ httpOnly: true,
53
+ sameSite: "lax",
54
+ path: "/",
55
+ secure: useSecure,
56
+ maxAge: 60 * 15
57
+ }
58
+ }
59
+ };
60
+ }
61
+
62
+ // src/core/config.ts
63
+ var DEFAULT_MAX_AGE = 30 * 24 * 60 * 60;
64
+ function resolveConfig(config) {
65
+ const secret = config.secret ?? (typeof process !== "undefined" ? process.env.NEXTAUTH_SECRET ?? process.env.VINEXTAUTH_SECRET : void 0);
66
+ if (!secret) {
67
+ throw new Error(
68
+ "[VinextAuth] No secret provided. Set NEXTAUTH_SECRET or VINEXTAUTH_SECRET env var, or pass `secret` to VinextAuth()."
69
+ );
70
+ }
71
+ const baseUrl = (typeof process !== "undefined" ? process.env.NEXTAUTH_URL ?? process.env.VINEXTAUTH_URL ?? process.env.VERCEL_URL : void 0) ?? "http://localhost:3000";
72
+ const normalizedBaseUrl = baseUrl.startsWith("http") ? baseUrl : `https://${baseUrl}`;
73
+ const useSecureCookies = config.useSecureCookies ?? normalizedBaseUrl.startsWith("https://");
74
+ const sessionMaxAge = config.session?.maxAge ?? DEFAULT_MAX_AGE;
75
+ return {
76
+ providers: config.providers,
77
+ secret,
78
+ baseUrl: normalizedBaseUrl,
79
+ basePath: "/api/auth",
80
+ callbacks: config.callbacks ?? {},
81
+ pages: {
82
+ signIn: "/api/auth/signin",
83
+ signOut: "/api/auth/signout",
84
+ error: "/api/auth/error",
85
+ verifyRequest: "/api/auth/verify-request",
86
+ newUser: "/",
87
+ ...config.pages
88
+ },
89
+ session: {
90
+ strategy: config.session?.strategy ?? "jwt",
91
+ maxAge: sessionMaxAge,
92
+ updateAge: config.session?.updateAge ?? 24 * 60 * 60
93
+ },
94
+ jwt: {
95
+ secret,
96
+ maxAge: sessionMaxAge,
97
+ encode: config.jwt?.encode,
98
+ decode: config.jwt?.decode
99
+ },
100
+ debug: config.debug ?? false,
101
+ useSecureCookies,
102
+ cookies: {
103
+ ...buildCookieNames(useSecureCookies),
104
+ ...config.cookies
105
+ },
106
+ adapter: config.adapter
107
+ };
108
+ }
109
+
110
+ // src/jwt/keys.ts
111
+ var keyCache = /* @__PURE__ */ new Map();
112
+ async function deriveKey(secret) {
113
+ const cached = keyCache.get(secret);
114
+ if (cached) return cached;
115
+ const encoder = new TextEncoder();
116
+ const keyData = encoder.encode(secret);
117
+ const key = await crypto.subtle.importKey(
118
+ "raw",
119
+ keyData,
120
+ { name: "HMAC", hash: "SHA-256" },
121
+ false,
122
+ ["sign", "verify"]
123
+ );
124
+ keyCache.set(secret, key);
125
+ return key;
126
+ }
127
+
128
+ // src/jwt/index.ts
129
+ function base64urlEncode(data) {
130
+ const bytes = new Uint8Array(data);
131
+ let binary = "";
132
+ for (const byte of bytes) {
133
+ binary += String.fromCharCode(byte);
134
+ }
135
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
136
+ }
137
+ function base64urlDecode(str) {
138
+ const base64 = str.replace(/-/g, "+").replace(/_/g, "/");
139
+ const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
140
+ const binary = atob(padded);
141
+ const bytes = new Uint8Array(binary.length);
142
+ for (let i = 0; i < binary.length; i++) {
143
+ bytes[i] = binary.charCodeAt(i);
144
+ }
145
+ return bytes;
146
+ }
147
+ function encodeJson(obj) {
148
+ return base64urlEncode(new TextEncoder().encode(JSON.stringify(obj)).buffer);
149
+ }
150
+ function decodeJson(str) {
151
+ return JSON.parse(new TextDecoder().decode(base64urlDecode(str)));
152
+ }
153
+ encodeJson({ alg: "HS256", typ: "JWT" });
154
+ async function verify(token, secret) {
155
+ try {
156
+ const parts = token.split(".");
157
+ if (parts.length !== 3) return null;
158
+ const [header, payload, sig] = parts;
159
+ const message = `${header}.${payload}`;
160
+ const key = await deriveKey(secret);
161
+ const signatureBytes = base64urlDecode(sig);
162
+ const valid = await crypto.subtle.verify(
163
+ "HMAC",
164
+ key,
165
+ signatureBytes,
166
+ new TextEncoder().encode(message)
167
+ );
168
+ if (!valid) return null;
169
+ const decoded = decodeJson(payload);
170
+ if (decoded.exp && decoded.exp < Math.floor(Date.now() / 1e3)) {
171
+ return null;
172
+ }
173
+ return decoded;
174
+ } catch {
175
+ return null;
176
+ }
177
+ }
178
+
179
+ // src/cookies/index.ts
180
+ function sessionToExpires(maxAge) {
181
+ return new Date(Date.now() + maxAge * 1e3).toISOString();
182
+ }
183
+ function buildSessionFromJWT(jwt, maxAge) {
184
+ return {
185
+ user: {
186
+ id: jwt.sub ?? "",
187
+ name: jwt.name ?? null,
188
+ email: jwt.email ?? null,
189
+ image: jwt.picture ?? null
190
+ },
191
+ expires: sessionToExpires(maxAge)
192
+ };
193
+ }
194
+
195
+ // src/core/session.ts
196
+ async function decodeSession(token, config) {
197
+ if (config.jwt.decode) {
198
+ return config.jwt.decode({ token, secret: config.secret });
199
+ }
200
+ return verify(token, config.secret);
201
+ }
202
+ async function buildSession(jwt, config) {
203
+ const baseSession = buildSessionFromJWT(jwt, config.session.maxAge);
204
+ if (config.callbacks.session) {
205
+ return config.callbacks.session({ session: baseSession, token: jwt });
206
+ }
207
+ return baseSession;
208
+ }
209
+
210
+ // src/handlers/index.ts
211
+ var _resolvedConfig = null;
212
+ function getResolvedConfig() {
213
+ return _resolvedConfig;
214
+ }
215
+
216
+ // src/server/index.ts
217
+ async function getServerSession(config) {
218
+ const resolved = config ? resolveConfig(config) : getResolvedConfig();
219
+ if (!resolved) {
220
+ console.warn(
221
+ "[VinextAuth] getServerSession called before VinextAuth() was initialized. Pass authOptions directly: getServerSession(authOptions)"
222
+ );
223
+ return null;
224
+ }
225
+ let token = null;
226
+ try {
227
+ const { cookies } = await import('next/headers');
228
+ const cookieStore = await cookies();
229
+ const secureName = `__Secure-${resolved.cookies.sessionToken.name.replace("__Secure-", "")}`;
230
+ token = cookieStore.get(secureName)?.value ?? cookieStore.get(resolved.cookies.sessionToken.name)?.value ?? null;
231
+ } catch {
232
+ return null;
233
+ }
234
+ if (!token) return null;
235
+ const jwt = await decodeSession(token, resolved);
236
+ if (!jwt) return null;
237
+ return buildSession(jwt, resolved);
238
+ }
239
+ async function auth() {
240
+ return getServerSession();
241
+ }
242
+
243
+ export { auth, getServerSession, getServerSession as getSession };
244
+ //# sourceMappingURL=index.js.map
245
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cookies/strategy.ts","../../src/core/config.ts","../../src/jwt/keys.ts","../../src/jwt/index.ts","../../src/cookies/index.ts","../../src/core/session.ts","../../src/handlers/index.ts","../../src/server/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { CookiesConfig } from \"../types.js\";\n\nexport const SESSION_TOKEN_COOKIE = \"vinextauth.session-token\";\nexport const CALLBACK_URL_COOKIE = \"vinextauth.callback-url\";\nexport const CSRF_TOKEN_COOKIE = \"vinextauth.csrf-token\";\nexport const STATE_COOKIE = \"vinextauth.state\";\nexport const NONCE_COOKIE = \"vinextauth.nonce\";\n\n// Secure prefix for HTTPS\nconst SECURE_PREFIX = \"__Secure-\";\n\nexport function buildCookieNames(useSecure: boolean): CookiesConfig {\n const prefix = useSecure ? SECURE_PREFIX : \"\";\n\n return {\n sessionToken: {\n name: `${prefix}${SESSION_TOKEN_COOKIE}`,\n options: {\n httpOnly: true,\n sameSite: \"lax\",\n path: \"/\",\n secure: useSecure,\n },\n },\n callbackUrl: {\n name: `${prefix}${CALLBACK_URL_COOKIE}`,\n options: {\n httpOnly: true,\n sameSite: \"lax\",\n path: \"/\",\n secure: useSecure,\n },\n },\n csrfToken: {\n name: `${CSRF_TOKEN_COOKIE}`,\n options: {\n httpOnly: true,\n sameSite: \"lax\",\n path: \"/\",\n secure: useSecure,\n },\n },\n state: {\n name: `${prefix}${STATE_COOKIE}`,\n options: {\n httpOnly: true,\n sameSite: \"lax\",\n path: \"/\",\n secure: useSecure,\n maxAge: 60 * 15, // 15 minutes\n },\n },\n nonce: {\n name: `${prefix}${NONCE_COOKIE}`,\n options: {\n httpOnly: true,\n sameSite: \"lax\",\n path: \"/\",\n secure: useSecure,\n maxAge: 60 * 15,\n },\n },\n };\n}\n\nexport function serializeCookie(name: string, value: string, options: CookiesConfig[keyof CookiesConfig][\"options\"] & { maxAge?: number }): string {\n let cookie = `${name}=${encodeURIComponent(value)}`;\n\n if (options.httpOnly) cookie += \"; HttpOnly\";\n if (options.secure) cookie += \"; Secure\";\n if (options.sameSite) cookie += `; SameSite=${capitalize(options.sameSite)}`;\n if (options.path) cookie += `; Path=${options.path}`;\n if (options.maxAge !== undefined) cookie += `; Max-Age=${options.maxAge}`;\n if (options.domain) cookie += `; Domain=${options.domain}`;\n\n return cookie;\n}\n\nexport function deleteCookieString(name: string, options: CookiesConfig[keyof CookiesConfig][\"options\"]): string {\n return serializeCookie(name, \"\", { ...options, maxAge: 0 });\n}\n\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n","import type { VinextAuthConfig, ResolvedConfig } from \"../types.js\";\nimport { buildCookieNames } from \"../cookies/strategy.js\";\n\nconst DEFAULT_MAX_AGE = 30 * 24 * 60 * 60; // 30 days\n\nexport function resolveConfig(config: VinextAuthConfig): ResolvedConfig {\n const secret =\n config.secret ??\n (typeof process !== \"undefined\"\n ? process.env.NEXTAUTH_SECRET ?? process.env.VINEXTAUTH_SECRET\n : undefined);\n\n if (!secret) {\n throw new Error(\n \"[VinextAuth] No secret provided. Set NEXTAUTH_SECRET or VINEXTAUTH_SECRET env var, or pass `secret` to VinextAuth().\"\n );\n }\n\n const baseUrl =\n (typeof process !== \"undefined\"\n ? process.env.NEXTAUTH_URL ?? process.env.VINEXTAUTH_URL ?? process.env.VERCEL_URL\n : undefined) ?? \"http://localhost:3000\";\n\n const normalizedBaseUrl = baseUrl.startsWith(\"http\") ? baseUrl : `https://${baseUrl}`;\n\n const useSecureCookies =\n config.useSecureCookies ?? normalizedBaseUrl.startsWith(\"https://\");\n\n const sessionMaxAge = config.session?.maxAge ?? DEFAULT_MAX_AGE;\n\n return {\n providers: config.providers,\n secret,\n baseUrl: normalizedBaseUrl,\n basePath: \"/api/auth\",\n callbacks: config.callbacks ?? {},\n pages: {\n signIn: \"/api/auth/signin\",\n signOut: \"/api/auth/signout\",\n error: \"/api/auth/error\",\n verifyRequest: \"/api/auth/verify-request\",\n newUser: \"/\",\n ...config.pages,\n },\n session: {\n strategy: config.session?.strategy ?? \"jwt\",\n maxAge: sessionMaxAge,\n updateAge: config.session?.updateAge ?? 24 * 60 * 60,\n },\n jwt: {\n secret,\n maxAge: sessionMaxAge,\n encode: config.jwt?.encode as ResolvedConfig[\"jwt\"][\"encode\"],\n decode: config.jwt?.decode as ResolvedConfig[\"jwt\"][\"decode\"],\n },\n debug: config.debug ?? false,\n useSecureCookies,\n cookies: {\n ...buildCookieNames(useSecureCookies),\n ...config.cookies,\n },\n adapter: config.adapter,\n };\n}\n","// Key cache to avoid re-importing on every request\nconst keyCache = new Map<string, CryptoKey>();\n\nexport async function deriveKey(secret: string): Promise<CryptoKey> {\n const cached = keyCache.get(secret);\n if (cached) return cached;\n\n const encoder = new TextEncoder();\n const keyData = encoder.encode(secret);\n\n const key = await crypto.subtle.importKey(\n \"raw\",\n keyData,\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\", \"verify\"]\n );\n\n keyCache.set(secret, key);\n return key;\n}\n","import type { JWT } from \"../types.js\";\nimport { deriveKey } from \"./keys.js\";\n\n// ─── Base64url helpers ────────────────────────────────────────────────────────\n\nfunction base64urlEncode(data: ArrayBuffer): string {\n const bytes = new Uint8Array(data);\n let binary = \"\";\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=/g, \"\");\n}\n\nfunction base64urlDecode(str: string): Uint8Array {\n const base64 = str.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), \"=\");\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\nfunction encodeJson(obj: unknown): string {\n return base64urlEncode(new TextEncoder().encode(JSON.stringify(obj)).buffer as ArrayBuffer);\n}\n\nfunction decodeJson(str: string): unknown {\n return JSON.parse(new TextDecoder().decode(base64urlDecode(str)));\n}\n\n// ─── JWT operations ───────────────────────────────────────────────────────────\n\nconst HEADER = encodeJson({ alg: \"HS256\", typ: \"JWT\" });\n\nexport async function sign(payload: JWT, secret: string): Promise<string> {\n const encodedPayload = encodeJson(payload);\n const message = `${HEADER}.${encodedPayload}`;\n\n const key = await deriveKey(secret);\n const signature = await crypto.subtle.sign(\n \"HMAC\",\n key,\n new TextEncoder().encode(message)\n );\n\n return `${message}.${base64urlEncode(signature)}`;\n}\n\nexport async function verify(token: string, secret: string): Promise<JWT | null> {\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3) return null;\n\n const [header, payload, sig] = parts;\n const message = `${header}.${payload}`;\n\n const key = await deriveKey(secret);\n const signatureBytes = base64urlDecode(sig);\n\n const valid = await crypto.subtle.verify(\n \"HMAC\",\n key,\n signatureBytes,\n new TextEncoder().encode(message)\n );\n\n if (!valid) return null;\n\n const decoded = decodeJson(payload) as JWT;\n\n // Check expiry\n if (decoded.exp && decoded.exp < Math.floor(Date.now() / 1000)) {\n return null;\n }\n\n return decoded;\n } catch {\n return null;\n }\n}\n\nexport function decode(token: string): JWT | null {\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3) return null;\n return decodeJson(parts[1]) as JWT;\n } catch {\n return null;\n }\n}\n","import type { ResolvedConfig, Session } from \"../types.js\";\nimport { serializeCookie, deleteCookieString } from \"./strategy.js\";\n\n// ─── Read ─────────────────────────────────────────────────────────────────────\n\nexport function getSessionToken(request: Request, config: ResolvedConfig): string | null {\n return getCookieValue(request, config.cookies.sessionToken.name);\n}\n\nexport function getCallbackUrl(request: Request, config: ResolvedConfig): string | null {\n return getCookieValue(request, config.cookies.callbackUrl.name);\n}\n\nexport function getCsrfCookie(request: Request, config: ResolvedConfig): string | null {\n return getCookieValue(request, config.cookies.csrfToken.name);\n}\n\nexport function getStateCookie(request: Request, config: ResolvedConfig): string | null {\n return getCookieValue(request, config.cookies.state.name);\n}\n\nfunction getCookieValue(request: Request, name: string): string | null {\n const cookieHeader = request.headers.get(\"cookie\") ?? \"\";\n for (const part of cookieHeader.split(\";\")) {\n const [key, ...val] = part.trim().split(\"=\");\n if (key.trim() === name) {\n return decodeURIComponent(val.join(\"=\"));\n }\n }\n return null;\n}\n\n// ─── Set ──────────────────────────────────────────────────────────────────────\n\nexport function applySessionCookie(headers: Headers, token: string, config: ResolvedConfig): void {\n const { name, options } = config.cookies.sessionToken;\n headers.append(\n \"Set-Cookie\",\n serializeCookie(name, token, { ...options, maxAge: config.session.maxAge })\n );\n}\n\nexport function applyCallbackUrlCookie(headers: Headers, url: string, config: ResolvedConfig): void {\n const { name, options } = config.cookies.callbackUrl;\n headers.append(\"Set-Cookie\", serializeCookie(name, url, { ...options, maxAge: 60 * 10 }));\n}\n\nexport function applyCsrfCookie(headers: Headers, value: string, config: ResolvedConfig): void {\n const { name, options } = config.cookies.csrfToken;\n headers.append(\"Set-Cookie\", serializeCookie(name, value, options));\n}\n\nexport function applyStateCookie(headers: Headers, state: string, config: ResolvedConfig): void {\n const { name, options } = config.cookies.state;\n headers.append(\"Set-Cookie\", serializeCookie(name, state, options));\n}\n\n// ─── Delete ───────────────────────────────────────────────────────────────────\n\nexport function clearSessionCookie(headers: Headers, config: ResolvedConfig): void {\n const { name, options } = config.cookies.sessionToken;\n headers.append(\"Set-Cookie\", deleteCookieString(name, options));\n}\n\nexport function clearStateCookie(headers: Headers, config: ResolvedConfig): void {\n const { name, options } = config.cookies.state;\n headers.append(\"Set-Cookie\", deleteCookieString(name, options));\n}\n\nexport function clearCallbackUrlCookie(headers: Headers, config: ResolvedConfig): void {\n const { name, options } = config.cookies.callbackUrl;\n headers.append(\"Set-Cookie\", deleteCookieString(name, options));\n}\n\n// ─── Session cookie helpers ───────────────────────────────────────────────────\n\nexport function sessionToExpires(maxAge: number): string {\n return new Date(Date.now() + maxAge * 1000).toISOString();\n}\n\nexport function buildSessionFromJWT(\n jwt: Record<string, unknown>,\n maxAge: number\n): Session {\n return {\n user: {\n id: (jwt.sub as string) ?? \"\",\n name: (jwt.name as string | null) ?? null,\n email: (jwt.email as string | null) ?? null,\n image: (jwt.picture as string | null) ?? null,\n },\n expires: sessionToExpires(maxAge),\n };\n}\n","import type { JWT, Session, ResolvedConfig } from \"../types.js\";\nimport * as jwtLib from \"../jwt/index.js\";\nimport { buildSessionFromJWT } from \"../cookies/index.js\";\n\nexport async function encodeSession(\n payload: JWT,\n config: ResolvedConfig\n): Promise<string> {\n if (config.jwt.encode) {\n return config.jwt.encode({\n token: payload,\n secret: config.secret,\n maxAge: config.session.maxAge,\n });\n }\n return jwtLib.sign(payload, config.secret);\n}\n\nexport async function decodeSession(\n token: string,\n config: ResolvedConfig\n): Promise<JWT | null> {\n if (config.jwt.decode) {\n return config.jwt.decode({ token, secret: config.secret });\n }\n return jwtLib.verify(token, config.secret);\n}\n\nexport async function buildSession(\n jwt: JWT,\n config: ResolvedConfig\n): Promise<Session> {\n const baseSession = buildSessionFromJWT(jwt, config.session.maxAge);\n\n if (config.callbacks.session) {\n return config.callbacks.session({ session: baseSession, token: jwt });\n }\n\n return baseSession;\n}\n\nexport async function buildJWT(\n user: { id: string; name?: string | null; email?: string | null; image?: string | null },\n account: Parameters<NonNullable<ResolvedConfig[\"callbacks\"][\"jwt\"]>>[0][\"account\"],\n profile: Record<string, unknown> | undefined,\n config: ResolvedConfig\n): Promise<JWT> {\n const now = Math.floor(Date.now() / 1000);\n\n let token: JWT = {\n sub: user.id,\n name: user.name,\n email: user.email,\n picture: user.image,\n iat: now,\n exp: now + config.session.maxAge,\n jti: generateId(),\n };\n\n if (config.callbacks.jwt) {\n token = await config.callbacks.jwt({\n token,\n user,\n account,\n profile,\n trigger: \"signIn\",\n });\n }\n\n return token;\n}\n\nfunction generateId(): string {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n","import type { VinextAuthConfig, VinextAuthHandlers } from \"../types.js\";\nimport { resolveConfig } from \"../core/config.js\";\nimport { handleSignIn } from \"./signin.js\";\nimport { handleCallback } from \"./callback.js\";\nimport { handleSignOut } from \"./signout.js\";\nimport { handleSessionRoute } from \"./session-route.js\";\nimport { handleCsrfRoute } from \"./csrf-route.js\";\n\n// Module-level config storage for server-side helpers (getServerSession)\nlet _resolvedConfig: ReturnType<typeof resolveConfig> | null = null;\n\nexport function getResolvedConfig() {\n return _resolvedConfig;\n}\n\n/**\n * VinextAuth — main factory function.\n *\n * Usage (identical to NextAuth v4):\n * ```ts\n * const handler = VinextAuth(authOptions)\n * export { handler as GET, handler as POST }\n * ```\n */\nexport function VinextAuth(config: VinextAuthConfig): VinextAuthHandlers {\n const resolved = resolveConfig(config);\n _resolvedConfig = resolved;\n\n async function handler(request: Request): Promise<Response> {\n const url = new URL(request.url);\n\n // Extract path segments after /api/auth/\n const basePath = resolved.basePath; // \"/api/auth\"\n const pathname = url.pathname;\n\n if (!pathname.startsWith(basePath)) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n const action = pathname.slice(basePath.length).replace(/^\\//, \"\");\n // action examples: \"signin/google\", \"callback/google\", \"signout\", \"session\", \"csrf\", \"error\"\n\n const parts = action.split(\"/\");\n const verb = parts[0];\n const param = parts[1]; // provider id, if any\n\n // Route\n if (verb === \"signin\" && param) {\n return handleSignIn(request, param, resolved);\n }\n\n if (verb === \"signin\" && !param) {\n // Show built-in signin page (list providers)\n return handleSignInPage(resolved);\n }\n\n if (verb === \"callback\" && param) {\n return handleCallback(request, param, resolved);\n }\n\n if (verb === \"signout\") {\n if (request.method === \"POST\") {\n return handleSignOut(request, resolved);\n }\n // GET signout — show confirmation page or just do it\n return handleSignOut(request, resolved);\n }\n\n if (verb === \"session\") {\n return handleSessionRoute(request, resolved);\n }\n\n if (verb === \"csrf\") {\n return handleCsrfRoute(request, resolved);\n }\n\n if (verb === \"error\") {\n const error = url.searchParams.get(\"error\") ?? \"Unknown\";\n return new Response(\n `<!DOCTYPE html><html><body><h1>Authentication Error</h1><p>${error}</p><a href=\"${resolved.pages.signIn}\">Try again</a></body></html>`,\n { status: 400, headers: { \"Content-Type\": \"text/html\" } }\n );\n }\n\n return new Response(\"Not Found\", { status: 404 });\n }\n\n return { GET: handler, POST: handler };\n}\n\nfunction handleSignInPage(config: ReturnType<typeof resolveConfig>): Response {\n const providers = config.providers.map((p) => `\n <a href=\"${config.basePath}/signin/${p.id}\" style=\"display:block;margin:8px 0;padding:12px 24px;border:1px solid #ccc;border-radius:6px;text-decoration:none;color:#000;\">\n Sign in with ${p.name}\n </a>\n `).join(\"\");\n\n return new Response(\n `<!DOCTYPE html><html><body style=\"font-family:sans-serif;max-width:400px;margin:80px auto;padding:24px\">\n <h1>Sign In</h1>${providers}\n </body></html>`,\n { headers: { \"Content-Type\": \"text/html\" } }\n );\n}\n\nexport default VinextAuth;\n","import type { Session, VinextAuthConfig } from \"../types.js\";\nimport { resolveConfig } from \"../core/config.js\";\nimport { decodeSession, buildSession } from \"../core/session.js\";\nimport { getResolvedConfig } from \"../handlers/index.js\";\n\n/**\n * getServerSession — drop-in for NextAuth v4's getServerSession.\n *\n * Usage (identical to NextAuth v4):\n * ```ts\n * const session = await getServerSession(authOptions)\n * ```\n *\n * Works in:\n * - Next.js App Router Server Components\n * - Server Actions\n * - Route Handlers\n * - Vinext/Cloudflare Workers\n */\nexport async function getServerSession(\n config?: VinextAuthConfig\n): Promise<Session | null> {\n const resolved = config ? resolveConfig(config) : getResolvedConfig();\n\n if (!resolved) {\n console.warn(\n \"[VinextAuth] getServerSession called before VinextAuth() was initialized. \" +\n \"Pass authOptions directly: getServerSession(authOptions)\"\n );\n return null;\n }\n\n // Try to get the session token from headers (Next.js server context)\n let token: string | null = null;\n\n try {\n // Dynamic import to avoid breaking in non-Next.js environments\n const { cookies } = await import(\"next/headers\");\n const cookieStore = await cookies();\n\n const secureName = `__Secure-${resolved.cookies.sessionToken.name.replace(\"__Secure-\", \"\")}`;\n token =\n cookieStore.get(secureName)?.value ??\n cookieStore.get(resolved.cookies.sessionToken.name)?.value ??\n null;\n } catch {\n // Not in Next.js context — token not available via this method\n return null;\n }\n\n if (!token) return null;\n\n const jwt = await decodeSession(token, resolved);\n if (!jwt) return null;\n\n return buildSession(jwt, resolved);\n}\n\n/**\n * auth — zero-argument alias for Auth.js v5 style usage.\n * Requires VinextAuth() to have been called first (e.g., in the route handler).\n */\nexport async function auth(): Promise<Session | null> {\n return getServerSession();\n}\n\n// NextAuth v4 compat alias\nexport { getServerSession as getSession };\n"]}
@@ -0,0 +1,180 @@
1
+ interface User {
2
+ id: string;
3
+ name?: string | null;
4
+ email?: string | null;
5
+ image?: string | null;
6
+ }
7
+ interface Session {
8
+ user: User;
9
+ expires: string;
10
+ }
11
+ interface JWT {
12
+ sub?: string;
13
+ name?: string | null;
14
+ email?: string | null;
15
+ picture?: string | null;
16
+ iat?: number;
17
+ exp?: number;
18
+ jti?: string;
19
+ [key: string]: unknown;
20
+ }
21
+ interface OAuthProvider {
22
+ id: string;
23
+ name: string;
24
+ type: "oauth";
25
+ clientId: string;
26
+ clientSecret: string;
27
+ authorization: {
28
+ url: string;
29
+ params?: Record<string, string>;
30
+ };
31
+ token: {
32
+ url: string;
33
+ };
34
+ userinfo: {
35
+ url: string;
36
+ };
37
+ profile(profile: Record<string, unknown>): User;
38
+ checks?: Array<"state" | "pkce" | "none">;
39
+ scope?: string;
40
+ }
41
+ interface AdapterSession {
42
+ sessionToken: string;
43
+ userId: string;
44
+ expires: Date;
45
+ }
46
+ interface AdapterInterface {
47
+ getSession(sessionToken: string): Promise<(AdapterSession & {
48
+ user: User;
49
+ }) | null>;
50
+ createSession(session: AdapterSession): Promise<AdapterSession>;
51
+ updateSession(session: Partial<AdapterSession> & {
52
+ sessionToken: string;
53
+ }): Promise<AdapterSession | null>;
54
+ deleteSession(sessionToken: string): Promise<void>;
55
+ }
56
+ interface SignInCallbackParams {
57
+ user: User;
58
+ account: {
59
+ provider: string;
60
+ type: string;
61
+ providerAccountId: string;
62
+ access_token?: string;
63
+ refresh_token?: string;
64
+ expires_at?: number;
65
+ token_type?: string;
66
+ scope?: string;
67
+ id_token?: string;
68
+ } | null;
69
+ profile?: Record<string, unknown>;
70
+ }
71
+ interface SessionCallbackParams {
72
+ session: Session;
73
+ token: JWT;
74
+ user?: User;
75
+ }
76
+ interface JWTCallbackParams {
77
+ token: JWT;
78
+ user?: User;
79
+ account?: SignInCallbackParams["account"];
80
+ profile?: Record<string, unknown>;
81
+ trigger?: "signIn" | "signUp" | "update";
82
+ isNewUser?: boolean;
83
+ session?: Session;
84
+ }
85
+ interface CallbacksConfig {
86
+ signIn?: (params: SignInCallbackParams) => Promise<boolean | string> | boolean | string;
87
+ session?: (params: SessionCallbackParams) => Promise<Session> | Session;
88
+ jwt?: (params: JWTCallbackParams) => Promise<JWT> | JWT;
89
+ redirect?: (params: {
90
+ url: string;
91
+ baseUrl: string;
92
+ }) => Promise<string> | string;
93
+ }
94
+ interface PagesConfig {
95
+ signIn?: string;
96
+ signOut?: string;
97
+ error?: string;
98
+ verifyRequest?: string;
99
+ newUser?: string;
100
+ }
101
+ interface SessionConfig {
102
+ strategy?: "jwt" | "database";
103
+ maxAge?: number;
104
+ updateAge?: number;
105
+ }
106
+ interface JWTConfig {
107
+ secret?: string;
108
+ maxAge?: number;
109
+ encode?: (params: {
110
+ token: JWT;
111
+ secret: string;
112
+ maxAge: number;
113
+ }) => Promise<string>;
114
+ decode?: (params: {
115
+ token: string;
116
+ secret: string;
117
+ }) => Promise<JWT | null>;
118
+ }
119
+ interface VinextAuthConfig {
120
+ providers: OAuthProvider[];
121
+ secret?: string;
122
+ callbacks?: CallbacksConfig;
123
+ pages?: PagesConfig;
124
+ session?: SessionConfig;
125
+ jwt?: JWTConfig;
126
+ adapter?: AdapterInterface;
127
+ debug?: boolean;
128
+ useSecureCookies?: boolean;
129
+ cookies?: Partial<CookiesConfig>;
130
+ }
131
+ interface CookieOption {
132
+ name: string;
133
+ options: {
134
+ httpOnly?: boolean;
135
+ sameSite?: "lax" | "strict" | "none";
136
+ path?: string;
137
+ secure?: boolean;
138
+ maxAge?: number;
139
+ domain?: string;
140
+ };
141
+ }
142
+ interface CookiesConfig {
143
+ sessionToken: CookieOption;
144
+ callbackUrl: CookieOption;
145
+ csrfToken: CookieOption;
146
+ state: CookieOption;
147
+ nonce: CookieOption;
148
+ }
149
+ interface VinextAuthHandlers {
150
+ GET: (request: Request) => Promise<Response>;
151
+ POST: (request: Request) => Promise<Response>;
152
+ }
153
+ type SessionStatus = "loading" | "authenticated" | "unauthenticated";
154
+ interface SessionContextValue {
155
+ data: Session | null;
156
+ status: SessionStatus;
157
+ update: (data?: Partial<Session>) => Promise<Session | null>;
158
+ }
159
+ interface SignInOptions {
160
+ callbackUrl?: string;
161
+ redirect?: boolean;
162
+ }
163
+ interface SignOutOptions {
164
+ callbackUrl?: string;
165
+ redirect?: boolean;
166
+ }
167
+ interface WithAuthOptions {
168
+ pages?: {
169
+ signIn?: string;
170
+ };
171
+ callbacks?: {
172
+ authorized?: (params: {
173
+ token: JWT | null;
174
+ req: Request;
175
+ }) => boolean | Promise<boolean>;
176
+ };
177
+ secret?: string;
178
+ }
179
+
180
+ export type { AdapterInterface as A, CallbacksConfig as C, JWT as J, OAuthProvider as O, PagesConfig as P, Session as S, User as U, VinextAuthConfig as V, WithAuthOptions as W, VinextAuthHandlers as a, AdapterSession as b, JWTCallbackParams as c, JWTConfig as d, SessionCallbackParams as e, SessionConfig as f, SessionContextValue as g, SessionStatus as h, SignInCallbackParams as i, SignInOptions as j, SignOutOptions as k };
package/package.json ADDED
@@ -0,0 +1,86 @@
1
+ {
2
+ "name": "vinextauth",
3
+ "version": "0.1.0",
4
+ "description": "Drop-in NextAuth v4 replacement for Vinext + Cloudflare Workers. Zero Node.js dependencies, pure Web Crypto API.",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=18"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./react": {
15
+ "types": "./dist/react/index.d.ts",
16
+ "import": "./dist/react/index.js"
17
+ },
18
+ "./server": {
19
+ "types": "./dist/server/index.d.ts",
20
+ "import": "./dist/server/index.js"
21
+ },
22
+ "./middleware": {
23
+ "types": "./dist/middleware/index.d.ts",
24
+ "import": "./dist/middleware/index.js"
25
+ },
26
+ "./providers/google": {
27
+ "types": "./dist/providers/google.d.ts",
28
+ "import": "./dist/providers/google.js"
29
+ },
30
+ "./providers/github": {
31
+ "types": "./dist/providers/github.d.ts",
32
+ "import": "./dist/providers/github.js"
33
+ },
34
+ "./adapters/cloudflare-kv": {
35
+ "types": "./dist/adapters/cloudflare-kv.d.ts",
36
+ "import": "./dist/adapters/cloudflare-kv.js"
37
+ }
38
+ },
39
+ "main": "./dist/index.js",
40
+ "types": "./dist/index.d.ts",
41
+ "sideEffects": false,
42
+ "files": [
43
+ "dist",
44
+ "README.md"
45
+ ],
46
+ "scripts": {
47
+ "build": "tsup",
48
+ "dev": "tsup --watch",
49
+ "typecheck": "tsc --noEmit",
50
+ "test": "vitest run",
51
+ "test:watch": "vitest"
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^22.0.0",
55
+ "@types/react": "^19.0.0",
56
+ "tsup": "^8.0.0",
57
+ "typescript": "^5.7.0",
58
+ "vitest": "^2.0.0"
59
+ },
60
+ "peerDependencies": {
61
+ "react": ">=18",
62
+ "react-dom": ">=18"
63
+ },
64
+ "peerDependenciesMeta": {
65
+ "react": {
66
+ "optional": true
67
+ },
68
+ "react-dom": {
69
+ "optional": true
70
+ }
71
+ },
72
+ "keywords": [
73
+ "vinext",
74
+ "nextauth",
75
+ "cloudflare-workers",
76
+ "oauth",
77
+ "authentication",
78
+ "edge-runtime",
79
+ "web-crypto"
80
+ ],
81
+ "license": "MIT",
82
+ "repository": {
83
+ "type": "git",
84
+ "url": "https://github.com/your-org/vinextauth"
85
+ }
86
+ }