AgentSkillsCN

api

sveltekit-auth库架构指南

SKILL.md
--- frontmatter
name: api
description: Guidelines for the sveltekit-auth library architecture
triggers:
  - api
  - auth
  - providers
  - adapters
  - middleware
  - oauth
  - credentials
  - session
  - cli
  - schema
files:
  - src/lib/index.ts
  - src/lib/types.ts
  - src/lib/adapters/drizzle.ts
  - src/lib/providers/oauth.ts
  - src/lib/middleware/index.ts
  - src/cli/index.ts

API Design Skill

Guidelines for designing and implementing the sveltekit-auth library.

Library Architecture

This is an authentication library for SvelteKit applications. It provides:

  • CLI - Schema generation with customizable options
  • Providers - OAuth (GitHub, Google, Discord) and credentials authentication
  • Adapters - Database abstraction following the Repository pattern
  • Middleware - Route protection and session handling
  • Flows - Password reset, email verification, etc.
code
src/
  cli/
    index.ts            # CLI for schema generation
  lib/
    providers/          # Authentication providers
      oauth.ts          # Base OAuth implementation
      github.ts         # GitHub provider
      google.ts         # Google provider
      discord.ts        # Discord provider
      credentials.ts    # Username/password provider
    adapters/           # Database adapters (Repository pattern)
      drizzle.ts        # Drizzle ORM adapter
      prisma.ts         # Prisma adapter (planned)
      memory.ts         # In-memory adapter (testing)
    middleware/         # SvelteKit middleware
      index.ts          # Main auth handle
      routes.ts         # Route protection helpers
    flows/              # Authentication flows
      verification.ts   # Email verification
      password-reset.ts # Password reset flow
    client/             # Client-side helpers
      auth-client.ts    # Browser auth client
      actions.ts        # Form action helpers
    utils/              # Utilities
      jwt.ts            # JWT handling
      session.ts        # Session management
      password.ts       # Password hashing
      crypto.ts         # Crypto utilities
    types.ts            # TypeScript interfaces
    index.ts            # Main exports

CLI Schema Generation

The CLI generates schema files that users own and can extend. This enforces the base structure while allowing full customization.

Usage

bash
npx sveltekit-auth init [options]

Options

OptionDescriptionDefault
-d, --databasepostgres, mysql, sqlitepostgres
--ormdrizzle, prismadrizzle
-o, --outputOutput directorysrc/lib/server/schemas
-t, --tablesTable casing: snake, camel, pascalsnake
-c, --columnsColumn casing: snake, camel, pascalsnake
--idID type: uuid, cuiduuid
--singularUse singular table names(plural)
--prefixTable name prefix (e.g., auth_)(none)
--soft-deleteAdd deletedAt columnfalse
--dry-runPreview without writingfalse
-f, --forceOverwrite existing filesfalse

Generated Files

The CLI creates two files:

1. users.ts - User-owned, extendable

typescript
// Generated by CLI - extend with your custom fields
export const users = pgTable('users', {
  id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
  name: text('name'),
  email: text('email'),
  image: text('image'),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().notNull(),

  // Add your custom fields here:
  // role: text('role').$type<'user' | 'admin'>().default('user'),
  // organizationId: text('organization_id').references(() => organizations.id),
});

2. auth.ts - Library-managed, don't modify

typescript
// Internal auth tables - typically don't modify
export const accounts = pgTable('accounts', { /* ... */ });
export const sessions = pgTable('sessions', { /* ... */ });
export const verifications = pgTable('verifications', { /* ... */ });

Examples

bash
# Default: PostgreSQL + Drizzle, snake_case, plural, UUID
npx sveltekit-auth init

# MySQL with camelCase columns
npx sveltekit-auth init -d mysql --columns camel

# Prisma with table prefix
npx sveltekit-auth init --orm prisma --prefix auth_

# Preview what will be generated
npx sveltekit-auth init --dry-run

# Use CUID for IDs with soft deletes
npx sveltekit-auth init --id cuid --soft-delete

Adapter Pattern (Repository)

All database adapters implement the Adapter interface, which follows the Repository pattern. This provides a consistent API regardless of the underlying database or ORM.

Why Repository Pattern?

  1. Consistent interface - Same methods whether using Drizzle, Prisma, or custom adapter
  2. Swappable implementations - Change database without changing application code
  3. Shared across routes - Both API endpoints and form actions use the same adapter
  4. Testable - Use createMemoryAdapter for unit tests

Adapter Interface

