npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nightmar3/uauth-tanstack-start

v1.0.1

Published

TanStack Start integration for Universal Auth SDK - Zero config authentication with full SSR support

Readme

@nightmar3/uauth-tanstack-start

**TanStack Start integration for Universal Auth SDK. Provides server-first authentication with full SSR support.

[!IMPORTANT] Backend Required: This SDK requires a backend API. See Backend Requirements for the complete API contract, or use our Backend Implementation Guide for step-by-step instructions.

License: MIT TypeScript

Features

  • Zero-config setup - Works with environment variables
  • Full SSR support - Server-first authentication
  • Security-first - Read-only client hooks, server-only validation
  • Type-safe - Full TypeScript support
  • Middleware - Route protection with beforeLoad
  • Tiny - Minimal bundle size
  • Framework-native - Uses TanStack Start patterns

Installation

npm install @nightmar3/uauth-tanstack-start

Quick Start

1. Set Environment Variables

# .env
AUTH_URL=https://api.yourapp.com/auth
SESSION_SECRET=your-secret-key-min-32-characters

2. Set Up Root Route

// routes/__root.tsx
import { createRootRoute } from '@tanstack/react-router'
import { getSessionFn } from '@nightmar3/uauth-tanstack-start/server'

export const Route = createRootRoute({
  loader: async () => {
    const session = await getSessionFn()
    return {
      user: session?.user ?? null,
      isAuthenticated: !!session?.user,
    }
  },
})

3. Use in Components

// components/Profile.tsx
import { useAuth } from '@nightmar3/uauth-tanstack-start/client'

export function Profile() {
  const { user, isAuthenticated, signOut } = useAuth()

  if (!isAuthenticated) {
    return <div>Please log in</div>
  }

  return (
    <div>
      <p>Hello, {user.name}</p>
      <button onClick={signOut}>Sign Out</button>
    </div>
  )
}

4. Protect Routes

// routes/dashboard.tsx
import { createFileRoute } from '@tanstack/react-router'
import { createAuthBeforeLoad } from '@nightmar3/uauth-tanstack-start/middleware'

export const Route = createFileRoute('/dashboard')({
  beforeLoad: createAuthBeforeLoad({ redirectTo: '/login' }),
  component: DashboardPage,
})

API Reference

Server Functions

getSessionFn()

Get current session (auto-refreshes tokens if expired)

const session = await getSessionFn()
// Returns: { user, accessToken } | null

signInFn({ data })

Sign in with email and password

const result = await signInFn({ 
  data: { email: '[email protected]', password: 'password' } 
})

signUpFn({ data })

Create new user account

const result = await signUpFn({ 
  data: { email: '[email protected]', password: 'password', name: 'John' } 
})

signOutFn()

Sign out and clear session

await signOutFn()

Client Hooks

useAuth()

Access auth state and methods

const { user, isAuthenticated, signIn, signUp, signOut } = useAuth()

Note: All data is read-only from server. Auth methods call server functions and trigger router invalidation.

useUser()

Get current user

const user = useUser()

useSession()

Get full session

const session = useSession()

Components

<SignedIn>

Render children only when authenticated

<SignedIn>
  <UserDashboard />
</SignedIn>

<SignedOut>

Render children only when NOT authenticated

<SignedOut>
  <LoginPrompt />
</SignedOut>

<AuthGate>

Conditional rendering with fallback

<AuthGate fallback={<LoginPrompt />} loading={<Spinner />}>
  <UserDashboard />
</AuthGate>

Middleware

createAuthBeforeLoad(options)

Route protection helper

export const Route = createFileRoute('/protected')({
  beforeLoad: createAuthBeforeLoad({ redirectTo: '/login' }),
})

requireAuth

Simple auth requirement

export const Route = createFileRoute('/protected')({
  beforeLoad: requireAuth,
})

createAuthMiddleware()

Server function middleware

const authMiddleware = createAuthMiddleware()

export const protectedFn = createServerFn()
  .middleware([authMiddleware])
  .handler(async ({ context }) => {
    // context.user is guaranteed to exist
    return { userId: context.user.id }
  })

Security Architecture

Server-First Design

  • ✅ All auth data comes from server via loaders
  • ✅ Client hooks are read-only consumers
  • ✅ No client-side auth state manipulation
  • ✅ Server validates every operation
  • ✅ HTTP-only cookies prevent XSS

How It Works

User Action (e.g., Sign In)
  ↓
useAuth().signIn() (client)
  ↓
signInFn() (server function)
  ↓
Server validates credentials
  ↓
Server updates session (HTTP-only cookies)
  ↓
Router invalidates
  ↓
Root loader re-runs
  ↓
Fresh user data from server
  ↓
UI updates

OAuth Support

Setup OAuth Providers

// Use the useOAuth hook in your login component
import { useOAuth } from '@nightmar3/uauth-tanstack-start/client'

function LoginPage() {
  const { providers, signInWithOAuth } = useOAuth()

  return (
    <div>
      {providers.map((provider) => (
        <button
          key={provider.name}
          onClick={async () => {
            await signInWithOAuth(provider.name)
            window.location.href = '/dashboard'
          }}
        >
          Sign in with {provider.displayName}
        </button>
      ))}
    </div>
  )
}

OAuth Callback Route

Create a callback route to handle OAuth redirects:

// routes/auth/callback.tsx
import { createFileRoute } from '@tanstack/react-router'
import { useEffect } from 'react'

export const Route = createFileRoute('/auth/callback')({
  component: AuthCallback,
})

