grey-haven-authentication-patterns
About
This skill provides Grey Haven's authentication patterns using better-auth for TanStack Start projects. It implements multi-tenant authentication with magic links, passkeys, OAuth providers, Redis session management, and JWT claims including tenant_id. Use this when implementing authentication features in multi-tenant applications.
Quick Install
Claude Code
Recommended/plugin add https://github.com/greyhaven-ai/claude-code-configgit clone https://github.com/greyhaven-ai/claude-code-config.git ~/.claude/skills/grey-haven-authentication-patternsCopy and paste this command in Claude Code to install this skill
Documentation
Grey Haven Authentication Patterns
Follow Grey Haven Studio's authentication patterns using better-auth for TanStack Start projects with multi-tenant support.
Stack
- better-auth: Authentication library for TanStack Start
- Drizzle ORM: Database adapter for better-auth
- Doppler: Secret management (BETTER_AUTH_SECRET, OAuth keys)
- Redis: Session storage (via Upstash)
- PostgreSQL: User and session data with RLS
Critical Requirements
Multi-Tenant Authentication
ALWAYS include tenant_id in auth tables:
export const users = pgTable("users", {
id: uuid("id").primaryKey().defaultRandom(),
tenant_id: uuid("tenant_id").notNull(), // CRITICAL!
email_address: text("email_address").notNull().unique(),
// ... other fields
});
export const sessions = pgTable("sessions", {
id: uuid("id").primaryKey().defaultRandom(),
user_id: uuid("user_id").references(() => users.id),
tenant_id: uuid("tenant_id").notNull(), // CRITICAL!
// ... other fields
});
Doppler for Secrets
NEVER commit auth secrets:
# Doppler provides these at runtime
BETTER_AUTH_SECRET=<generated-secret>
BETTER_AUTH_URL=https://app.example.com
GOOGLE_CLIENT_ID=<from-google-console>
GOOGLE_CLIENT_SECRET=<from-google-console>
Basic Configuration
// lib/server/auth.ts
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "@better-auth/drizzle";
import { db } from "~/lib/server/db";
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg",
schema,
}),
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
},
secret: process.env.BETTER_AUTH_SECRET!,
baseURL: process.env.BETTER_AUTH_URL!,
trustedOrigins: [process.env.BETTER_AUTH_URL!],
});
Authentication Methods
1. Email & Password
// Sign up with email verification
await auth.signUp.email({
email: "[email protected]",
password: "secure-password",
name: "John Doe",
data: {
tenant_id: tenantId, // Include tenant context
},
});
// Sign in
await auth.signIn.email({
email: "[email protected]",
password: "secure-password",
});
2. Magic Links
// Send magic link
await auth.magicLink.send({
email: "[email protected]",
callbackURL: "/auth/verify",
});
// Verify magic link token
await auth.magicLink.verify({
token: tokenFromEmail,
});
3. OAuth Providers
// Google OAuth
export const auth = betterAuth({
// ... other config
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
scopes: ["email", "profile"],
},
},
});
// Redirect to Google
await auth.signIn.social({
provider: "google",
callbackURL: "/auth/callback",
});
4. Passkeys (WebAuthn)
// Enable passkeys
export const auth = betterAuth({
// ... other config
passkey: {
enabled: true,
},
});
// Register passkey
await auth.passkey.register({
name: "My MacBook",
});
// Authenticate with passkey
await auth.passkey.authenticate();
Session Management
JWT Claims with tenant_id
// Middleware to extract tenant from JWT
export async function getTenantFromSession() {
const session = await auth.api.getSession();
if (!session) {
throw new Error("Not authenticated");
}
return {
userId: session.user.id,
tenantId: session.user.tenant_id, // From JWT claims
user: session.user,
};
}
Session Storage with Redis
// Use Upstash Redis for sessions
export const auth = betterAuth({
// ... other config
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // Refresh daily
cookieCache: {
enabled: true,
maxAge: 5 * 60, // 5 minutes
},
},
});
Protected Routes
TanStack Router beforeLoad
// routes/_authenticated/_layout.tsx
import { createFileRoute, redirect } from "@tanstack/react-router";
import { getTenantFromSession } from "~/lib/server/auth";
export const Route = createFileRoute("/_authenticated/_layout")({
beforeLoad: async () => {
try {
const { userId, tenantId, user } = await getTenantFromSession();
return { session: { userId, tenantId, user } };
} catch {
throw redirect({
to: "/auth/login",
search: { redirect: location.href },
});
}
},
});
Supporting Documentation
All supporting files are under 500 lines per Anthropic best practices:
-
examples/ - Complete auth examples
- magic-link.md - Magic link implementation
- oauth.md - OAuth provider setup
- passkeys.md - Passkey authentication
- multi-tenant.md - Multi-tenant patterns
- INDEX.md - Examples navigation
-
reference/ - Auth references
- better-auth-config.md - Configuration options
- session-management.md - Session patterns
- doppler-setup.md - Secret management
- INDEX.md - Reference navigation
-
templates/ - Copy-paste ready templates
- auth-config.ts - better-auth configuration
- auth-schema.ts - Drizzle auth schema
- protected-route.tsx - Protected route layout
-
checklists/ - Security checklists
- auth-checklist.md - Authentication security
When to Apply This Skill
Use this skill when:
- Implementing user authentication
- Adding OAuth providers (Google, GitHub)
- Setting up magic link authentication
- Configuring passkey support
- Managing user sessions
- Implementing multi-tenant auth
- Securing API endpoints
- Setting up protected routes
Template Reference
These patterns are from Grey Haven's production templates:
- cvi-template: TanStack Start + better-auth + multi-tenant
Critical Reminders
- tenant_id: Always include in users and sessions tables
- Doppler: Use for all auth secrets (never commit!)
- Email verification: Required for email/password signup
- JWT claims: Include tenant_id in session data
- Protected routes: Use beforeLoad for auth checks
- Redis sessions: Use Upstash for distributed sessions
- OAuth secrets: Store in Doppler (Google, GitHub, etc.)
- RLS policies: Create for users and sessions tables
- Session expiry: 7 days default, refresh daily
- Magic links: 15-minute expiry, single-use tokens
GitHub Repository
Related Skills
sglang
MetaSGLang is a high-performance LLM serving framework that specializes in fast, structured generation for JSON, regex, and agentic workflows using its RadixAttention prefix caching. It delivers significantly faster inference, especially for tasks with repeated prefixes, making it ideal for complex, structured outputs and multi-turn conversations. Choose SGLang over alternatives like vLLM when you need constrained decoding or are building applications with extensive prefix sharing.
evaluating-llms-harness
TestingThis Claude Skill runs the lm-evaluation-harness to benchmark LLMs across 60+ standardized academic tasks like MMLU and GSM8K. It's designed for developers to compare model quality, track training progress, or report academic results. The tool supports various backends including HuggingFace and vLLM models.
llamaguard
OtherLlamaGuard is Meta's 7-8B parameter model for moderating LLM inputs and outputs across six safety categories like violence and hate speech. It offers 94-95% accuracy and can be deployed using vLLM, Hugging Face, or Amazon SageMaker. Use this skill to easily integrate content filtering and safety guardrails into your AI applications.
langchain
MetaLangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.