typescript
interface Adapter {
  // User methods - only requires 'id' field
  createUser(data: Record<string, unknown>): Promise<{ id: string; [key: string]: unknown }>;
  getUser(id: string): Promise<{ id: string; [key: string]: unknown } | null>;
  getUserByEmail(email: string): Promise<{ id: string; [key: string]: unknown } | null>;
  getUserByAccount(params: { provider: string; providerAccountId: string }): Promise<{ id: string; [key: string]: unknown } | null>;
  updateUser(user: { id: string; [key: string]: unknown }): Promise<{ id: string; [key: string]: unknown }>;
  deleteUser(id: string): Promise<void>;

  // Account methods
  linkAccount(account: Omit<AdapterAccount, 'id' | 'createdAt' | 'updatedAt'>): Promise<AdapterAccount>;
  unlinkAccount(params: { provider: string; providerAccountId: string }): Promise<void>;
  getAccount(params: { provider: string; providerAccountId: string }): Promise<AdapterAccount | null>;
  getAccountByLogin?(provider: string, login: string): Promise<AdapterAccount | null>;
  updateAccount?(accountId: string, data: Partial<AdapterAccount>): Promise<AdapterAccount | null>;

  // Session methods
  createSession(session: Omit<AdapterSession, 'id' | 'createdAt' | 'updatedAt'>): Promise<AdapterSession>;
  getSessionAndUser(sessionToken: string): Promise<{ session: AdapterSession; user: { id: string; [key: string]: unknown } } | null>;
  updateSession(session: { sessionToken: string; [key: string]: unknown }): Promise<AdapterSession | null>;
  deleteSession(sessionToken: string): Promise<void>;

  // Verification token methods
  createVerificationToken(token: VerificationToken): Promise<VerificationToken>;
  useVerificationToken(params: { identifier: string; token: string }): Promise<VerificationToken | null>;
}

User Schema Flexibility

The library only requires user.id for relationships. Everything else flows through untouched:

typescript
// Library internals only access user.id
const session = await adapter.createSession({
  userId: user.id,  // Only field we need
  sessionToken: generateToken(),
  expires: new Date(Date.now() + maxAge)
});

// User's custom fields pass through to their application
const user = await adapter.getUser(id);
// user.role, user.organizationId, etc. - all available

Adapter Usage

In API endpoints:

typescript
// src/routes/api/users/[id]/+server.ts
import { json } from '@sveltejs/kit';

export const GET: RequestHandler = async ({ params, locals }) => {
  const user = await locals.auth.adapter.getUser(params.id);
  if (!user) return json({ error: 'Not found' }, { status: 404 });
  return json(user);
};

In form actions:

typescript
// src/routes/settings/+page.server.ts
export const actions: Actions = {
  updateProfile: async ({ request, locals }) => {
    const data = await request.formData();
    const user = await locals.auth.adapter.updateUser({
      id: locals.user.id,
      name: data.get('name'),
      // Custom fields work too
      bio: data.get('bio')
    });
    return { success: true, user };
  }
};

In tests:

typescript
import { createMemoryAdapter } from '@sveltekit-auth/core/adapters';

const adapter = createMemoryAdapter();
const user = await adapter.createUser({ email: 'test@example.com' });

Provider Pattern

Providers implement authentication strategies:

typescript
// OAuth Provider
export function GitHub(config: {
  clientId: string;
  clientSecret: string;
}): OAuthProviderConfig {
  return {
    id: 'github',
    name: 'GitHub',
    type: 'oauth',
    authorization: 'https://github.com/login/oauth/authorize',
    token: 'https://github.com/login/oauth/access_token',
    userinfo: 'https://api.github.com/user',
    profile(profile) {
      return {
        id: profile.id.toString(),
        name: profile.name ?? profile.login,
        email: profile.email,
        image: profile.avatar_url
      };
    },
    ...config
  };
}

// Credentials Provider
export function Credentials(config: {
  loginType: 'email' | 'username' | 'either';
  authorize: (credentials, request) => Promise<User | null>;
}): CredentialsProviderConfig {
  return {
    id: 'credentials',
    name: 'Credentials',
    type: 'credentials',
    ...config
  };
}

Middleware Integration

The auth middleware integrates with SvelteKit's handle hook:

typescript
// src/hooks.server.ts
import { createAuth } from '@sveltekit-auth/core';
import { createDrizzleAdapter } from '@sveltekit-auth/core/adapters/drizzle';
import GitHub from '@sveltekit-auth/core/providers/github';
import { db } from '$lib/server/db';
import * as schema from '$lib/server/schemas/auth';