function AuthCallback() {
  useEffect(() => {
    const params = new URLSearchParams(window.location.search)
    const code = params.get('code')
    const error = params.get('error')
    const error_description = params.get('error_description')

    if (window.opener) {
      window.opener.postMessage(
        {
          type: 'oauth2_callback',
          code,
          error,
          error_description,
        },
        window.location.origin
      )
      
      setTimeout(() => {
        window.close()
      }, 100)
    }
  }, [])

  return <div>Processing authentication...</div>
}

Configuration

Environment Variables (Required)

# Required for session encryption
SESSION_SECRET=your-secret-key-min-32-characters

# Required for API communication
AUTH_URL=https://api.yourapp.com/auth

# Optional: OAuth provider credentials (if using OAuth)
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret

Important: SESSION_SECRET must be set when running the production build. It's validated at runtime, not build time.

Programmatic Configuration

import { configureAuth } from '@nightmar3/uauth-tanstack-start'

configureAuth({
  baseURL: 'https://api.yourapp.com/auth',
  sessionSecret: process.env.SESSION_SECRET!,
  cookies: {
    secure: true,
    sameSite: 'lax',
    maxAge: 60 * 60 * 24 * 7, // 7 days
  },
})

Backend Requirements

The SDK expects a backend API that implements the following endpoints. You can use our FastAPI reference implementation or build your own.

Endpoints

| Method | Path | Description | Request Body | Response Data | |--------|------|-------------|--------------|---------------| | POST | /sign-in/password | Sign in with email/password | { email, password } | { user, tokens } | | POST | /sign-in/oauth2 | Exchange OAuth code for tokens | { provider, code, redirect_uri } | { user, tokens } | | POST | /sign-up | Create new account | { email, password, name? } | { user, tokens } | | DELETE| /session | Sign out (revoke tokens) | - | { ok: boolean } | | GET | /session | Get current user session | - | { user } | | POST | /token/refresh | Refresh access token | { refresh_token } | { tokens } | | GET | /providers | List available OAuth providers | - | { providers } |

Response Format

All API responses must follow this envelope structure:

interface ApiResponse<T> {
  ok: boolean
  data: T | null
  error: {
    code: string
    message: string
    details?: any
  } | null
}

Token Structure

The backend must return tokens in this format:

interface AuthTokens {
  access_token: string
  refresh_token: string
  expires_in: number // seconds
}

OAuth Provider Response (Optional)

If implementing OAuth support, the /providers endpoint must return:

interface OAuth2Provider {
  name: string
  displayName: string
  clientId: string
  authorizationUrl: string
  scope?: string
}

Example:

{
  "ok": true,
  "data": {
    "providers": [
      {
        "name": "google",
        "displayName": "Google",
        "clientId": "your-google-client-id",
        "authorizationUrl": "https://accounts.google.com/o/oauth2/v2/auth",
        "scope": "openid email profile"
      }
    ]
  },
  "error": null
}

Production Deployment

Building for Production

npm run build

Running the Production Build

You must provide environment variables when running the production server:

# Option 1: Inline environment variables
SESSION_SECRET="your-secret" AUTH_URL="https://api.yourapp.com/auth" npm start

# Option 2: Load from .env file
export $(cat .env | xargs) && npm start

# Option 3: Use a process manager like PM2
pm2 start .output/server/index.mjs --name "my-app" --env production

Deployment Platforms

For platforms like Vercel, Netlify, or Railway:

  1. Set environment variables in your platform's dashboard
  2. Deploy your application
  3. The platform will automatically set the variables at runtime

Required Environment Variables for Production:

  • SESSION_SECRET (min 32 characters)
  • AUTH_URL (your backend API URL)
  • OAuth credentials (if using OAuth)

Examples

Login Page

import { useAuth } from '@nightmar3/uauth-tanstack-start/client'
import { useState } from 'react'

export function LoginPage() {
  const { signIn } = useAuth()
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    const result = await signIn(email, password)
    
    if (!result.ok) {
      alert(result.error?.message)
    }
    // Router will auto-redirect after successful login
  }

  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="email" 
        value={email} 
        onChange={(e) => setEmail(e.target.value)} 
      />
      <input 
        type="password" 
        value={password} 
        onChange={(e) => setPassword(e.target.value)} 
      />
      <button type="submit">Sign In</button>
    </form>
  )
}

Protected Route with Data

// routes/dashboard.tsx
import { createFileRoute } from '@tanstack/react-router'
import { createAuthBeforeLoad } from '@nightmar3/uauth-tanstack-start/middleware'

export const Route = createFileRoute('/dashboard')({
  beforeLoad: createAuthBeforeLoad({ redirectTo: '/login' }),
  loader: async ({ context }) => {
    // context.user is available from beforeLoad
    const data = await fetchUserData(context.user.id)
    return { data }
  },
  component: DashboardPage,
})

function DashboardPage() {
  const { data } = Route.useLoaderData()
  const { user } = useAuth()
  
  return (
    <div>
      <h1>Welcome, {user.name}</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  )
}

Migration from Next.js

If you're migrating from @nightmar3/uauth-next:

  1. Replace package:

    npm uninstall @nightmar3/uauth-next
    npm install @nightmar3/uauth-tanstack-start
  2. Update imports:

    // Before
    import { useAuth } from '@nightmar3/uauth-next/client'
       
    // After
    import { useAuth } from '@nightmar3/uauth-tanstack-start/client'
  3. Replace middleware with beforeLoad:

    // Before (Next.js middleware)
    export default authMiddleware({ protectedRoutes: ['/dashboard'] })
       
    // After (TanStack Start beforeLoad)
    export const Route = createFileRoute('/dashboard')({
      beforeLoad: requireAuth,
    })

License

MIT © nightmar3

Links