git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/api" ~/.claude/skills/diegosouzapw-awesome-omni-skill-api-b7f63c && rm -rf "$T"
skills/development/api/SKILL.mdAPI 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.
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
npx sveltekit-auth init [options]
Options
| Option | Description | Default |
|---|---|---|
| postgres, mysql, sqlite | postgres |
| drizzle, prisma | drizzle |
| Output directory | src/lib/server/schemas |
| Table casing: snake, camel, pascal | snake |
| Column casing: snake, camel, pascal | snake |
| ID type: uuid, cuid | uuid |
| Use singular table names | (plural) |
| Table name prefix (e.g., ) | (none) |
| Add deletedAt column | false |
| Preview without writing | false |
| Overwrite existing files | false |
Generated Files
The CLI creates two files:
1.
- User-owned, extendableusers.ts
// 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.
- Library-managed, don't modifyauth.ts
// Internal auth tables - typically don't modify export const accounts = pgTable('accounts', { /* ... */ }); export const sessions = pgTable('sessions', { /* ... */ }); export const verifications = pgTable('verifications', { /* ... */ });
Examples
# 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?
- Consistent interface - Same methods whether using Drizzle, Prisma, or custom adapter
- Swappable implementations - Change database without changing application code
- Shared across routes - Both API endpoints and form actions use the same adapter
- Testable - Use createMemoryAdapter for unit tests
Adapter Interface
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:
// 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:
// 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:
// 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:
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:
// 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:
// 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):
| Route | Method | Description |
|---|---|---|
| GET | Sign-in page |
| POST | Initiate OAuth flow |
| GET | OAuth callback |
| POST | Sign out |
| GET | Get current session (JSON) |
| GET | Get CSRF token |
Session Strategies
JWT Strategy (default)
Sessions stored in signed cookies. No database calls for session validation.
{ session: { strategy: 'jwt', maxAge: 30 * 24 * 60 * 60 } }
Database Strategy
Sessions stored in database. Allows server-side session revocation.
{ session: { strategy: 'database', maxAge: 30 * 24 * 60 * 60 } }
Route Protection
// 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:
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:
- Implement all required methods from the
interfaceAdapter - Work with the schema generated by the CLI (or compatible schema)
- Return objects with at least an
field for usersid - Handle the relationship between users, accounts, and sessions
Error Handling
Adapters throw
AdapterError for known error conditions:
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:
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/:
| Spec | Description |
|---|---|
| System architecture, data flow, security overview |
| Database adapter interface, schema details |
| CLI options, naming conventions, output formats |
| OAuth and credentials provider configuration |
| Email verification, password reset flows |
| Password hashing, session encryption, CSRF protection |