export const handle = createAuth({
  adapter: createDrizzleAdapter(db, schema),
  providers: [
    GitHub({
      clientId: env.GITHUB_CLIENT_ID,
      clientSecret: env.GITHUB_CLIENT_SECRET
    })
  ],
  secret: env.AUTH_SECRET
});

Auth Routes

The library handles these routes under the configured base path (default: /auth):

RouteMethodDescription
/auth/signinGETSign-in page
/auth/signin/:providerPOSTInitiate OAuth flow
/auth/callback/:providerGETOAuth callback
/auth/signoutPOSTSign out
/auth/sessionGETGet current session (JSON)
/auth/csrfGETGet CSRF token

Session Strategies

JWT Strategy (default)

Sessions stored in signed cookies. No database calls for session validation.

typescript
{ session: { strategy: 'jwt', maxAge: 30 * 24 * 60 * 60 } }

Database Strategy

Sessions stored in database. Allows server-side session revocation.

typescript
{ session: { strategy: 'database', maxAge: 30 * 24 * 60 * 60 } }

Route Protection

typescript
// src/routes/dashboard/+page.server.ts
import { redirect } from '@sveltejs/kit';

export async function load({ locals }) {
  const session = await locals.auth.getSession();
  if (!session) {
    throw redirect(302, '/auth/signin');
  }
  return { user: session.user };
}

Creating New Adapters

To create a new adapter (e.g., for a different database), implement the Adapter interface:

typescript
import type { Adapter } from '@sveltekit-auth/core';

export function MyCustomAdapter(db: MyDB): Adapter {
  return {
    async createUser(data) {
      const user = await db.users.insert(data);
      return user;
    },

    async getUser(id) {
      return db.users.findById(id);
    },

    // ... implement all required methods
  };
}

The adapter must:

  1. Implement all required methods from the Adapter interface
  2. Work with the schema generated by the CLI (or compatible schema)
  3. Return objects with at least an id field for users
  4. Handle the relationship between users, accounts, and sessions

Error Handling

Adapters throw AdapterError for known error conditions:

typescript
import { AdapterError, AdapterErrorCodes } from '@sveltekit-auth/core';

class AdapterError extends Error {
  code: string;
}

const AdapterErrorCodes = {
  USER_NOT_FOUND: 'USER_NOT_FOUND',
  USER_ALREADY_EXISTS: 'USER_ALREADY_EXISTS',
  ACCOUNT_NOT_FOUND: 'ACCOUNT_NOT_FOUND',
  ACCOUNT_ALREADY_LINKED: 'ACCOUNT_ALREADY_LINKED',
  SESSION_NOT_FOUND: 'SESSION_NOT_FOUND',
  SESSION_EXPIRED: 'SESSION_EXPIRED',
  TOKEN_NOT_FOUND: 'TOKEN_NOT_FOUND',
  TOKEN_EXPIRED: 'TOKEN_EXPIRED',
  DATABASE_ERROR: 'DATABASE_ERROR'
};

Middleware Utilities

The library exports additional middleware utilities:

typescript
import { createAuth, createProtectedRoutesMiddleware, sequence } from '@sveltekit-auth/core';

// Combine multiple middleware handlers
export const handle = sequence(
  createAuth({ /* config */ }),
  createProtectedRoutesMiddleware({
    protectedRoutes: ['/dashboard/*', '/settings/*'],
    publicRoutes: ['/auth/*', '/about'],
    unauthorizedRedirect: '/auth/signin'
  })
);

Roadmap & TODOs

See .claude/todos/improvements.todo.md for planned features:

High Priority:

  • Account linking for OAuth (link multiple providers to one account)
  • More OAuth providers (Apple, Microsoft, Twitter, Facebook, LinkedIn)
  • Rate limiting for auth endpoints

Medium Priority:

  • Magic link authentication (passwordless)
  • Two-factor authentication (TOTP)
  • Session management utilities

Lower Priority:

  • Full CSRF protection
  • Audit logging

Detailed Specifications

For comprehensive details, see the spec files in .claude/specs/:

SpecDescription
architecture.spec.mdSystem architecture, data flow, security overview
adapters.spec.mdDatabase adapter interface, schema details
cli.spec.mdCLI options, naming conventions, output formats
providers.spec.mdOAuth and credentials provider configuration
flows.spec.mdEmail verification, password reset flows
security.spec.mdPassword hashing, session encryption, CSRF protection