xiaohongshu
xiaohongshu
better-auth-v2
Production-ready authentication system using Better Auth v2 with latest features. Includes OAuth providers, advanced RBAC, multi-tenant support, and security best practices.
Full skill instructions
Provides cutting-edge authentication templates and expert integration guidance using Better Auth v2. This skill analyzes your project architecture and implements a full-featured authentication system including:
✅ Automated Implementation Plan: Analyzes project structure (framework, DB, backend) and provides a tailored integration strategy. ✅ Latest Better Auth v2 configuration templates ✅ Enhanced database schemas (Prisma, Drizzle, Kysely, MongoDB) ✅ Modern React/Vue/Svelte authentication clients ✅ Middleware for route protection with advanced features ✅ Fine-grained RBAC and permission utilities ✅ OAuth provider setup with token encryption ✅ 2FA and passkey authentication templates ✅ Advanced security configurations ✅ Separate Backend Creation: For static/SSR-incompatible sites, creates a dedicated Hono + Node.js backend.
❌ Custom business logic implementation (use better-auth-specialist subagent) ❌ Database migration scripts execution (provides the scripts, does not run them) ❌ OAuth credential provisioning (you must provide Client IDs/Secrets) ❌ Production deployment without security review
Use this skill when:
How to use:
Load the better-auth-v2 skill and use the [template-name] for [framework] with [database-adapter]
OR
Simply ask: "Add Better-Auth to my project" to start the automated integrator flow.
Project Analysis Phase
Database Strategy
Backend Setup
Better-Auth Core Implementation
better-auth + better-auth-ui + required plugins (google, apple, facebook, twitter)Onboarding Flow
/onboardinguser_metadata table for future personalizationUI Integration (Better-Auth-UI)
/sign-in, /sign-up pages with <AuthUI />Session Management & Protection
authClient React hook for frontend stateAfter activation, I will deliver:
.env.example with all required variablesUser says:
“I have a Next.js 14 App Router site using Prisma + PostgreSQL on Supabase. Add Better-Auth with Google, Apple, Facebook, X login and ask users about their dev experience after signup.”
This Skill Instantly Activates → Delivers:
/src/auth folder structure with full Better-Auth config/app/(auth)/sign-in/[[...better-auth]]/page.tsx using <AuthUI />/app/onboarding/page.tsx with questionnaireUser says:
“My site is a static Astro + React site. Just add login with social providers.”
This Skill Responds:
→ Creates separate better-auth-backend/ folder (Hono + Node)
→ Deploys in < 2 minutes to Vercel
→ Adds minimal client authClient to Astro
→ Injects header buttons + modal sign-in using Better-Auth-UI
→ Full social login working on a 100% static frontend
Production Auth Config v2 (config/production-auth-v2.template.ts)
Enterprise Auth Config (config/enterprise-auth.template.ts)
OAuth-First Config (config/oauth-first.template.ts)
B2B SaaS Config (config/b2b-saas.template.ts)
schemas/prisma-v2.template.prisma)schemas/drizzle-v2.template.ts)schemas/kysely-v2.template.ts)schemas/mongodb.template.ts)client/react-auth-client-v2.template.tsx)client/vue3-auth-client.template.ts)client/svelte-auth-client.template.ts)client/auth-utils.template.ts)components/SignInForm-v2.template.tsx)components/SignUpFlow.template.tsx)components/OAuthProviderGrid.template.tsx)components/TwoFactorSetup.template.tsx)components/OrganizationManager.template.tsx)components/PasskeyEnroll.template.tsx)components/SessionSecurity.template.tsx)middleware/nextjs-app-router.template.ts)middleware/rbac-gateway.template.ts)middleware/rate-limiter.template.ts)middleware/audit-logger.template.ts)utils/permission-engine.template.ts)utils/role-manager.template.ts)utils/security-validator.template.ts)utils/oauth-helper.template.ts)File: .claude/skills/better-auth-v2/config/production-auth-v2.template.ts
/**
* Better Auth v2 - Production-Ready Configuration
*
* Latest Features:
* - OAuth token encryption
* - Account linking across providers
* - Enhanced security defaults
* - Performance optimizations
*
* Setup:
* 1. Copy to `lib/auth.ts`
* 2. Configure environment variables
* 3. Set up database with schema
* 4. Configure OAuth providers
*/
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { organization, twoFactor, passkey } from "better-auth/plugins";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export const auth = betterAuth({
// ===========================================
// DATABASE CONFIGURATION
// ===========================================
database: prismaAdapter(prisma, {
provider: "postgresql",
}),
// ===========================================
// SECURITY: SECRET KEY (REQUIRED)
// ===========================================
secret: process.env.BETTER_AUTH_SECRET || process.env.AUTH_SECRET!,
// ===========================================
// BASE URL CONFIGURATION
// ===========================================
baseURL: process.env.BETTER_AUTH_URL || "http://localhost:3000",
// ===========================================
// EMAIL & PASSWORD AUTHENTICATION
// ===========================================
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
minPasswordLength: 8,
maxPasswordLength: 128,
// Enhanced password validation
passwordValidation: (password) => {
const checks = {
length: password.length >= 8 && password.length <= 128,
upper: /[A-Z]/.test(password),
lower: /[a-z]/.test(password),
number: /[0-9]/.test(password),
special: /[!@#$%^&*(),.?":{}|<>]/.test(password),
noCommon: !password.toLowerCase().includes('password'),
noUsername: false, // Will be checked against username/email
};
if (Object.values(checks).some(check => !check)) {
return {
valid: false,
message: "Password must be 8-128 characters with uppercase, lowercase, number, and special character",
};
}
return { valid: true };
},
sendEmailVerificationOnSignUp: true,
},
// ===========================================
// OAUTH PROVIDERS WITH ENCRYPTION
// ===========================================
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
scope: ["user:email"],
// Optional: Custom redirect URI
// redirectURI: "https://yourapp.com/auth/callback/github"
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
scope: ["openid", "email", "profile"],
},
discord: {
clientId: process.env.DISCORD_CLIENT_ID!,
clientSecret: process.env.DISCORD_CLIENT_SECRET!,
scope: ["identify", "email"],
},
microsoft: {
clientId: process.env.MICROSOFT_CLIENT_ID!,
clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
tenant: "common",
scope: ["openid", "email", "profile"],
},
// Additional providers
/*
apple: {
clientId: process.env.APPLE_CLIENT_ID!,
clientSecret: process.env.APPLE_CLIENT_SECRET!,
},
twitter: {
clientId: process.env.TWITTER_CLIENT_ID!,
clientSecret: process.env.TWITTER_CLIENT_SECRET!,
},
facebook: {
clientId: process.env.FACEBOOK_CLIENT_ID!,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET!,
},
*/
},
// ===========================================
// ACCOUNT MANAGEMENT WITH ENCRYPTION
// ===========================================
account: {
// Model configuration (optional)
modelName: "accounts",
fields: {
userId: "user_id",
},
// Encrypt OAuth tokens at rest (SECURITY: Production best practice)
encryptOAuthTokens: true,
// Store account in cookie for database-less flows
storeAccountCookie: false, // Set true only for specific use cases
// Account linking configuration
accountLinking: {
enabled: true,
// Trusted providers for auto-linking
trustedProviders: ["google", "github", "microsoft", "apple"],
// Require same email for linking
allowDifferentEmails: false,
},
},
// ===========================================
// ENHANCED SESSION MANAGEMENT
// ===========================================
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // Update every 24 hours
cookieCache: {
enabled: true,
maxAge: 5 * 60, // 5 minutes
},
// Additional session security
sessionExpiration: {
warning: 60 * 60 * 24, // Warn 1 day before expiration
},
},
// ===========================================
// ADVANCED SECURITY CONFIGURATION
// ===========================================
advanced: {
// CSRF protection (NEVER disable in production)
disableCSRFCheck: false,
// Trusted origins
trustedOrigins: [
process.env.BETTER_AUTH_URL!,
...(process.env.NODE_ENV === "development"
? ["http://localhost:3000", "http://localhost:5173", "http://localhost:4173"]
: []
),
],
// Cookie security
defaultCookieAttributes: {
sameSite: "lax",
secure: process.env.NODE_ENV === "production",
httpOnly: true,
// Optional: Domain for cross-subdomain cookies
// domain: ".yourdomain.com",
},
// Cross-subdomain cookies
crossSubDomainCookies: {
enabled: false, // Enable if using subdomains
// domain: ".yourdomain.com",
},
},
// ===========================================
// RATE LIMITING WITH FINE-GRAINED CONTROL
// ===========================================
rateLimit: {
enabled: true,
window: 60, // 1 minute window
max: 10, // 10 requests per window
// Endpoint-specific limits
customRules: [
{
pathPattern: "/api/auth/sign-in",
window: 60,
max: 5, // Stricter for sign-in
},
{
pathPattern: "/api/auth/sign-up",
window: 300, // 5 minutes
max: 3, // Prevent mass sign-ups
},
{
pathPattern: "/api/auth/reset-password",
window: 900, // 15 minutes
max: 3, // Prevent spam
},
{
pathPattern: "/api/auth/verify-email",
window: 60,
max: 10, // Allow more attempts for verification
},
],
// IP-based rate limiting
ipBasedLimiting: true,
// Custom storage for rate limits (Redis recommended for production)
// storage: customRateLimitStorage,
},
// ===========================================
// USER PROFILE CONFIGURATION
// ===========================================
user: {
// Additional user fields
additionalFields: {
displayName: {
type: "string",
required: false,
},
avatar: {
type: "string",
required: false,
},
bio: {
type: "string",
required: false,
},
preferences: {
type: "json",
required: false,
},
},
// User model configuration
modelName: "users",
fields: {
email: "email_address",
name: "full_name",
},
},
// ===========================================
// PLUGINS FOR ENTERPRISE FEATURES
// ===========================================
plugins: [
// Organization plugin for multi-tenancy
organization({
enabled: true,
organizationLimit: 5, // Max organizations per user
allowUserToCreateOrganization: true,
// Role definitions
roles: [
{
role: "owner",
permissions: ["*"], // Full access
},
{
role: "admin",
permissions: [
"org:*",
"member:*",
"billing:*",
],
},
{
role: "member",
permissions: [
"org:read",
"content:*",
],
},
],
// Custom application roles
customRoles: [
{
role: "moderator",
label: "Content Moderator",
permissions: [
"content:moderate",
"content:delete:any",
"user:warn",
],
},
{
role: "analyst",
label: "Data Analyst",
permissions: [
"analytics:*",
"reports:*",
"data:read",
],
},
],
}),
// Two-factor authentication
twoFactor({
// Require 2FA for specific roles
requireTwoFactorFor: ["admin", "owner"],
// 2FA configuration
issuer: process.env.APP_NAME || "MyApp",
// Backup codes
backupCodes: {
enabled: true,
count: 10,
},
// 2FA methods
methods: ["totp", "sms", "email"],
}),
// Passkey (WebAuthn) support
passkey({
enabled: true,
// Passkey configuration
rpName: process.env.APP_NAME || "MyApp",
rpID: process.env.PASSKEY_RP_ID || "localhost",
origin: process.env.PASSKEY_ORIGIN || "http://localhost:3000",
}),
],
// ===========================================
// EMAIL SERVICE CONFIGURATION
// ===========================================
emailProvider: {
type: "smtp",
config: {
host: process.env.SMTP_HOST!,
port: parseInt(process.env.SMTP_PORT || "587"),
secure: process.env.SMTP_SECURE === "true",
auth: {
user: process.env.SMTP_USER!,
pass: process.env.SMTP_PASSWORD!,
},
},
from: {
name: process.env.EMAIL_FROM_NAME || "MyApp Team",
email: process.env.EMAIL_FROM_ADDRESS || "[email protected]",
},
// Email templates (optional)
templates: {
"email-verification": {
subject: "Verify your email",
text: (data) => `Click here to verify: ${data.url}`,
html: (data) => `<a href="${data.url}">Verify Email</a>`,
},
"password-reset": {
subject: "Reset your password",
text: (data) => `Click here to reset: ${data.url}`,
html: (data) => `<a href="${data.url}">Reset Password</a>`,
},
},
},
// ===========================================
// WEBHOOKS (Optional)
// ===========================================
webhooks: {
// User events
onUserCreated: async (user) => {
console.log("User created:", user.id);
// Trigger welcome email, analytics, etc.
},
onUserDeleted: async (user) => {
console.log("User deleted:", user.id);
// Cleanup user data, revoke access tokens, etc.
},
// Session events
onSignIn: async (session) => {
console.log("User signed in:", session.userId);
// Track login analytics
},
onSignOut: async (session) => {
console.log("User signed out:", session.userId);
// Cleanup session-specific data
},
// Organization events
onOrganizationCreated: async (org) => {
console.log("Organization created:", org.id);
// Initialize organization settings
},
},
// ===========================================
// CUSTOM HANDLERS (Optional)
// ===========================================
/*
onRequest: (request) => {
// Custom request handling
console.log("Auth request:", request.url);
},
onResponse: (response) => {
// Custom response handling
console.log("Auth response:", response.status);
},
onError: (error, context) => {
// Custom error handling
console.error("Auth error:", error, context);
// Send to monitoring service
},
*/
});
// ===========================================
// TYPE EXPORTS FOR TYPE SAFETY
// ===========================================
export type Session = typeof auth.$Infer.Session;
export type User = typeof auth.$Infer.User;
export type Organization = typeof auth.$Infer.Organization;
File: .claude/skills/better-auth-v2/client/react-auth-client-v2.template.tsx
/**
* React Auth Client v2 with Enhanced Features
*
* Features:
* - TypeScript integration
* - Organization management
* - 2FA support
* - Passkey authentication
* - Automatic session refresh
* - Error boundaries
*
* Setup:
* 1. Copy to `lib/auth-client.ts`
* 2. Configure environment variables
* 3. Use hooks in components
*/
import React, { createContext, useContext, useEffect, useState } from "react";
import { createAuthClient } from "better-auth/react";
import {
organizationClient,
twoFactorClient,
passkeyClient
} from "better-auth/client";
// ===========================================
// AUTH CLIENT CONFIGURATION
// ===========================================
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_AUTH_URL || "http://localhost:3000",
// Fetch options
fetchOptions: {
credentials: "include",
headers: {
"Content-Type": "application/json",
},
},
// Enable client plugins
plugins: [
organizationClient({
organizationInvitationPage: "/invite",
}),
twoFactorClient({
twoFactorPage: "/2fa",
}),
passkeyClient(),
],
// Storage configuration
storage: {
// Use localStorage for persistence
mode: "localStorage",
},
// Session configuration
session: {
// Auto-refresh session
autoRefresh: true,
// Refresh threshold (5 minutes before expiry)
refreshThreshold: 5 * 60,
},
});
// ===========================================
// EXPORT AUTH METHODS
// ===========================================
export const {
// Authentication
useSession,
signIn,
signUp,
signOut,
// User management
useUser,
updateUser,
// Password management
forgetPassword,
resetPassword,
changePassword,
// Email verification
sendVerificationEmail,
verifyEmail,
// Organizations
useActiveOrganization,
useOrganizations,
createOrganization,
updateOrganization,
deleteOrganization,
inviteMember,
removeMember,
updateMemberRole,
// 2FA
enableTwoFactor,
disableTwoFactor,
verifyTwoFactor,
generateBackupCodes,
// Passkeys
createPasskey,
deletePasskey,
usePasskeys,
// OAuth
listConnectedAccounts,
unlinkAccount,
} = authClient;
// ===========================================
// REACT CONTEXT PROVIDER
// ===========================================
interface AuthContextType {
session: Session | null;
user: User | null;
isLoading: boolean;
error: string | null;
// Actions
signIn: typeof signIn;
signUp: typeof signUp;
signOut: typeof signOut;
refresh: () => Promise<void>;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function AuthProvider({ children }: { children: React.ReactNode }) {
const { data: session, isPending, error } = useSession();
const [refreshing, setRefreshing] = useState(false);
// Session refresh handler
const refresh = React.useCallback(async () => {
if (refreshing) return;
setRefreshing(true);
try {
await authClient.getSession({
fetchOptions: {
headers: { "Cache-Control": "no-cache" },
},
});
} catch (err) {
console.error("Failed to refresh session:", err);
} finally {
setRefreshing(false);
}
}, [refreshing]);
// Auto-refresh on visibility change
useEffect(() => {
const handleVisibilityChange = () => {
if (!document.hidden && session) {
refresh();
}
};
document.addEventListener("visibilitychange", handleVisibilityChange);
return () => document.removeEventListener("visibilitychange", handleVisibilityChange);
}, [session, refresh]);
// Periodic session refresh
useEffect(() => {
if (!session) return;
const interval = setInterval(refresh, 5 * 60 * 1000); // 5 minutes
return () => clearInterval(interval);
}, [session, refresh]);
const contextValue: AuthContextType = {
session,
user: session?.user || null,
isLoading: isPending || refreshing,
error: error?.message || null,
signIn,
signUp,
signOut,
refresh,
};
return (
<AuthContext.Provider value={contextValue}>
{children}
</AuthContext.Provider>
);
}
// ===========================================
// CUSTOM HOOKS
// ===========================================
/**
* Use auth context
*/
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within AuthProvider");
}
return context;
}
/**
* Check if user is authenticated
*/
export function useIsAuthenticated() {
const { session, isLoading } = useAuth();
return { isAuthenticated: !!session, isLoading };
}
/**
* Require authentication (with redirect)
*/
export function useRequireAuth(redirectTo: string = "/signin") {
const { isAuthenticated, isLoading } = useIsAuthenticated();
const router = useRouter();
useEffect(() => {
if (!isLoading && !isAuthenticated) {
router.push(redirectTo);
}
}, [isAuthenticated, isLoading, router, redirectTo]);
return { isAuthenticated, isLoading };
}
/**
* Check user permissions
*/
export function usePermissions(permissions: string | string[]) {
const { session } = useAuth();
const [userPermissions, setUserPermissions] = useState<string[]>([]);
useEffect(() => {
if (!session?.user) {
setUserPermissions([]);
return;
}
// Get user permissions based on role
const getPermissions = async () => {
try {
const response = await fetch("/api/auth/permissions", {
credentials: "include",
});
const data = await response.json();
setUserPermissions(data.permissions || []);
} catch (err) {
console.error("Failed to fetch permissions:", err);
setUserPermissions([]);
}
};
getPermissions();
}, [session]);
const requiredPermissions = Array.isArray(permissions) ? permissions : [permissions];
const hasPermissions = requiredPermissions.every(p =>
userPermissions.includes(p) || userPermissions.includes("*")
);
return { hasPermissions, permissions: userPermissions };
}
/**
* Require specific permissions
*/
export function useRequirePermissions(
permissions: string | string[],
redirectTo: string = "/unauthorized"
) {
const { hasPermissions, isLoading } = usePermissions(permissions);
const router = useRouter();
useEffect(() => {
if (!isLoading && !hasPermissions) {
router.push(redirectTo);
}
}, [hasPermissions, isLoading, router, redirectTo]);
return { hasPermissions, isLoading };
}
/**
* Organization management
*/
export function useOrganization(orgId?: string) {
const [organization, setOrganization] = useState<Organization | null>(null);
const [members, setMembers] = useState<Member[]>([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const fetchOrganization = async () => {
try {
const [orgData, membersData] = await Promise.all([
fetch(`/api/organizations/${orgId || "current"}`, {
credentials: "include",
}),
fetch(`/api/organizations/${orgId || "current"}/members`, {
credentials: "include",
}),
]);
const org = await orgData.json();
const memberList = await membersData.json();
setOrganization(org);
setMembers(memberList);
} catch (err) {
console.error("Failed to fetch organization:", err);
} finally {
setIsLoading(false);
}
};
if (orgId || organization) {
fetchOrganization();
}
}, [orgId, organization]);
return { organization, members, isLoading };
}
/**
* 2FA status
*/
export function useTwoFactor() {
const [isEnabled, setIsEnabled] = useState(false);
const [isVerified, setIsVerified] = useState(false);
const [backupCodes, setBackupCodes] = useState<string[]>([]);
useEffect(() => {
const fetch2FAStatus = async () => {
try {
const response = await fetch("/api/auth/2fa/status", {
credentials: "include",
});
const data = await response.json();
setIsEnabled(data.enabled);
setIsVerified(data.verified);
} catch (err) {
console.error("Failed to fetch 2FA status:", err);
}
};
fetch2FAStatus();
}, []);
const enable = async () => {
const result = await enableTwoFactor();
if (result.data) {
setIsEnabled(true);
if (result.data.backupCodes) {
setBackupCodes(result.data.backupCodes);
}
}
return result;
};
const disable = async () => {
const result = await disableTwoFactor();
if (result.data) {
setIsEnabled(false);
setIsVerified(false);
setBackupCodes([]);
}
return result;
};
return {
isEnabled,
isVerified,
backupCodes,
enable,
disable,
generateBackupCodes,
};
}
/**
* Passkey management
*/
export function usePasskeyAuth() {
const { data: passkeys, isLoading } = usePasskeys();
const [isSupported, setIsSupported] = useState(false);
useEffect(() => {
// Check if WebAuthn is supported
setIsSupported(!!window.PublicKeyCredential);
}, []);
const addPasskey = async (name: string) => {
return await createPasskey({
name,
options: {
authenticatorSelection: {
userVerification: "required",
residentKey: "preferred",
},
},
});
};
const removePasskey = async (passkeyId: string) => {
return await deletePasskey(passkeyId);
};
return {
passkeys: passkeys || [],
isLoading,
isSupported,
addPasskey,
removePasskey,
};
}
// ===========================================
// UTILITY FUNCTIONS
// ===========================================
/**
* Format user display name
*/
export function formatUserName(user: User): string {
if (user.name) return user.name;
if (user.displayName) return user.displayName;
return user.email.split("@")[0];
}
/**
* Get user initials
*/
export function getUserInitials(user: User): string {
const name = formatUserName(user);
return name
.split(" ")
.map(word => word[0])
.join("")
.toUpperCase()
.slice(0, 2);
}
/**
* Validate session activity
*/
export function isSessionActive(session: Session | null): boolean {
if (!session) return false;
const now = new Date();
const expires = new Date(session.expiresAt);
// Check if session expires within 5 minutes
const fiveMinutesFromNow = new Date(now.getTime() + 5 * 60 * 1000);
return expires > fiveMinutesFromNow;
}
// ===========================================
// TYPE EXPORTS
// ===========================================
export type Session = Awaited<ReturnType<typeof useSession>>["data"];
export type User = NonNullable<Session>["user"];
export type Organization = Awaited<ReturnType<typeof useOrganizations>>["data"]?.[0];
export type Member = Awaited<ReturnType<typeof removeMember>>["data"];
Install Dependencies
npm install better-auth@latest @better-auth/prisma-adapter
Set Up Database
Configure Auth
Set Up Client
AuthProviderAdd Components
Create a .env.local file with:
# Core Auth
BETTER_AUTH_SECRET=your-secret-key-here
BETTER_AUTH_URL=http://localhost:3000
# Database
DATABASE_URL=your-database-url
# OAuth Providers
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
DISCORD_CLIENT_ID=your-discord-client-id
DISCORD_CLIENT_SECRET=your-discord-client-secret
MICROSOFT_CLIENT_ID=your-microsoft-client-id
MICROSOFT_CLIENT_SECRET=your-microsoft-client-secret
# Email Service
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-smtp-user
SMTP_PASSWORD=your-smtp-password
[email protected]
EMAIL_FROM_NAME=Your App
# App Settings
APP_NAME=Your App Name
PASSKEY_RP_ID=yourdomain.com
PASSKEY_ORIGIN=https://yourdomain.com
BETTER_AUTH_SECRETBased on real-world OAuth implementation challenges, avoid these common pitfalls:
Problem: Using reserved attribute names like metadata in SQLAlchemy models
# ❌ Causes error
class ChatMessage(Base):
metadata = Column(JSON, nullable=True) # SQLAlchemy reserves this!
# ✅ Fixed version
class ChatMessage(Base):
message_metadata = Column(JSON, nullable=True) # Use different name
Critical Issues:
# OAuth callback to backend
AUTH_REDIRECT_URI=https://your-hf-space.hf.space/backend/auth/google/callback
# Frontend redirect after auth
FRONTEND_URL=https://your-username.github.io/your-repo
Must-Have: OAuth requires SessionMiddleware for state parameter
from starlette.middleware.sessions import SessionMiddleware
app.add_middleware(
SessionMiddleware,
secret_key=settings.jwt_secret_key,
session_cookie="session_id",
max_age=3600,
same_site="lax",
https_only=False, # Set true in production
)
Issue: Static sites have base paths that affect OAuth callbacks
// Docusaurus config
baseUrl: '/ai-humanoid-robotics/', // This affects ALL routes
// Must match in environment
FRONTEND_URL=https://username.github.io/ai-humanoid-robotics
Problem: Provider account ID (sub) field might be missing
def create_or_update_account(db: Session, user: User, provider: str, account_info: dict) -> Account:
provider_account_id = account_info.get('sub') # Primary: Google ID
if not provider_account_id:
# Fallback to email
provider_account_id = account_info.get('email')
if not provider_account_id:
# Final fallback
provider_account_id = str(user.id)
Problem: Cannot create dynamic routes on static sites
// Create static page: src/pages/auth/callback.tsx
export default function AuthCallbackPage() {
return <OAuthCallbackHandler />;
}
Missing Dependencies Cause Failures:
# pyproject.toml
dependencies = [
"sqlalchemy>=2.0.0",
"alembic>=1.12.0",
"python-jose[cryptography]>=3.3.0",
"authlib>=1.2.1",
"itsdangerous>=2.1.0", # Required for SessionMiddleware
]
Essential for Cross-Platform:
# main.py
ALLOWED_ORIGINS = [
"http://localhost:3000",
"https://username.github.io",
"https://huggingface.co",
"https://your-hf-space.hf.space",
]
Must Configure:
class Settings(BaseSettings):
# Add to your settings class
jwt_secret_key: str = "your-super-secret-jwt-key-at-least-32-characters-long"
For detailed documentation and advanced configurations, visit the Better Auth documentation.
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience