# auth-session-management: Implement Secure Session Handling

## Priority: HIGH

## Explanation

Sessions maintain user authentication state across requests. Use HTTP-only cookies with secure settings to prevent XSS and CSRF attacks. Never store sensitive data in client-accessible storage.

## Bad Example

```tsx
// Storing auth in localStorage - vulnerable to XSS
function login(credentials: Credentials) {
  const token = await authenticate(credentials)
  localStorage.setItem('authToken', token)  // XSS can steal this
}

// Non-HTTP-only cookie - JavaScript accessible
export const setSession = createServerFn({ method: 'POST' })
  .handler(async ({ data }) => {
    setResponseHeader('Set-Cookie', `session=${data.token}`)  // Not secure
  })
```

## Good Example: Secure Session Cookie

```tsx
// lib/session.server.ts
import { useSession } from '@tanstack/react-start/server'

// Configure session with secure defaults
export function getSession() {
  return useSession({
    password: process.env.SESSION_SECRET!,  // At least 32 characters
    cookie: {
      name: '__session',
      httpOnly: true,          // Not accessible via JavaScript
      secure: process.env.NODE_ENV === 'production',  // HTTPS only in prod
      sameSite: 'lax',         // CSRF protection
      maxAge: 60 * 60 * 24 * 7, // 7 days
    },
  })
}

// Usage in server function
export const login = createServerFn({ method: 'POST' })
  .validator(loginSchema)
  .handler(async ({ data }) => {
    const session = await getSession()

    // Verify credentials
    const user = await verifyCredentials(data.email, data.password)
    if (!user) {
      throw new Error('Invalid credentials')
    }

    // Store only essential data in session
    await session.update({
      userId: user.id,
      email: user.email,
      createdAt: Date.now(),
    })

    return { success: true }
  })
```

## Good Example: Full Authentication Flow

```tsx
// lib/auth.functions.ts
import { createServerFn } from '@tanstack/react-start'
import { redirect } from '@tanstack/react-router'
import { getSession } from './session.server'
import { hashPassword, verifyPassword } from './password.server'

// Login
export const login = createServerFn({ method: 'POST' })
  .validator(z.object({
    email: z.string().email(),
    password: z.string().min(1),
  }))
  .handler(async ({ data }) => {
    const user = await db.users.findUnique({
      where: { email: data.email },
    })

    if (!user || !await verifyPassword(data.password, user.passwordHash)) {
      throw new Error('Invalid email or password')
    }

    const session = await getSession()
    await session.update({
      userId: user.id,
      email: user.email,
    })

    throw redirect({ to: '/dashboard' })
  })

// Logout
export const logout = createServerFn({ method: 'POST' })
  .handler(async () => {
    const session = await getSession()
    await session.clear()
    throw redirect({ to: '/' })
  })

// Get current user
export const getCurrentUser = createServerFn()
  .handler(async () => {
    const session = await getSession()
    const data = await session.data

    if (!data?.userId) {
      return null
    }

    const user = await db.users.findUnique({
      where: { id: data.userId },
      select: {
        id: true,
        email: true,
        name: true,
        avatar: true,
        // Don't include passwordHash!
      },
    })

    return user
  })
```

## Good Example: Session with Role-Based Access

```tsx
// lib/session.server.ts
interface SessionData {
  userId: string
  email: string
  role: 'user' | 'admin'
  createdAt: number
}

export async function getSessionData(): Promise<SessionData | null> {
  const session = await getSession()
  const data = await session.data

  if (!data?.userId) return null

  // Validate session age
  const maxAge = 7 * 24 * 60 * 60 * 1000  // 7 days
  if (Date.now() - data.createdAt > maxAge) {
    await session.clear()
    return null
  }

  return data as SessionData
}

// Middleware for admin-only routes
export const requireAdmin = createMiddleware()
  .server(async ({ next }) => {
    const session = await getSessionData()

    if (!session || session.role !== 'admin') {
      throw redirect({ to: '/unauthorized' })
    }

    return next({ context: { session } })
  })
```

## Session Security Checklist

| Setting | Value | Purpose |
|---------|-------|---------|
| `httpOnly` | `true` | Prevents XSS from accessing cookie |
| `secure` | `true` in prod | Requires HTTPS |
| `sameSite` | `'lax'` or `'strict'` | CSRF protection |
| `maxAge` | Application-specific | Session duration |
| `password` | 32+ random chars | Encryption key |

## Context

- Always use HTTP-only cookies for session tokens
- Generate `SESSION_SECRET` with `openssl rand -base64 32`
- Store minimal data in session - fetch user details on demand
- Implement session rotation on privilege changes
- Consider session invalidation on password change
- Use `sameSite: 'strict'` for highest CSRF protection
