diff --git a/docs/start/config.json b/docs/start/config.json index 9dec7c22211..cd9af3d0428 100644 --- a/docs/start/config.json +++ b/docs/start/config.json @@ -301,6 +301,10 @@ "label": "Material UI", "to": "framework/react/examples/start-material-ui" }, + { + "label": "Basic + Auth.js", + "to": "framework/solid/examples/start-basic-authjs" + }, { "label": "Basic + Static rendering", "to": "framework/react/examples/start-basic-static" @@ -331,9 +335,13 @@ "to": "framework/solid/examples/start-supabase-basic" }, { - "label": "Bare + Convex + Better Auth", + "label": "Basic + Convex + Better Auth", "to": "framework/solid/examples/start-convex-better-auth" }, + { + "label": "Basic + Auth.js", + "to": "framework/solid/examples/start-basic-authjs" + }, { "label": "Basic + Static rendering", "to": "framework/solid/examples/start-basic-static" diff --git a/docs/start/framework/react/guide/authentication.md b/docs/start/framework/react/guide/authentication.md index 4bee76cff70..0f5ee36dcb8 100644 --- a/docs/start/framework/react/guide/authentication.md +++ b/docs/start/framework/react/guide/authentication.md @@ -16,6 +16,7 @@ You have several options for authentication in your TanStack Start application: 1. **[Clerk](https://clerk.dev)** - Complete authentication platform with UI components 2. **[WorkOS](https://workos.com)** - Enterprise-focused with SSO and compliance features 3. **[Better Auth](https://www.better-auth.com/)** - Open-source TypeScript library +4. **[Auth.js](https://authjs.dev/)** - Open-source library supporting 80+ OAuth providers **DIY Implementation Benefits:** diff --git a/docs/start/framework/solid/guide/authentication.md b/docs/start/framework/solid/guide/authentication.md index 276381fb08e..63f5e64bb35 100644 --- a/docs/start/framework/solid/guide/authentication.md +++ b/docs/start/framework/solid/guide/authentication.md @@ -16,6 +16,7 @@ You have several options for authentication in your TanStack Start application: 1. **[Clerk](https://clerk.dev)** - Complete authentication platform with UI components 2. **[WorkOS](https://workos.com)** - Enterprise-focused with SSO and compliance features 3. **[Better Auth](https://www.better-auth.com/)** - Open-source TypeScript library +4. **[Auth.js](https://authjs.dev/)** - Open-source library supporting 80+ OAuth providers **DIY Implementation Benefits:** diff --git a/examples/react/start-basic-authjs/.env.example b/examples/react/start-basic-authjs/.env.example new file mode 100644 index 00000000000..062e707907f --- /dev/null +++ b/examples/react/start-basic-authjs/.env.example @@ -0,0 +1,11 @@ +# Auth.js Configuration +AUTH_SECRET=your-secret-key-here-min-32-chars-long + +# Auth.js URL (must include the auth path) +AUTH_URL=http://localhost:10000/api/auth + +# Auth0 Configuration (https://manage.auth0.com) +# Auth.js auto-reads these env vars for the Auth0 provider +AUTH_AUTH0_ID=your-auth0-client-id +AUTH_AUTH0_SECRET=your-auth0-client-secret +AUTH_AUTH0_ISSUER=https://your-tenant.auth0.com diff --git a/examples/react/start-basic-authjs/.gitignore b/examples/react/start-basic-authjs/.gitignore new file mode 100644 index 00000000000..dc87421f7dc --- /dev/null +++ b/examples/react/start-basic-authjs/.gitignore @@ -0,0 +1,6 @@ +node_modules +dist +.env +*.local +.DS_Store +.tanstack \ No newline at end of file diff --git a/examples/react/start-basic-authjs/package.json b/examples/react/start-basic-authjs/package.json new file mode 100644 index 00000000000..4445f8115a1 --- /dev/null +++ b/examples/react/start-basic-authjs/package.json @@ -0,0 +1,34 @@ +{ + "name": "tanstack-router-react-example-basic-authjs", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "start": "pnpx srvx --prod -s ../client dist/server/server.js" + }, + "dependencies": { + "@auth/core": "^0.41.1", + "@tanstack/react-router": "^1.139.3", + "@tanstack/react-router-devtools": "^1.139.3", + "@tanstack/react-start": "^1.139.3", + "start-authjs": "^1.0.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "tailwind-merge": "^2.6.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.15", + "@types/node": "^22.5.4", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "postcss": "^8.5.1", + "tailwindcss": "^4.1.15", + "typescript": "^5.7.2", + "vite": "^7.1.7", + "@vitejs/plugin-react": "^4.6.0", + "vite-tsconfig-paths": "^5.1.4" + } +} diff --git a/examples/react/start-basic-authjs/postcss.config.mjs b/examples/react/start-basic-authjs/postcss.config.mjs new file mode 100644 index 00000000000..a7f73a2d1d7 --- /dev/null +++ b/examples/react/start-basic-authjs/postcss.config.mjs @@ -0,0 +1,5 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + }, +} diff --git a/examples/react/start-basic-authjs/src/components/DefaultCatchBoundary.tsx b/examples/react/start-basic-authjs/src/components/DefaultCatchBoundary.tsx new file mode 100644 index 00000000000..260c2ea41f7 --- /dev/null +++ b/examples/react/start-basic-authjs/src/components/DefaultCatchBoundary.tsx @@ -0,0 +1,53 @@ +import { + ErrorComponent, + Link, + rootRouteId, + useMatch, + useRouter, +} from '@tanstack/react-router' +import type { ErrorComponentProps } from '@tanstack/react-router' + +export function DefaultCatchBoundary({ error }: ErrorComponentProps) { + const router = useRouter() + const isRoot = useMatch({ + strict: false, + select: (state) => state.id === rootRouteId, + }) + + console.error('DefaultCatchBoundary Error:', error) + + return ( +
+ +
+ + {isRoot ? ( + + Home + + ) : ( + { + e.preventDefault() + window.history.back() + }} + > + Go Back + + )} +
+
+ ) +} diff --git a/examples/react/start-basic-authjs/src/components/NotFound.tsx b/examples/react/start-basic-authjs/src/components/NotFound.tsx new file mode 100644 index 00000000000..fd4e75befe3 --- /dev/null +++ b/examples/react/start-basic-authjs/src/components/NotFound.tsx @@ -0,0 +1,25 @@ +import { Link } from '@tanstack/react-router' + +export function NotFound({ children }: { children?: any }) { + return ( +
+
+ {children ||

The page you are looking for does not exist.

} +
+

+ + + Start Over + +

+
+ ) +} diff --git a/examples/react/start-basic-authjs/src/routeTree.gen.ts b/examples/react/start-basic-authjs/src/routeTree.gen.ts new file mode 100644 index 00000000000..2f59e4ba338 --- /dev/null +++ b/examples/react/start-basic-authjs/src/routeTree.gen.ts @@ -0,0 +1,122 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as ProtectedRouteImport } from './routes/protected' +import { Route as LoginRouteImport } from './routes/login' +import { Route as IndexRouteImport } from './routes/index' +import { Route as ApiAuthSplatRouteImport } from './routes/api/auth/$' + +const ProtectedRoute = ProtectedRouteImport.update({ + id: '/protected', + path: '/protected', + getParentRoute: () => rootRouteImport, +} as any) +const LoginRoute = LoginRouteImport.update({ + id: '/login', + path: '/login', + getParentRoute: () => rootRouteImport, +} as any) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const ApiAuthSplatRoute = ApiAuthSplatRouteImport.update({ + id: '/api/auth/$', + path: '/api/auth/$', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/login': typeof LoginRoute + '/protected': typeof ProtectedRoute + '/api/auth/$': typeof ApiAuthSplatRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/login': typeof LoginRoute + '/protected': typeof ProtectedRoute + '/api/auth/$': typeof ApiAuthSplatRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/login': typeof LoginRoute + '/protected': typeof ProtectedRoute + '/api/auth/$': typeof ApiAuthSplatRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/login' | '/protected' | '/api/auth/$' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/login' | '/protected' | '/api/auth/$' + id: '__root__' | '/' | '/login' | '/protected' | '/api/auth/$' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + LoginRoute: typeof LoginRoute + ProtectedRoute: typeof ProtectedRoute + ApiAuthSplatRoute: typeof ApiAuthSplatRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/protected': { + id: '/protected' + path: '/protected' + fullPath: '/protected' + preLoaderRoute: typeof ProtectedRouteImport + parentRoute: typeof rootRouteImport + } + '/login': { + id: '/login' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof LoginRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + '/api/auth/$': { + id: '/api/auth/$' + path: '/api/auth/$' + fullPath: '/api/auth/$' + preLoaderRoute: typeof ApiAuthSplatRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + LoginRoute: LoginRoute, + ProtectedRoute: ProtectedRoute, + ApiAuthSplatRoute: ApiAuthSplatRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/examples/react/start-basic-authjs/src/router.tsx b/examples/react/start-basic-authjs/src/router.tsx new file mode 100644 index 00000000000..f380f73be74 --- /dev/null +++ b/examples/react/start-basic-authjs/src/router.tsx @@ -0,0 +1,15 @@ +import { createRouter } from '@tanstack/react-router' +import { routeTree } from './routeTree.gen' +import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' +import { NotFound } from './components/NotFound' + +export function getRouter() { + const router = createRouter({ + routeTree, + defaultPreload: 'intent', + defaultErrorComponent: DefaultCatchBoundary, + defaultNotFoundComponent: () => , + scrollRestoration: true, + }) + return router +} diff --git a/examples/react/start-basic-authjs/src/routes/__root.tsx b/examples/react/start-basic-authjs/src/routes/__root.tsx new file mode 100644 index 00000000000..af2c24ab57b --- /dev/null +++ b/examples/react/start-basic-authjs/src/routes/__root.tsx @@ -0,0 +1,117 @@ +/// +import type { ReactNode } from 'react' +import type { AuthSession } from 'start-authjs' +import { + HeadContent, + Link, + Outlet, + Scripts, + createRootRouteWithContext, +} from '@tanstack/react-router' +import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' +import { createServerFn } from '@tanstack/react-start' +import { getRequest } from '@tanstack/react-start/server' +import { getSession } from 'start-authjs' +import { authConfig } from '~/utils/auth' +import appCss from '~/styles/app.css?url' + +interface RouterContext { + session: AuthSession | null +} + +const fetchSession = createServerFn({ method: 'GET' }).handler(async () => { + const request = getRequest() + const session = await getSession(request, authConfig) + return session +}) + +export const Route = createRootRouteWithContext()({ + beforeLoad: async () => { + const session = await fetchSession() + return { + session, + } + }, + head: () => ({ + meta: [ + { + charSet: 'utf-8', + }, + { + name: 'viewport', + content: 'width=device-width, initial-scale=1', + }, + { + title: 'TanStack Start Auth Example', + }, + ], + links: [{ rel: 'stylesheet', href: appCss }], + }), + component: RootComponent, +}) + +function RootComponent() { + return ( + + + + ) +} + +function RootDocument({ children }: { children: ReactNode }) { + return ( + + + + + + +
{children}
+ + + + + ) +} + +function NavBar() { + const routeContext = Route.useRouteContext() + + return ( + + ) +} diff --git a/examples/react/start-basic-authjs/src/routes/api/auth/$.ts b/examples/react/start-basic-authjs/src/routes/api/auth/$.ts new file mode 100644 index 00000000000..3e59239db5c --- /dev/null +++ b/examples/react/start-basic-authjs/src/routes/api/auth/$.ts @@ -0,0 +1,18 @@ +import { createFileRoute } from '@tanstack/react-router' +import { StartAuthJS } from 'start-authjs' +import { authConfig } from '~/utils/auth' + +/** + * Auth.js API route handler + * Handles all auth routes: /api/auth/* + */ +const { GET, POST } = StartAuthJS(authConfig) + +export const Route = createFileRoute('/api/auth/$')({ + server: { + handlers: { + GET: ({ request }) => GET({ request, response: new Response() }), + POST: ({ request }) => POST({ request, response: new Response() }), + }, + }, +}) diff --git a/examples/react/start-basic-authjs/src/routes/index.tsx b/examples/react/start-basic-authjs/src/routes/index.tsx new file mode 100644 index 00000000000..5723b0f030a --- /dev/null +++ b/examples/react/start-basic-authjs/src/routes/index.tsx @@ -0,0 +1,50 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/')({ + component: Home, +}) + +function Home() { + const { session } = Route.useRouteContext() + const user = session?.user + + return ( +
+

+ TanStack Start Auth.js Example +

+

+ This example demonstrates auth.js integration with TanStack Start using + Auth0 OAuth. +

+ +
+

Auth Status

+ + {session ? ( +
+

Authenticated

+ {user?.image && ( + Avatar + )} +

+ Name: {user?.name ?? 'N/A'} +

+

+ Email: {user?.email ?? 'N/A'} +

+
+ ) : ( +

+ You are not signed in. Click "Sign In" in the navigation bar to + authenticate with Auth0. +

+ )} +
+
+ ) +} diff --git a/examples/react/start-basic-authjs/src/routes/login.tsx b/examples/react/start-basic-authjs/src/routes/login.tsx new file mode 100644 index 00000000000..008f62d5112 --- /dev/null +++ b/examples/react/start-basic-authjs/src/routes/login.tsx @@ -0,0 +1,48 @@ +import { useState, useEffect, Suspense } from 'react' +import { createFileRoute, redirect } from '@tanstack/react-router' + +export const Route = createFileRoute('/login')({ + beforeLoad: ({ context }) => { + // Redirect if already authenticated + if (context.session) { + throw redirect({ to: '/' }) + } + }, + component: Login, +}) + +function Login() { + const [csrfToken, setCsrfToken] = useState('') + + useEffect(() => { + fetch('/api/auth/csrf') + .then((res) => res.json()) + .then((data) => setCsrfToken(data.csrfToken)) + }, []) + + return ( +
+

Sign In

+ +
+
+ + + +
+ +

+ You'll be redirected to Auth0 to complete the sign-in process. +

+
+
+ ) +} diff --git a/examples/react/start-basic-authjs/src/routes/protected.tsx b/examples/react/start-basic-authjs/src/routes/protected.tsx new file mode 100644 index 00000000000..a5b960582ad --- /dev/null +++ b/examples/react/start-basic-authjs/src/routes/protected.tsx @@ -0,0 +1,55 @@ +import { createFileRoute, redirect } from '@tanstack/react-router' + +export const Route = createFileRoute('/protected')({ + beforeLoad: ({ context }) => { + if (!context.session) { + throw redirect({ to: '/login' }) + } + }, + component: Protected, +}) + +function Protected() { + const { session } = Route.useRouteContext() + const user = session?.user + + return ( +
+

Protected Page

+

+ This page is only accessible to authenticated users. +

+ +
+

+ Welcome, {user?.name ?? 'User'}! +

+ + {user && ( +
+

+ Email: {user?.email ?? 'N/A'} +

+ {user?.image && ( +
+ Avatar: + User avatar +
+ )} +
+ )} +
+ +
+

Session Data (Debug)

+
+          {JSON.stringify(session, null, 2)}
+        
+
+
+ ) +} diff --git a/examples/react/start-basic-authjs/src/styles/app.css b/examples/react/start-basic-authjs/src/styles/app.css new file mode 100644 index 00000000000..d4b5078586e --- /dev/null +++ b/examples/react/start-basic-authjs/src/styles/app.css @@ -0,0 +1 @@ +@import 'tailwindcss'; diff --git a/examples/react/start-basic-authjs/src/utils/auth.ts b/examples/react/start-basic-authjs/src/utils/auth.ts new file mode 100644 index 00000000000..8d5c0b1baff --- /dev/null +++ b/examples/react/start-basic-authjs/src/utils/auth.ts @@ -0,0 +1,50 @@ +import Auth0 from '@auth/core/providers/auth0' +import { setCookie } from '@tanstack/react-start/server' +import type { Profile } from '@auth/core/types' +import type { StartAuthJSConfig } from 'start-authjs' + +declare module '@auth/core/types' { + export interface Session { + user: { + name: string + email: string + sub: string + email_verified: boolean + } & Profile + account: { + access_token: string + } + expires: Date + } +} + +/** + * Auth.js configuration for TanStack Start with Auth0 + */ +export const authConfig: StartAuthJSConfig = { + // basePath is derived from AUTH_URL env var + secret: process.env.AUTH_SECRET, + providers: [ + Auth0({ + // Auth.js auto-reads AUTH_AUTH0_ID, AUTH_AUTH0_SECRET, AUTH_AUTH0_ISSUER from env + authorization: { + params: { + scope: 'email email_verified openid profile', + prompt: 'login', + }, + }, + async profile(profile, tokens) { + await setCookie( + 'auth0Token', + encodeURIComponent(tokens.access_token ?? ''), + ) + await setCookie( + 'auth0User', + encodeURIComponent(JSON.stringify(profile)), + ) + + return profile + }, + }), + ], +} diff --git a/examples/react/start-basic-authjs/tsconfig.json b/examples/react/start-basic-authjs/tsconfig.json new file mode 100644 index 00000000000..70cbbc5ce37 --- /dev/null +++ b/examples/react/start-basic-authjs/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "paths": { + "~/*": ["./src/*"] + } + }, + "include": ["src", "env.d.ts", "vite.config.ts"] +} diff --git a/examples/react/start-basic-authjs/vite.config.ts b/examples/react/start-basic-authjs/vite.config.ts new file mode 100644 index 00000000000..75bdbbe66b4 --- /dev/null +++ b/examples/react/start-basic-authjs/vite.config.ts @@ -0,0 +1,18 @@ +import { tanstackStart } from '@tanstack/react-start/plugin/vite' +import { defineConfig } from 'vite' +import tsConfigPaths from 'vite-tsconfig-paths' +import viteReact from '@vitejs/plugin-react' + +export default defineConfig({ + server: { + port: 10000, + strictPort: true, + }, + plugins: [ + tsConfigPaths({ + projects: ['./tsconfig.json'], + }), + tanstackStart(), + viteReact(), + ], +}) diff --git a/examples/solid/start-basic-authjs/.env.example b/examples/solid/start-basic-authjs/.env.example new file mode 100644 index 00000000000..062e707907f --- /dev/null +++ b/examples/solid/start-basic-authjs/.env.example @@ -0,0 +1,11 @@ +# Auth.js Configuration +AUTH_SECRET=your-secret-key-here-min-32-chars-long + +# Auth.js URL (must include the auth path) +AUTH_URL=http://localhost:10000/api/auth + +# Auth0 Configuration (https://manage.auth0.com) +# Auth.js auto-reads these env vars for the Auth0 provider +AUTH_AUTH0_ID=your-auth0-client-id +AUTH_AUTH0_SECRET=your-auth0-client-secret +AUTH_AUTH0_ISSUER=https://your-tenant.auth0.com diff --git a/examples/solid/start-basic-authjs/.gitignore b/examples/solid/start-basic-authjs/.gitignore new file mode 100644 index 00000000000..dc87421f7dc --- /dev/null +++ b/examples/solid/start-basic-authjs/.gitignore @@ -0,0 +1,6 @@ +node_modules +dist +.env +*.local +.DS_Store +.tanstack \ No newline at end of file diff --git a/examples/solid/start-basic-authjs/package.json b/examples/solid/start-basic-authjs/package.json new file mode 100644 index 00000000000..21a2231d150 --- /dev/null +++ b/examples/solid/start-basic-authjs/package.json @@ -0,0 +1,31 @@ +{ + "name": "tanstack-solid-start-example-basic-authjs", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "start": "pnpx srvx --prod -s ../client dist/server/server.js" + }, + "dependencies": { + "@auth/core": "^0.41.1", + "@tanstack/solid-router": "^1.139.3", + "@tanstack/solid-router-devtools": "^1.139.3", + "@tanstack/solid-start": "^1.139.3", + "start-authjs": "^1.0.0", + "solid-js": "^1.9.10", + "tailwind-merge": "^2.6.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.15", + "@types/node": "^22.5.4", + "postcss": "^8.5.1", + "tailwindcss": "^4.1.15", + "typescript": "^5.7.2", + "vite": "^7.1.7", + "vite-plugin-solid": "^2.11.10", + "vite-tsconfig-paths": "^5.1.4" + } +} diff --git a/examples/solid/start-basic-authjs/postcss.config.mjs b/examples/solid/start-basic-authjs/postcss.config.mjs new file mode 100644 index 00000000000..a7f73a2d1d7 --- /dev/null +++ b/examples/solid/start-basic-authjs/postcss.config.mjs @@ -0,0 +1,5 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + }, +} diff --git a/examples/solid/start-basic-authjs/src/components/DefaultCatchBoundary.tsx b/examples/solid/start-basic-authjs/src/components/DefaultCatchBoundary.tsx new file mode 100644 index 00000000000..1ae7a0cf945 --- /dev/null +++ b/examples/solid/start-basic-authjs/src/components/DefaultCatchBoundary.tsx @@ -0,0 +1,53 @@ +import { + ErrorComponent, + Link, + rootRouteId, + useMatch, + useRouter, +} from '@tanstack/solid-router' +import type { ErrorComponentProps } from '@tanstack/solid-router' + +export function DefaultCatchBoundary({ error }: ErrorComponentProps) { + const router = useRouter() + const isRoot = useMatch({ + strict: false, + select: (state) => state.id === rootRouteId, + }) + + console.error('DefaultCatchBoundary Error:', error) + + return ( +
+ +
+ + {isRoot() ? ( + + Home + + ) : ( + { + e.preventDefault() + window.history.back() + }} + > + Go Back + + )} +
+
+ ) +} diff --git a/examples/solid/start-basic-authjs/src/components/NotFound.tsx b/examples/solid/start-basic-authjs/src/components/NotFound.tsx new file mode 100644 index 00000000000..af5ad273c5f --- /dev/null +++ b/examples/solid/start-basic-authjs/src/components/NotFound.tsx @@ -0,0 +1,25 @@ +import { Link } from '@tanstack/solid-router' + +export function NotFound({ children }: { children?: any }) { + return ( +
+
+ {children ||

The page you are looking for does not exist.

} +
+

+ + + Start Over + +

+
+ ) +} diff --git a/examples/solid/start-basic-authjs/src/routeTree.gen.ts b/examples/solid/start-basic-authjs/src/routeTree.gen.ts new file mode 100644 index 00000000000..e772ba9c3ed --- /dev/null +++ b/examples/solid/start-basic-authjs/src/routeTree.gen.ts @@ -0,0 +1,122 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as ProtectedRouteImport } from './routes/protected' +import { Route as LoginRouteImport } from './routes/login' +import { Route as IndexRouteImport } from './routes/index' +import { Route as ApiAuthSplatRouteImport } from './routes/api/auth/$' + +const ProtectedRoute = ProtectedRouteImport.update({ + id: '/protected', + path: '/protected', + getParentRoute: () => rootRouteImport, +} as any) +const LoginRoute = LoginRouteImport.update({ + id: '/login', + path: '/login', + getParentRoute: () => rootRouteImport, +} as any) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const ApiAuthSplatRoute = ApiAuthSplatRouteImport.update({ + id: '/api/auth/$', + path: '/api/auth/$', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/login': typeof LoginRoute + '/protected': typeof ProtectedRoute + '/api/auth/$': typeof ApiAuthSplatRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/login': typeof LoginRoute + '/protected': typeof ProtectedRoute + '/api/auth/$': typeof ApiAuthSplatRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/login': typeof LoginRoute + '/protected': typeof ProtectedRoute + '/api/auth/$': typeof ApiAuthSplatRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/login' | '/protected' | '/api/auth/$' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/login' | '/protected' | '/api/auth/$' + id: '__root__' | '/' | '/login' | '/protected' | '/api/auth/$' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + LoginRoute: typeof LoginRoute + ProtectedRoute: typeof ProtectedRoute + ApiAuthSplatRoute: typeof ApiAuthSplatRoute +} + +declare module '@tanstack/solid-router' { + interface FileRoutesByPath { + '/protected': { + id: '/protected' + path: '/protected' + fullPath: '/protected' + preLoaderRoute: typeof ProtectedRouteImport + parentRoute: typeof rootRouteImport + } + '/login': { + id: '/login' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof LoginRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + '/api/auth/$': { + id: '/api/auth/$' + path: '/api/auth/$' + fullPath: '/api/auth/$' + preLoaderRoute: typeof ApiAuthSplatRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + LoginRoute: LoginRoute, + ProtectedRoute: ProtectedRoute, + ApiAuthSplatRoute: ApiAuthSplatRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/solid-start' +declare module '@tanstack/solid-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/examples/solid/start-basic-authjs/src/router.tsx b/examples/solid/start-basic-authjs/src/router.tsx new file mode 100644 index 00000000000..5da353c1ce2 --- /dev/null +++ b/examples/solid/start-basic-authjs/src/router.tsx @@ -0,0 +1,16 @@ +import { createRouter } from '@tanstack/solid-router' +import { routeTree } from './routeTree.gen' +import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' +import { NotFound } from './components/NotFound' + +export function getRouter() { + const router = createRouter({ + routeTree, + defaultPreload: 'intent', + defaultErrorComponent: DefaultCatchBoundary, + defaultNotFoundComponent: () => , + scrollRestoration: true, + }) + + return router +} diff --git a/examples/solid/start-basic-authjs/src/routes/__root.tsx b/examples/solid/start-basic-authjs/src/routes/__root.tsx new file mode 100644 index 00000000000..29167dea728 --- /dev/null +++ b/examples/solid/start-basic-authjs/src/routes/__root.tsx @@ -0,0 +1,121 @@ +/// +import type { AuthSession } from 'start-authjs' +import { + HeadContent, + Link, + Outlet, + Scripts, + createRootRouteWithContext, +} from '@tanstack/solid-router' +import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools' +import { createServerFn } from '@tanstack/solid-start' +import { getRequest } from '@tanstack/solid-start/server' +import { HydrationScript } from 'solid-js/web' +import { Show } from 'solid-js' +import { getSession } from 'start-authjs' +import type { JSX } from 'solid-js' +import { authConfig } from '~/utils/auth' +import appCss from '~/styles/app.css?url' + +interface RouterContext { + session: AuthSession | null +} + +const fetchSession = createServerFn({ method: 'GET' }).handler(async () => { + const request = getRequest() + const session = await getSession(request, authConfig) + return session +}) + +export const Route = createRootRouteWithContext()({ + beforeLoad: async () => { + const session = await fetchSession() + return { + session, + } + }, + head: () => ({ + meta: [ + { + charset: 'utf-8', + }, + { + name: 'viewport', + content: 'width=device-width, initial-scale=1', + }, + { + title: 'TanStack Start Auth Example', + }, + ], + links: [{ rel: 'stylesheet', href: appCss }], + }), + component: RootComponent, +}) + +function RootComponent() { + return ( + + + + ) +} + +function RootDocument({ children }: { children: JSX.Element }) { + return ( + + + + + + + +
{children}
+ + + + + ) +} + +function NavBar() { + const routeContext = Route.useRouteContext() + + return ( + + ) +} diff --git a/examples/solid/start-basic-authjs/src/routes/api/auth/$.ts b/examples/solid/start-basic-authjs/src/routes/api/auth/$.ts new file mode 100644 index 00000000000..d6736c6df4b --- /dev/null +++ b/examples/solid/start-basic-authjs/src/routes/api/auth/$.ts @@ -0,0 +1,18 @@ +import { createFileRoute } from '@tanstack/solid-router' +import { StartAuthJS } from 'start-authjs' +import { authConfig } from '~/utils/auth' + +/** + * Auth.js API route handler + * Handles all auth routes: /api/auth/* + */ +const { GET, POST } = StartAuthJS(authConfig) + +export const Route = createFileRoute('/api/auth/$')({ + server: { + handlers: { + GET: ({ request }) => GET({ request, response: new Response() }), + POST: ({ request }) => POST({ request, response: new Response() }), + }, + }, +}) diff --git a/examples/solid/start-basic-authjs/src/routes/index.tsx b/examples/solid/start-basic-authjs/src/routes/index.tsx new file mode 100644 index 00000000000..c7a8ce6a2a2 --- /dev/null +++ b/examples/solid/start-basic-authjs/src/routes/index.tsx @@ -0,0 +1,55 @@ +import { createFileRoute } from '@tanstack/solid-router' +import { Show } from 'solid-js' + +export const Route = createFileRoute('/')({ + component: Home, +}) + +function Home() { + const routeContext = Route.useRouteContext() + + return ( +
+

TanStack Start Auth.js Example

+

+ This example demonstrates auth.js integration with TanStack Start using + Auth0 OAuth. +

+ +
+

Auth Status

+ + + You are not signed in. Click "Sign In" in the navigation bar to + authenticate with Auth0. +

+ } + > + {(session) => ( +
+

Authenticated

+ + {(image) => ( + Avatar + )} + +

+ Name: {session().user?.name ?? 'N/A'} +

+

+ Email: {session().user?.email ?? 'N/A'} +

+
+ )} +
+
+
+ ) +} diff --git a/examples/solid/start-basic-authjs/src/routes/login.tsx b/examples/solid/start-basic-authjs/src/routes/login.tsx new file mode 100644 index 00000000000..6669e1b5b7f --- /dev/null +++ b/examples/solid/start-basic-authjs/src/routes/login.tsx @@ -0,0 +1,50 @@ +import { createFileRoute, redirect } from '@tanstack/solid-router' +import { createResource, Suspense } from 'solid-js' + +export const Route = createFileRoute('/login')({ + beforeLoad: ({ context }) => { + // Redirect if already authenticated + if (context.session) { + throw redirect({ to: '/' }) + } + }, + component: Login, +}) + +async function getCsrfToken(): Promise { + const res = await fetch('/api/auth/csrf') + const data = await res.json() + return data.csrfToken +} + +function Login() { + const [csrfToken] = createResource(getCsrfToken) + + return ( +
+

Sign In

+ +
+ Loading...
}> +
+ + + +
+ + +

+ You'll be redirected to Auth0 to complete the sign-in process. +

+
+ + ) +} diff --git a/examples/solid/start-basic-authjs/src/routes/protected.tsx b/examples/solid/start-basic-authjs/src/routes/protected.tsx new file mode 100644 index 00000000000..982eb962f1a --- /dev/null +++ b/examples/solid/start-basic-authjs/src/routes/protected.tsx @@ -0,0 +1,59 @@ +import { createFileRoute, redirect } from '@tanstack/solid-router' +import { Show } from 'solid-js' + +export const Route = createFileRoute('/protected')({ + beforeLoad: ({ context }) => { + if (!context.session) { + throw redirect({ to: '/login' }) + } + }, + component: Protected, +}) + +function Protected() { + const routeContext = Route.useRouteContext() + + return ( +
+

Protected Page

+

+ This page is only accessible to authenticated users. +

+ +
+

+ Welcome, {routeContext().session?.user?.name ?? 'User'}! +

+ + + {(user) => ( +
+

+ Email: {user().email ?? 'N/A'} +

+ + {(image) => ( +
+ Avatar: + User avatar +
+ )} +
+
+ )} +
+
+ +
+

Session Data (Debug)

+
+          {JSON.stringify(routeContext().session, null, 2)}
+        
+
+
+ ) +} diff --git a/examples/solid/start-basic-authjs/src/styles/app.css b/examples/solid/start-basic-authjs/src/styles/app.css new file mode 100644 index 00000000000..d4b5078586e --- /dev/null +++ b/examples/solid/start-basic-authjs/src/styles/app.css @@ -0,0 +1 @@ +@import 'tailwindcss'; diff --git a/examples/solid/start-basic-authjs/src/utils/auth.ts b/examples/solid/start-basic-authjs/src/utils/auth.ts new file mode 100644 index 00000000000..18ea4da38ca --- /dev/null +++ b/examples/solid/start-basic-authjs/src/utils/auth.ts @@ -0,0 +1,50 @@ +import Auth0 from '@auth/core/providers/auth0' +import { setCookie } from '@tanstack/solid-start/server' +import type { Profile } from '@auth/core/types' +import type { StartAuthJSConfig } from 'start-authjs' + +declare module '@auth/core/types' { + export interface Session { + user: { + name: string + email: string + sub: string + email_verified: boolean + } & Profile + account: { + access_token: string + } + expires: Date + } +} + +/** + * Auth.js configuration for TanStack Start with Auth0 + */ +export const authConfig: StartAuthJSConfig = { + // basePath is derived from AUTH_URL env var + secret: process.env.AUTH_SECRET, + providers: [ + Auth0({ + // Auth.js auto-reads AUTH_AUTH0_ID, AUTH_AUTH0_SECRET, AUTH_AUTH0_ISSUER from env + authorization: { + params: { + scope: 'email email_verified openid profile', + prompt: 'login', + }, + }, + async profile(profile, tokens) { + await setCookie( + 'auth0Token', + encodeURIComponent(tokens.access_token ?? ''), + ) + await setCookie( + 'auth0User', + encodeURIComponent(JSON.stringify(profile)), + ) + + return profile + }, + }), + ], +} diff --git a/examples/solid/start-basic-authjs/tsconfig.json b/examples/solid/start-basic-authjs/tsconfig.json new file mode 100644 index 00000000000..608d3ba5f39 --- /dev/null +++ b/examples/solid/start-basic-authjs/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "paths": { + "~/*": ["./src/*"] + } + }, + "include": ["src", "env.d.ts", "vite.config.ts"] +} diff --git a/examples/solid/start-basic-authjs/vite.config.ts b/examples/solid/start-basic-authjs/vite.config.ts new file mode 100644 index 00000000000..c917cc531db --- /dev/null +++ b/examples/solid/start-basic-authjs/vite.config.ts @@ -0,0 +1,18 @@ +import { tanstackStart } from '@tanstack/solid-start/plugin/vite' +import { defineConfig } from 'vite' +import tsConfigPaths from 'vite-tsconfig-paths' +import viteSolid from 'vite-plugin-solid' + +export default defineConfig({ + server: { + port: 10000, + strictPort: true, + }, + plugins: [ + tsConfigPaths({ + projects: ['./tsconfig.json'], + }), + tanstackStart(), + viteSolid({ ssr: true }), + ], +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3b844498d67..6f1abdcbe0b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5785,6 +5785,64 @@ importers: specifier: ^5.1.4 version: 5.1.4(typescript@5.8.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + examples/react/start-basic-authjs: + dependencies: + '@auth/core': + specifier: ^0.41.1 + version: 0.41.1 + '@tanstack/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@tanstack/react-router-devtools': + specifier: workspace:^ + version: link:../../../packages/react-router-devtools + '@tanstack/react-start': + specifier: workspace:* + version: link:../../../packages/react-start + react: + specifier: ^19.2.0 + version: 19.2.0 + react-dom: + specifier: ^19.2.0 + version: 19.2.0(react@19.2.0) + start-authjs: + specifier: ^1.0.0 + version: 1.0.0(@auth/core@0.41.1) + tailwind-merge: + specifier: ^2.6.0 + version: 2.6.0 + devDependencies: + '@tailwindcss/postcss': + specifier: ^4.1.15 + version: 4.1.15 + '@types/node': + specifier: 22.10.2 + version: 22.10.2 + '@types/react': + specifier: ^19.2.2 + version: 19.2.2 + '@types/react-dom': + specifier: ^19.2.2 + version: 19.2.2(@types/react@19.2.2) + '@vitejs/plugin-react': + specifier: ^4.6.0 + version: 4.7.0(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + postcss: + specifier: ^8.5.1 + version: 8.5.6 + tailwindcss: + specifier: ^4.1.15 + version: 4.1.17 + typescript: + specifier: ^5.7.2 + version: 5.9.2 + vite: + specifier: ^7.1.7 + version: 7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + examples/react/start-basic-cloudflare: dependencies: '@tanstack/react-router': @@ -8331,6 +8389,55 @@ importers: specifier: ^5.1.4 version: 5.1.4(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + examples/solid/start-basic-authjs: + dependencies: + '@auth/core': + specifier: ^0.41.1 + version: 0.41.1 + '@tanstack/solid-router': + specifier: ^1.139.3 + version: link:../../../packages/solid-router + '@tanstack/solid-router-devtools': + specifier: workspace:^ + version: link:../../../packages/solid-router-devtools + '@tanstack/solid-start': + specifier: workspace:* + version: link:../../../packages/solid-start + solid-js: + specifier: 1.9.10 + version: 1.9.10 + start-authjs: + specifier: ^1.0.0 + version: 1.0.0(@auth/core@0.41.1) + tailwind-merge: + specifier: ^2.6.0 + version: 2.6.0 + devDependencies: + '@tailwindcss/postcss': + specifier: ^4.1.15 + version: 4.1.15 + '@types/node': + specifier: 22.10.2 + version: 22.10.2 + postcss: + specifier: ^8.5.1 + version: 8.5.6 + tailwindcss: + specifier: ^4.1.15 + version: 4.1.17 + typescript: + specifier: ^5.7.2 + version: 5.9.2 + vite: + specifier: ^7.1.7 + version: 7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) + vite-plugin-solid: + specifier: ^2.11.10 + version: 2.11.10(@testing-library/jest-dom@6.6.3)(solid-js@1.9.10)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + examples/solid/start-basic-cloudflare: dependencies: '@tanstack/solid-router': @@ -10121,6 +10228,20 @@ packages: '@asamuzakjp/nwsapi@2.3.9': resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + '@auth/core@0.41.1': + resolution: {integrity: sha512-t9cJ2zNYAdWMacGRMT6+r4xr1uybIdmYa49calBPeTqwgAFPV/88ac9TEvCR85pvATiSPt8VaNf+Gt24JIT/uw==} + peerDependencies: + '@simplewebauthn/browser': ^9.0.1 + '@simplewebauthn/server': ^9.0.2 + nodemailer: ^7.0.7 + peerDependenciesMeta: + '@simplewebauthn/browser': + optional: true + '@simplewebauthn/server': + optional: true + nodemailer: + optional: true + '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} @@ -12558,6 +12679,9 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@panva/hkdf@1.2.1': + resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} + '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} engines: {node: '>= 10.0.0'} @@ -19068,6 +19192,9 @@ packages: engines: {node: ^14.16.0 || >=16.10.0} hasBin: true + oauth4webapi@3.8.3: + resolution: {integrity: sha512-pQ5BsX3QRTgnt5HxgHwgunIRaDXBdkT23tf8dfzmtTIL2LTpdmxgbpbBm0VgFWAIDlezQvQCTgnVIUmHupXHxw==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -19453,6 +19580,14 @@ packages: resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} engines: {node: '>=12'} + preact-render-to-string@6.5.11: + resolution: {integrity: sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==} + peerDependencies: + preact: '>=10' + + preact@10.24.3: + resolution: {integrity: sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==} + precinct@12.2.0: resolution: {integrity: sha512-NFBMuwIfaJ4SocE9YXPU/n4AcNSoFMVFjP72nvl3cx69j/ke61/hPOWFREVxLkFhhEGnA8ZuVfTqJBa+PK3b5w==} engines: {node: '>=18'} @@ -20234,6 +20369,12 @@ packages: standardwebhooks@1.0.0: resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} + start-authjs@1.0.0: + resolution: {integrity: sha512-RM0zGg15kB8Kua7ZNWv6p4oxz7LepTXKLMrfRSi01faSYgDcCQDoaOk27ig1qBLXSuDChvwTPdGYNWI5ImDjIQ==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@auth/core': '>=0.41.1' + statuses@1.5.0: resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} engines: {node: '>= 0.6'} @@ -21582,6 +21723,14 @@ snapshots: '@asamuzakjp/nwsapi@2.3.9': {} + '@auth/core@0.41.1': + dependencies: + '@panva/hkdf': 1.2.1 + jose: 6.1.0 + oauth4webapi: 3.8.3 + preact: 10.24.3 + preact-render-to-string: 6.5.11(preact@10.24.3) + '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.27.1 @@ -24374,6 +24523,8 @@ snapshots: '@open-draft/until@2.1.0': {} + '@panva/hkdf@1.2.1': {} + '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -31573,6 +31724,8 @@ snapshots: pkg-types: 2.3.0 tinyexec: 0.3.2 + oauth4webapi@3.8.3: {} + object-assign@4.1.1: {} object-hash@3.0.0: {} @@ -31929,6 +32082,12 @@ snapshots: postgres@3.4.7: {} + preact-render-to-string@6.5.11(preact@10.24.3): + dependencies: + preact: 10.24.3 + + preact@10.24.3: {} + precinct@12.2.0: dependencies: '@dependents/detective-less': 5.0.1 @@ -32913,6 +33072,11 @@ snapshots: '@stablelib/base64': 1.0.1 fast-sha256: 1.3.0 + start-authjs@1.0.0(@auth/core@0.41.1): + dependencies: + '@auth/core': 0.41.1 + set-cookie-parser: 2.7.2 + statuses@1.5.0: {} statuses@2.0.1: {}