@spfn/auth 0.1.0-alpha.86 → 0.1.0-alpha.88

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/entities/schema.ts","../src/server/entities/roles.ts","../src/server/entities/users.ts","../src/server/entities/user-social-accounts.ts","../src/server/entities/user-public-keys.ts","../src/server/entities/verification-codes.ts","../src/server/entities/invitations.ts","../src/server/entities/permissions.ts","../src/server/entities/role-permissions.ts","../src/server/entities/user-permissions.ts","../src/server/entities/index.ts","../src/server/helpers/jwt.ts","../src/server/services/role.service.ts","../src/server/rbac/builtin.ts","../src/server/services/auth.service.ts","../src/server/helpers/password.ts","../src/server/helpers/index.ts","../src/server/helpers/verification.ts","../src/server/helpers/context.ts","../src/server/errors/auth-errors.ts","../src/server/services/key.service.ts","../src/server/services/user.service.ts","../src/server/services/verification.service.ts","../src/server/services/me.service.ts","../src/server/services/rbac.service.ts","../src/lib/config.ts","../src/server/services/permission.service.ts","../src/server/services/index.ts","../src/server/services/invitation.service.ts","../src/server/middleware/authenticate.ts","../src/server/middleware/require-permission.ts","../src/server/middleware/require-role.ts","../src/server/setup.ts"],"sourcesContent":["/**\n * @spfn/auth - Database Schema Definition\n *\n * Defines the 'spfn_auth' PostgreSQL schema for all auth-related tables\n */\n\nimport { createFunctionSchema } from '@spfn/core/db';\n\n/**\n * Auth schema for all authentication and authorization tables\n * Tables: users, roles, permissions, user_invitations, etc.\n */\nexport const authSchema = createFunctionSchema('@spfn/auth');","/**\n * @spfn/auth - Roles Entity\n *\n * Role-based access control (RBAC) roles table\n *\n * Features:\n * - Built-in roles (user, admin, superadmin) - cannot be deleted\n * - System roles (preset roles) - can be deactivated\n * - Custom roles (runtime created) - fully manageable\n * - Priority-based hierarchy\n */\n\nimport { text, boolean, integer, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { authSchema } from './schema';\n\nexport const roles = authSchema.table('roles',\n {\n // Primary key\n id: id(),\n\n // Role identifier (used in code, e.g., 'admin', 'editor')\n // Must be unique, lowercase, kebab-case recommended\n name: text('name').notNull().unique(),\n\n // Display name for UI (e.g., 'Administrator', 'Content Editor')\n displayName: text('display_name').notNull(),\n\n // Role description\n description: text('description'),\n\n // Built-in role flag\n // true: Core package roles (user, admin, superadmin) - cannot be deleted\n // false: Custom or preset roles - can be deleted\n isBuiltin: boolean('is_builtin').notNull().default(false),\n\n // System role flag\n // true: Defined in code (builtin or preset) - deletion restricted\n // false: Runtime created custom role - fully manageable\n isSystem: boolean('is_system').notNull().default(false),\n\n // Active status\n // false: Deactivated role (users cannot be assigned)\n isActive: boolean('is_active').notNull().default(true),\n\n // Priority level (higher = more privileged)\n // superadmin: 100, admin: 80, user: 10\n // Used for role hierarchy and conflict resolution\n priority: integer('priority').notNull().default(10),\n\n ...timestamps(),\n },\n (table) => [\n index('roles_name_idx').on(table.name),\n index('roles_is_system_idx').on(table.isSystem),\n index('roles_is_active_idx').on(table.isActive),\n index('roles_is_builtin_idx').on(table.isBuiltin),\n index('roles_priority_idx').on(table.priority),\n ]\n);\n\n// Type exports\nexport type RoleEntity = typeof roles.$inferSelect;\nexport type NewRoleEntity = typeof roles.$inferInsert;\n\n// Legacy alias for backward compatibility\nexport type Role = RoleEntity;\nexport type NewRole = NewRoleEntity;","/**\n * @spfn/auth - Users Entity\n *\n * Main user table supporting multiple authentication methods\n *\n * Features:\n * - Email or phone-based registration\n * - Password authentication (bcrypt)\n * - OAuth support (nullable passwordHash)\n * - Role-based access control (RBAC)\n * - Account status management\n * - Email/phone verification\n */\n\nimport { text, timestamp, check, boolean, bigint, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { sql } from 'drizzle-orm';\nimport { roles } from './roles';\nimport { authSchema } from './schema';\n\nexport const users = authSchema.table('users',\n {\n // Identity\n id: id(),\n\n // Email address (unique identifier)\n // Used for: login, password reset, notifications\n email: text('email').unique(),\n\n // Phone number in E.164 international format\n // Format: +[country code][number] (e.g., +821012345678)\n // Used for: SMS login, 2FA, notifications\n phone: text('phone').unique(),\n\n // Authentication\n // Bcrypt password hash ($2b$10$[salt][hash], 60 chars)\n // Nullable to support OAuth-only accounts\n passwordHash: text('password_hash'),\n\n // Force password change on next login\n // Use cases: initial setup, security breach, policy violation\n passwordChangeRequired: boolean('password_change_required').notNull().default(false),\n\n // Authorization (Role-Based Access Control)\n // Foreign key to roles table\n // References built-in roles: user (default), admin, superadmin\n // Can also reference custom roles created at runtime\n roleId: bigint('role_id', { mode: 'number' })\n .references(() => roles.id)\n .notNull(),\n\n // Account status\n // - active: Normal operation (default)\n // - inactive: Deactivated (user request, dormant)\n // - suspended: Locked (security incident, ToS violation)\n status: text(\n 'status',\n {\n enum: ['active', 'inactive', 'suspended']\n }\n ).notNull().default('active'),\n\n // Verification timestamps\n // null = unverified, timestamp = verified at this time\n // Email verification (via verification code or magic link)\n emailVerifiedAt: timestamp('email_verified_at', { withTimezone: true }),\n\n // Phone verification (via SMS OTP)\n phoneVerifiedAt: timestamp('phone_verified_at', { withTimezone: true }),\n\n // Metadata\n // Last successful login timestamp\n // Used for: security auditing, dormant account detection\n lastLoginAt: timestamp('last_login_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Database constraints\n // Ensure at least one identifier exists (email OR phone)\n check(\n 'email_or_phone_check',\n sql`${table.email} IS NOT NULL OR ${table.phone} IS NOT NULL`\n ),\n\n // Indexes for query optimization\n index('users_email_idx').on(table.email),\n index('users_phone_idx').on(table.phone),\n index('users_status_idx').on(table.status),\n index('users_role_id_idx').on(table.roleId),\n ]\n);\n\n// Type exports\nexport type User = typeof users.$inferSelect;\nexport type NewUser = typeof users.$inferInsert;\nexport type UserStatus = 'active' | 'inactive' | 'suspended';\n\n// Helper type with computed verification status\nexport type UserWithVerification = User &\n{\n isEmailVerified: boolean;\n isPhoneVerified: boolean;\n};","/**\n * @spfn/auth - User Social Accounts Entity\n *\n * Stores OAuth connections for social login providers\n */\n\nimport { text, timestamp, uniqueIndex } from 'drizzle-orm/pg-core';\nimport { id, timestamps, foreignKey } from '@spfn/core/db';\nimport { users } from './users';\nimport { authSchema } from './schema';\n\nexport const userSocialAccounts = authSchema.table('user_social_accounts',\n {\n id: id(),\n\n // Foreign key to users\n userId: foreignKey('user', () => users.id),\n\n // Provider info\n provider: text(\n 'provider',\n {\n enum: ['google', 'github', 'kakao', 'naver']\n }\n ).notNull(),\n\n providerUserId: text('provider_user_id').notNull(),\n providerEmail: text('provider_email'),\n\n // OAuth tokens (encrypted in production)\n accessToken: text('access_token'),\n refreshToken: text('refresh_token'),\n tokenExpiresAt: timestamp('token_expires_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Unique constraint: one provider account per provider\n uniqueIndex('provider_user_unique_idx')\n .on(table.provider, table.providerUserId),\n ]\n);\n\n// Type exports\nexport type UserSocialAccount = typeof userSocialAccounts.$inferSelect;\nexport type NewUserSocialAccount = typeof userSocialAccounts.$inferInsert;\nexport type SocialProvider = 'google' | 'github' | 'kakao' | 'naver';","/**\n * @spfn/auth - User Public Keys Entity\n *\n * Stores client-generated public keys for JWT verification\n * Supports key rotation and multi-key management per user\n */\n\nimport { text, timestamp, boolean, index } from 'drizzle-orm/pg-core';\nimport { id, foreignKey } from '@spfn/core/db';\nimport { users } from './users';\nimport { authSchema } from './schema';\n\n/**\n * User Public Keys Table\n * Each user can have multiple public keys (for rotation)\n */\nexport const userPublicKeys = authSchema.table(\n 'user_public_keys',\n {\n id: id(),\n\n // User reference\n userId: foreignKey('user', () => users.id),\n\n // Key identification (client-generated UUID)\n keyId: text('key_id').notNull().unique(),\n\n // Public key in Base64-encoded DER format (SPKI)\n publicKey: text('public_key').notNull(),\n\n // Algorithm used (ES256 recommended, RS256 fallback)\n algorithm: text('algorithm', {\n enum: ['ES256', 'RS256']\n }).notNull().default('ES256'),\n\n // Key fingerprint (SHA-256 hash for quick identification)\n fingerprint: text('fingerprint').notNull(),\n\n // Key status\n isActive: boolean('is_active').notNull().default(true),\n\n // Timestamps\n createdAt: timestamp('created_at', { mode: 'date', withTimezone: true })\n .notNull()\n .defaultNow(),\n\n lastUsedAt: timestamp('last_used_at', { mode: 'date', withTimezone: true }),\n\n expiresAt: timestamp('expires_at', { mode: 'date', withTimezone: true }),\n\n // Revocation\n revokedAt: timestamp('revoked_at', { mode: 'date', withTimezone: true }),\n revokedReason: text('revoked_reason'),\n },\n (table) => [\n index('user_public_keys_user_id_idx').on(table.userId),\n index('user_public_keys_key_id_idx').on(table.keyId),\n index('user_public_keys_active_idx').on(table.isActive),\n index('user_public_keys_fingerprint_idx').on(table.fingerprint),\n ]\n);\n\nexport type UserPublicKey = typeof userPublicKeys.$inferSelect;\nexport type NewUserPublicKey = typeof userPublicKeys.$inferInsert;","/**\n * @spfn/auth - Verification Codes Entity\n *\n * Stores verification codes for email and phone verification\n * Codes expire after a configurable time period\n */\n\nimport { text, timestamp, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { authSchema } from './schema';\n\nexport const verificationCodes = authSchema.table('verification_codes',\n {\n id: id(),\n\n // Target (email or phone)\n target: text('target').notNull(), // Email address or E.164 phone number\n targetType: text(\n 'target_type',\n {\n enum: ['email', 'phone']\n }\n ).notNull(),\n\n // Code\n code: text('code').notNull(), // 6-digit code by default (configurable)\n\n // Purpose\n purpose: text(\n 'purpose',\n {\n enum: ['registration', 'login', 'password_reset', 'email_change', 'phone_change']\n }\n ).notNull(),\n\n // Expiry\n expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),\n\n // Usage tracking\n usedAt: timestamp('used_at', { withTimezone: true }),\n attempts: text('attempts').notNull().default('0'), // Track failed verification attempts\n\n ...timestamps(),\n },\n (table) => [\n // Index for quick lookup by target and purpose\n index('target_purpose_idx')\n .on(table.target, table.purpose, table.expiresAt),\n ]\n);\n\n// Type exports\nexport type VerificationCode = typeof verificationCodes.$inferSelect;\nexport type NewVerificationCode = typeof verificationCodes.$inferInsert;\nexport type VerificationTargetType = 'email' | 'phone';\nexport type VerificationPurpose = 'registration' | 'login' | 'password_reset' | 'email_change' | 'phone_change';","/**\n * @spfn/auth - User Invitations Entity\n *\n * Invitation system for invite-only user registration\n *\n * Features:\n * - Email-based invitations with unique tokens\n * - Role assignment at invitation time\n * - Expiration and status tracking\n * - Audit trail (who invited whom, when accepted)\n * - Metadata support for custom data\n */\n\nimport { text, timestamp, bigint, index, jsonb } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { roles } from './roles';\nimport { users } from './users';\nimport { authSchema } from './schema';\n\nexport const invitations = authSchema.table('user_invitations',\n {\n // Primary key\n id: id(),\n\n // Target email address for the invitation\n // Will become the user's email upon acceptance\n email: text('email').notNull(),\n\n // Unique invitation token (UUID v4)\n // Used in invitation URL: /auth/invite/{token}\n // Single-use token that expires after acceptance\n token: text('token').notNull().unique(),\n\n // Role to be assigned when invitation is accepted\n // Foreign key to roles table\n roleId: bigint('role_id', { mode: 'number' })\n .references(() => roles.id)\n .notNull(),\n\n // User who created this invitation\n // Foreign key to users table\n // Used for: audit trail, permission checks\n invitedBy: bigint('invited_by', { mode: 'number' })\n .references(() => users.id)\n .notNull(),\n\n // Invitation status\n // - pending: Invitation sent, awaiting acceptance\n // - accepted: User accepted and account created\n // - expired: Invitation expired (automatic)\n // - cancelled: Invitation cancelled by admin\n status: text(\n 'status',\n {\n enum: ['pending', 'accepted', 'expired', 'cancelled']\n }\n ).notNull().default('pending'),\n\n // Expiration timestamp (default: 7 days from creation)\n // Invitation cannot be accepted after this time\n // Background job should update status to 'expired'\n expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),\n\n // Timestamp when invitation was accepted\n // null = not yet accepted\n // Used for: audit trail, analytics\n acceptedAt: timestamp('accepted_at', { withTimezone: true }),\n\n // Timestamp when invitation was cancelled\n // null = not cancelled\n // Used for: audit trail\n cancelledAt: timestamp('cancelled_at', { withTimezone: true }),\n\n // Additional metadata (JSONB)\n // Use cases:\n // - Custom welcome message\n // - Onboarding instructions\n // - Team/department assignment\n // - Custom fields for app-specific data\n // Example: { message: \"Welcome!\", department: \"Engineering\" }\n metadata: jsonb('metadata'),\n\n ...timestamps(),\n },\n (table) => [\n // Indexes for query optimization\n index('invitations_token_idx').on(table.token),\n index('invitations_email_idx').on(table.email),\n index('invitations_status_idx').on(table.status),\n index('invitations_invited_by_idx').on(table.invitedBy),\n index('invitations_expires_at_idx').on(table.expiresAt), // For cleanup jobs\n index('invitations_role_id_idx').on(table.roleId),\n ]\n);\n\n// Type exports\nexport type Invitation = typeof invitations.$inferSelect;\nexport type NewInvitation = typeof invitations.$inferInsert;\nexport type InvitationStatus = 'pending' | 'accepted' | 'expired' | 'cancelled';\n\n// Helper type with joined data\nexport type InvitationWithDetails = Invitation &\n{\n role: {\n id: number;\n name: string;\n displayName: string;\n };\n inviter: {\n id: number;\n email: string | null;\n };\n};","/**\n * @spfn/auth - Permissions Entity\n *\n * Granular permissions for RBAC system\n *\n * Features:\n * - Built-in permissions (auth:*, user:*, rbac:*) - required for package\n * - System permissions (preset permissions) - optional\n * - Custom permissions (app-specific) - defined by developers\n * - Category grouping for organization\n */\n\nimport { text, boolean, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { authSchema } from './schema';\n\nexport const permissions = authSchema.table('permissions',\n {\n // Primary key\n id: id(),\n\n // Permission identifier (e.g., 'user:delete', 'post:publish')\n // Format: resource:action or namespace:resource:action\n // Must be unique\n name: text('name').notNull().unique(),\n\n // Display name for UI\n displayName: text('display_name').notNull(),\n\n // Permission description\n description: text('description'),\n\n // Category for grouping (e.g., 'user', 'post', 'admin', 'system')\n category: text('category'),\n\n // Built-in permission flag\n // true: Core package permissions - cannot be deleted\n // false: Custom or preset permissions\n isBuiltin: boolean('is_builtin').notNull().default(false),\n\n // System permission flag\n // true: Defined in code (builtin or preset)\n // false: Runtime created custom permission\n isSystem: boolean('is_system').notNull().default(false),\n\n // Active status\n // false: Deactivated permission (not enforced)\n isActive: boolean('is_active').notNull().default(true),\n\n ...timestamps(),\n },\n (table) => [\n index('permissions_name_idx').on(table.name),\n index('permissions_category_idx').on(table.category),\n index('permissions_is_system_idx').on(table.isSystem),\n index('permissions_is_active_idx').on(table.isActive),\n index('permissions_is_builtin_idx').on(table.isBuiltin),\n ]\n);\n\n// Type exports\nexport type PermissionEntity = typeof permissions.$inferSelect;\nexport type NewPermissionEntity = typeof permissions.$inferInsert;\n\n// Legacy alias for backward compatibility\nexport type Permission = PermissionEntity;\nexport type NewPermission = NewPermissionEntity;","/**\n * @spfn/auth - Role-Permissions Mapping Entity\n *\n * Many-to-many relationship between roles and permissions\n *\n * Usage:\n * - Defines which permissions each role has\n * - Cascade delete when role or permission is deleted\n */\n\nimport { bigint, index, unique } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { roles } from './roles';\nimport { permissions } from './permissions';\nimport { authSchema } from './schema';\n\nexport const rolePermissions = authSchema.table('role_permissions',\n {\n // Primary key\n id: id(),\n\n // Foreign key to roles table\n roleId: bigint('role_id', { mode: 'number' })\n .notNull()\n .references(() => roles.id, { onDelete: 'cascade' }),\n\n // Foreign key to permissions table\n permissionId: bigint('permission_id', { mode: 'number' })\n .notNull()\n .references(() => permissions.id, { onDelete: 'cascade' }),\n\n ...timestamps(),\n },\n (table) => [\n // Indexes for query performance\n index('role_permissions_role_id_idx').on(table.roleId),\n index('role_permissions_permission_id_idx').on(table.permissionId),\n\n // Unique constraint: one role-permission pair only\n unique('role_permissions_unique').on(table.roleId, table.permissionId),\n ]\n);\n\n// Type exports\nexport type RolePermission = typeof rolePermissions.$inferSelect;\nexport type NewRolePermission = typeof rolePermissions.$inferInsert;","/**\n * @spfn/auth - User-Permissions Override Entity\n *\n * Per-user permission grants/revocations\n *\n * Features:\n * - Grant additional permissions to specific users\n * - Revoke role-inherited permissions from specific users\n * - Temporary permissions with expiration\n * - Audit trail with reason field\n *\n * Priority:\n * User permissions override role permissions\n */\n\nimport { bigint, boolean, text, timestamp, index, unique } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { users } from './users';\nimport { permissions } from './permissions';\nimport { authSchema } from './schema';\n\nexport const userPermissions = authSchema.table('user_permissions',\n {\n // Primary key\n id: id(),\n\n // Foreign key to users table\n userId: bigint('user_id', { mode: 'number' })\n .notNull()\n .references(() => users.id, { onDelete: 'cascade' }),\n\n // Foreign key to permissions table\n permissionId: bigint('permission_id', { mode: 'number' })\n .notNull()\n .references(() => permissions.id, { onDelete: 'cascade' }),\n\n // Grant or revoke\n // true: Grant this permission (even if role doesn't have it)\n // false: Revoke this permission (even if role has it)\n granted: boolean('granted').notNull().default(true),\n\n // Reason for grant/revocation (audit trail)\n reason: text('reason'),\n\n // Expiration timestamp (optional)\n // null: Permanent override\n // timestamp: Permission expires at this time\n expiresAt: timestamp('expires_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Indexes for query performance\n index('user_permissions_user_id_idx').on(table.userId),\n index('user_permissions_permission_id_idx').on(table.permissionId),\n index('user_permissions_expires_at_idx').on(table.expiresAt),\n\n // Unique constraint: one user-permission pair only\n unique('user_permissions_unique').on(table.userId, table.permissionId),\n ]\n);\n\n// Type exports\nexport type UserPermission = typeof userPermissions.$inferSelect;\nexport type NewUserPermission = typeof userPermissions.$inferInsert;","/**\n * @spfn/auth - Database entities\n *\n * Core authentication and authorization entities\n */\n\n// Schema definition\nexport * from './schema';\n\n// User entities\nexport * from './users';\nexport * from './user-social-accounts';\nexport * from './user-public-keys';\nexport * from './verification-codes';\nexport * from './invitations';\n\n// RBAC entities\nexport * from './roles';\nexport * from './permissions';\nexport * from './role-permissions';\nexport * from './user-permissions';","/**\n * @spfn/auth - JWT Helpers\n *\n * JWT token generation and verification\n * Supports both server-signed (legacy) and client-signed (asymmetric) tokens\n *\n * Architecture:\n * - Legacy: Server signs/verifies with SPFN_AUTH_JWT_SECRET (symmetric HMAC)\n * - New: Client signs with privateKey, server verifies with publicKey (asymmetric)\n */\n\nimport jwt, { type SignOptions } from 'jsonwebtoken';\nimport crypto from 'crypto';\nimport type { SessionPayload } from '@/lib/types/api';\n\nconst JWT_SECRET =\n process.env.SPFN_AUTH_JWT_SECRET || // New prefixed version (recommended)\n process.env.JWT_SECRET || // Legacy fallback\n 'dev-secret-key-change-in-production';\n\nconst JWT_EXPIRES_IN =\n process.env.SPFN_AUTH_JWT_EXPIRES_IN || // New prefixed version (recommended)\n process.env.JWT_EXPIRES_IN || // Legacy fallback\n '7d';\n\nexport interface TokenPayload extends SessionPayload\n{\n exp?: number;\n iat?: number;\n iss?: string;\n keyId?: string;\n timestamp?: number;\n [key: string]: any;\n}\n\n/**\n * Generate a JWT token (legacy server-signed)\n *\n * @deprecated Use client-side signing with private keys instead\n * This method uses symmetric HMAC which requires sharing the secret\n *\n * @param payload - Token payload\n * @returns JWT token string\n */\nexport function generateToken(payload: SessionPayload): string\n{\n return jwt.sign(payload, JWT_SECRET, {\n expiresIn: JWT_EXPIRES_IN,\n } as SignOptions);\n}\n\n/**\n * Verify and decode a JWT token (legacy server-signed)\n *\n * @deprecated Use verifyClientToken for client-signed tokens\n * This method uses symmetric HMAC verification\n *\n * @param token - JWT token to verify\n * @returns Decoded payload\n * @throws Error if verification fails\n */\nexport function verifyToken(token: string): TokenPayload\n{\n return jwt.verify(token, JWT_SECRET) as TokenPayload;\n}\n\n/**\n * Verify client-signed JWT token with public key (DER format)\n *\n * Flow:\n * 1. Decode Base64 DER to Buffer\n * 2. Create KeyObject from DER\n * 3. Verify JWT signature with public key\n * 4. Validate issuer claim\n *\n * @param token - JWT token signed by client's private key\n * @param publicKeyB64 - Base64 encoded DER public key (SPKI format)\n * @param algorithm - Algorithm used for signing (ES256 or RS256)\n * @returns Decoded token payload\n * @throws Error if verification fails\n *\n * @example\n * ```typescript\n * const payload = verifyClientToken(\n * token,\n * 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...',\n * 'ES256'\n * );\n * ```\n */\nexport function verifyClientToken(\n token: string,\n publicKeyB64: string,\n algorithm: 'ES256' | 'RS256'\n): TokenPayload\n{\n try\n {\n // Convert Base64 DER to key object\n const publicKeyDER = Buffer.from(publicKeyB64, 'base64');\n const publicKeyObject = crypto.createPublicKey({\n key: publicKeyDER,\n format: 'der',\n type: 'spki',\n });\n\n const decoded = jwt.verify(token, publicKeyObject, {\n algorithms: [algorithm], // Prevent algorithm confusion attacks\n issuer: 'spfn-client', // Validate token issuer\n });\n\n // jwt.verify can return string, but we expect object payload\n if (typeof decoded === 'string')\n {\n throw new Error('Invalid token format: expected object payload');\n }\n\n return decoded as TokenPayload;\n }\n catch (error)\n {\n if (error instanceof jwt.TokenExpiredError)\n {\n throw new Error('Token has expired');\n }\n\n if (error instanceof jwt.JsonWebTokenError)\n {\n throw new Error('Invalid token signature');\n }\n\n throw new Error(`Token verification failed: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n\n/**\n * Decode a JWT token without verification (for debugging)\n *\n * WARNING: Does not verify signature! Only use for debugging/logging.\n * Never trust decoded data without verification.\n *\n * @param token - JWT token to decode\n * @returns Decoded payload or null if invalid\n */\nexport function decodeToken(token: string): TokenPayload | null\n{\n try\n {\n return jwt.decode(token) as TokenPayload | null;\n }\n catch\n {\n return null;\n }\n}\n\n/**\n * Verify public key fingerprint matches\n *\n * Used during registration/login to ensure the public key wasn't tampered with\n * during transmission.\n *\n * Security:\n * - Client sends: publicKey + fingerprint\n * - Server calculates: SHA-256(publicKey)\n * - Server compares: calculated === received\n *\n * @param publicKeyB64 - Base64 encoded DER public key\n * @param expectedFingerprint - SHA-256 hex fingerprint (64 chars)\n * @returns True if fingerprint matches\n *\n * @example\n * ```typescript\n * const isValid = verifyKeyFingerprint(\n * publicKey,\n * 'a1b2c3d4e5f6...' // 64-char hex string\n * );\n * if (!isValid) {\n * throw new Error('Public key fingerprint mismatch');\n * }\n * ```\n */\nexport function verifyKeyFingerprint(\n publicKeyB64: string,\n expectedFingerprint: string\n): boolean\n{\n try\n {\n const publicKeyDER = Buffer.from(publicKeyB64, 'base64');\n const fingerprint = crypto\n .createHash('sha256')\n .update(publicKeyDER)\n .digest('hex');\n\n return fingerprint === expectedFingerprint;\n }\n catch (error)\n {\n console.error('Failed to verify key fingerprint:', error);\n return false;\n }\n}","/**\n * @spfn/auth - Role Service\n *\n * Role management functions for runtime role creation and modification\n */\n\nimport { getDatabase } from '@spfn/core/db';\nimport { roles, permissions, rolePermissions } from '@/server/entities';\nimport type { Role } from '@/server/entities/roles';\nimport { eq, and } from 'drizzle-orm';\n\n/**\n * Create a new custom role\n *\n * @param data - Role configuration\n * @returns Created role\n * @throws Error if role name already exists\n *\n * @example\n * ```typescript\n * const role = await createRole({\n * name: 'content-creator',\n * displayName: 'Content Creator',\n * description: 'Can create and publish content',\n * priority: 20,\n * permissionIds: [1n, 2n, 3n],\n * });\n * ```\n */\nexport async function createRole(data: {\n name: string;\n displayName: string;\n description?: string;\n priority?: number;\n permissionIds?: number[];\n}): Promise<Role>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n // Check for duplicate name\n const existing = await db\n .select()\n .from(roles)\n .where(eq(roles.name, data.name))\n .limit(1);\n\n if (existing.length > 0)\n {\n throw new Error(`Role with name '${data.name}' already exists`);\n }\n\n // Create role\n const [newRole] = await db\n .insert(roles)\n .values({\n name: data.name,\n displayName: data.displayName,\n description: data.description,\n priority: data.priority ?? 10,\n isSystem: false, // Custom roles are never system roles\n isBuiltin: false,\n })\n .returning();\n\n // Assign permissions if provided\n if (data.permissionIds && data.permissionIds.length > 0)\n {\n const mappings = data.permissionIds.map(permId => ({\n roleId: newRole.id,\n permissionId: Number(permId),\n }));\n\n await db.insert(rolePermissions).values(mappings);\n }\n\n console.log(`[Auth] ✅ Created custom role: ${data.name}`);\n\n return newRole;\n}\n\n/**\n * Update an existing role\n *\n * @param roleId - Role ID\n * @param data - Update data\n * @returns Updated role\n * @throws Error if role is built-in (cannot modify)\n *\n * @example\n * ```typescript\n * await updateRole(1n, {\n * displayName: 'Senior Content Creator',\n * priority: 25,\n * });\n * ```\n */\nexport async function updateRole(\n roleId: number,\n data: {\n displayName?: string;\n description?: string;\n priority?: number;\n isActive?: boolean;\n }\n): Promise<Role>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const roleIdNum = Number(roleId);\n\n // Get role\n const [role] = await db\n .select()\n .from(roles)\n .where(eq(roles.id, roleIdNum))\n .limit(1);\n\n if (!role)\n {\n throw new Error('Role not found');\n }\n\n // Cannot modify built-in role priority\n if (role.isBuiltin && data.priority !== undefined)\n {\n throw new Error('Cannot modify priority of built-in roles');\n }\n\n // Update role\n const [updated] = await db\n .update(roles)\n .set(data)\n .where(eq(roles.id, roleIdNum))\n .returning();\n\n return updated;\n}\n\n/**\n * Delete a role\n *\n * @param roleId - Role ID\n * @throws Error if role is built-in or system role\n *\n * @example\n * ```typescript\n * await deleteRole(5n); // Delete custom role\n * ```\n */\nexport async function deleteRole(roleId: number): Promise<void>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const roleIdNum = Number(roleId);\n\n // Get role\n const [role] = await db\n .select()\n .from(roles)\n .where(eq(roles.id, roleIdNum))\n .limit(1);\n\n if (!role)\n {\n throw new Error('Role not found');\n }\n\n // Cannot delete built-in roles\n if (role.isBuiltin)\n {\n throw new Error(`Cannot delete built-in role: ${role.name}`);\n }\n\n // Cannot delete system roles (optional protection)\n if (role.isSystem)\n {\n throw new Error(`Cannot delete system role: ${role.name}. Deactivate it instead.`);\n }\n\n // Delete role (cascade will remove role_permissions)\n await db.delete(roles).where(eq(roles.id, roleIdNum));\n\n console.log(`[Auth] 🗑️ Deleted role: ${role.name}`);\n}\n\n/**\n * Add permission to role\n *\n * @param roleId - Role ID\n * @param permissionId - Permission ID\n *\n * @example\n * ```typescript\n * await addPermissionToRole(1n, 5n);\n * ```\n */\nexport async function addPermissionToRole(roleId: number, permissionId: number): Promise<void>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const roleIdNum = Number(roleId);\n const permissionIdNum = Number(permissionId);\n\n // Check if mapping already exists\n const existing = await db\n .select()\n .from(rolePermissions)\n .where(\n and(\n eq(rolePermissions.roleId, roleIdNum),\n eq(rolePermissions.permissionId, permissionIdNum)\n )\n )\n .limit(1);\n\n if (existing.length > 0)\n {\n return; // Already exists\n }\n\n // Create mapping\n await db.insert(rolePermissions).values({\n roleId: roleIdNum,\n permissionId: permissionIdNum,\n });\n}\n\n/**\n * Remove permission from role\n *\n * @param roleId - Role ID\n * @param permissionId - Permission ID\n *\n * @example\n * ```typescript\n * await removePermissionFromRole(1n, 5n);\n * ```\n */\nexport async function removePermissionFromRole(roleId: number, permissionId: number): Promise<void>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const roleIdNum = Number(roleId);\n const permissionIdNum = Number(permissionId);\n\n await db\n .delete(rolePermissions)\n .where(\n and(\n eq(rolePermissions.roleId, roleIdNum),\n eq(rolePermissions.permissionId, permissionIdNum)\n )\n );\n}\n\n/**\n * Set permissions for a role (replaces all existing permissions)\n *\n * @param roleId - Role ID\n * @param permissionIds - Array of permission IDs\n *\n * @example\n * ```typescript\n * await setRolePermissions(1n, [1n, 2n, 3n]);\n * ```\n */\nexport async function setRolePermissions(roleId: number, permissionIds: number[]): Promise<void>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const roleIdNum = Number(roleId);\n\n // Delete existing mappings\n await db.delete(rolePermissions).where(eq(rolePermissions.roleId, roleIdNum));\n\n // Create new mappings\n if (permissionIds.length > 0)\n {\n const mappings = permissionIds.map(permId => ({\n roleId: roleIdNum,\n permissionId: Number(permId),\n }));\n\n await db.insert(rolePermissions).values(mappings);\n }\n}\n\n/**\n * Get all roles\n *\n * @param includeInactive - Include inactive roles\n * @returns Array of roles\n *\n * @example\n * ```typescript\n * const roles = await getAllRoles();\n * ```\n */\nexport async function getAllRoles(includeInactive = false): Promise<Role[]>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const query = db.select().from(roles);\n\n if (!includeInactive)\n {\n return query.where(eq(roles.isActive, true));\n }\n\n return query;\n}\n\n/**\n * Get role by name\n *\n * @param name - Role name\n * @returns Role or null\n *\n * @example\n * ```typescript\n * const role = await getRoleByName('admin');\n * ```\n */\nexport async function getRoleByName(name: string): Promise<Role | null>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const [role] = await db\n .select()\n .from(roles)\n .where(eq(roles.name, name))\n .limit(1);\n\n return role || null;\n}\n\n/**\n * Get role permissions\n *\n * @param roleId - Role ID\n * @returns Array of permission names\n *\n * @example\n * ```typescript\n * const perms = await getRolePermissions(1n);\n * // ['user:read', 'user:write']\n * ```\n */\nexport async function getRolePermissions(roleId: number): Promise<string[]>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const roleIdNum = Number(roleId);\n\n const perms = await db\n .select({ name: permissions.name })\n .from(rolePermissions)\n .innerJoin(permissions, eq(rolePermissions.permissionId, permissions.id))\n .where(eq(rolePermissions.roleId, roleIdNum));\n\n return perms.map(p => p.name);\n}","/**\n * @spfn/auth - Built-in Roles and Permissions\n *\n * Core roles and permissions required by the auth package\n * These cannot be deleted and are automatically created on initialization\n */\n\nimport type { RoleConfig, PermissionConfig } from './types';\n\n/**\n * Built-in roles (required by package)\n * These roles are always created and cannot be deleted\n */\nexport const BUILTIN_ROLES: Record<string, RoleConfig> = {\n SUPERADMIN: {\n name: 'superadmin',\n displayName: 'Super Administrator',\n description: 'Full system access and RBAC management',\n priority: 100,\n isSystem: true,\n isBuiltin: true,\n },\n ADMIN: {\n name: 'admin',\n displayName: 'Administrator',\n description: 'User management and organization administration',\n priority: 80,\n isSystem: true,\n isBuiltin: true,\n },\n USER: {\n name: 'user',\n displayName: 'User',\n description: 'Default user role with basic permissions',\n priority: 10,\n isSystem: true,\n isBuiltin: true,\n },\n} as const;\n\n/**\n * Built-in permissions (required by package)\n * These permissions are always created and cannot be deleted\n */\nexport const BUILTIN_PERMISSIONS: Record<string, PermissionConfig> = {\n // Self-service auth management\n AUTH_SELF_MANAGE: {\n name: 'auth:self:manage',\n displayName: 'Manage Own Auth',\n description: 'Change own password, rotate keys, manage own sessions',\n category: 'auth',\n isSystem: true,\n isBuiltin: true,\n },\n\n // User management (admin functions)\n USER_READ: {\n name: 'user:read',\n displayName: 'Read Users',\n description: 'View user information and list users',\n category: 'user',\n isSystem: true,\n isBuiltin: true,\n },\n USER_WRITE: {\n name: 'user:write',\n displayName: 'Write Users',\n description: 'Create and update user accounts',\n category: 'user',\n isSystem: true,\n isBuiltin: true,\n },\n USER_DELETE: {\n name: 'user:delete',\n displayName: 'Delete Users',\n description: 'Delete user accounts',\n category: 'user',\n isSystem: true,\n isBuiltin: true,\n },\n USER_INVITE: {\n name: 'user:invite',\n displayName: 'Invite Users',\n description: 'Create and send user invitations',\n category: 'user',\n isSystem: true,\n isBuiltin: true,\n },\n\n // RBAC management (superadmin functions)\n RBAC_ROLE_MANAGE: {\n name: 'rbac:role:manage',\n displayName: 'Manage Roles',\n description: 'Create, update, and delete roles',\n category: 'rbac',\n isSystem: true,\n isBuiltin: true,\n },\n RBAC_PERMISSION_MANAGE: {\n name: 'rbac:permission:manage',\n displayName: 'Manage Permissions',\n description: 'Assign permissions to roles and users',\n category: 'rbac',\n isSystem: true,\n isBuiltin: true,\n },\n} as const;\n\n/**\n * Built-in role-permission mappings\n * Defines default permissions for each built-in role\n */\nexport const BUILTIN_ROLE_PERMISSIONS: Record<string, string[]> = {\n superadmin: [\n 'auth:self:manage',\n 'user:read',\n 'user:write',\n 'user:delete',\n 'user:invite',\n 'rbac:role:manage',\n 'rbac:permission:manage',\n ],\n admin: [\n 'auth:self:manage',\n 'user:read',\n 'user:write',\n 'user:delete',\n 'user:invite',\n ],\n user: [\n 'auth:self:manage',\n ],\n} as const;\n\nexport type BuiltinRoleName = keyof typeof BUILTIN_ROLE_PERMISSIONS;\nexport type BuiltinPermissionName = typeof BUILTIN_PERMISSIONS[keyof typeof BUILTIN_PERMISSIONS]['name'];","/**\n * @spfn/auth - Auth Service\n *\n * Core authentication logic: registration, login, logout, password management\n */\n\nimport { findOne, create } from '@spfn/core/db';\nimport { ValidationError } from '@spfn/core/errors';\nimport { users, type User } from '@/server/entities';\nimport { hashPassword, verifyPassword } from '@/server/helpers';\nimport { validateVerificationToken } from '@/server/helpers/verification';\nimport {\n InvalidCredentialsError,\n AccountDisabledError,\n AccountAlreadyExistsError,\n InvalidVerificationTokenError,\n VerificationTokenPurposeMismatchError,\n VerificationTokenTargetMismatchError,\n} from '@/server/errors';\nimport { registerPublicKeyService, revokeKeyService } from './key.service';\nimport { updateLastLoginService } from './user.service';\n\nexport interface CheckAccountExistsParams\n{\n email?: string;\n phone?: string;\n}\n\nexport interface CheckAccountExistsResult\n{\n exists: boolean;\n identifier: string;\n identifierType: 'email' | 'phone';\n}\n\nexport interface RegisterParams\n{\n email?: string;\n phone?: string;\n verificationToken: string;\n password: string;\n publicKey: string;\n keyId: string;\n fingerprint: string;\n algorithm?: 'ES256' | 'RS256';\n}\n\nexport interface RegisterResult\n{\n userId: string;\n email?: string;\n phone?: string;\n}\n\nexport interface LoginParams\n{\n email?: string;\n phone?: string;\n password: string;\n publicKey: string;\n keyId: string;\n fingerprint: string;\n oldKeyId?: string;\n algorithm?: 'ES256' | 'RS256';\n}\n\nexport interface LoginResult\n{\n userId: string;\n email?: string;\n phone?: string;\n passwordChangeRequired: boolean;\n}\n\nexport interface LogoutParams\n{\n userId: number;\n keyId: string;\n}\n\nexport interface ChangePasswordParams\n{\n userId: number;\n currentPassword: string;\n newPassword: string;\n passwordHash?: string; // Optional: pass user's password hash to avoid re-fetch\n}\n\n/**\n * Check if an account exists by email or phone\n */\nexport async function checkAccountExistsService(\n params: CheckAccountExistsParams\n): Promise<CheckAccountExistsResult>\n{\n const { email, phone } = params;\n\n let identifier: string;\n let identifierType: 'email' | 'phone';\n let user: User | null;\n\n if (email)\n {\n identifier = email;\n identifierType = 'email';\n user = await findOne(users, { email });\n }\n else if (phone)\n {\n identifier = phone;\n identifierType = 'phone';\n user = await findOne(users, { phone });\n }\n else\n {\n throw new ValidationError('Either email or phone must be provided');\n }\n\n return {\n exists: !!user,\n identifier,\n identifierType,\n };\n}\n\n/**\n * Register a new user account\n */\nexport async function registerService(\n params: RegisterParams\n): Promise<RegisterResult>\n{\n const { email, phone, verificationToken, password, publicKey, keyId, fingerprint, algorithm } = params;\n\n // Validate verification token\n const tokenPayload = validateVerificationToken(verificationToken);\n if (!tokenPayload)\n {\n throw new InvalidVerificationTokenError();\n }\n\n // Verify that token purpose is registration\n if (tokenPayload.purpose !== 'registration')\n {\n throw new VerificationTokenPurposeMismatchError('registration', tokenPayload.purpose);\n }\n\n // Verify that token target matches provided email/phone\n const providedTarget = email || phone;\n if (tokenPayload.target !== providedTarget)\n {\n throw new VerificationTokenTargetMismatchError();\n }\n\n // Verify that token targetType matches\n const providedTargetType = email ? 'email' : 'phone';\n if (tokenPayload.targetType !== providedTargetType)\n {\n throw new VerificationTokenTargetMismatchError();\n }\n\n // Check if user already exists\n let existingUser: User | null;\n if (email)\n {\n existingUser = await findOne(users, { email });\n }\n else if (phone)\n {\n existingUser = await findOne(users, { phone });\n }\n else\n {\n throw new ValidationError('Either email or phone must be provided');\n }\n\n if (existingUser)\n {\n const identifierType = email ? 'email' : 'phone';\n throw new AccountAlreadyExistsError(email || phone!, identifierType);\n }\n\n // Hash password\n const passwordHash = await hashPassword(password);\n\n // Get default user role\n const { getRoleByName } = await import('./role.service');\n const userRole = await getRoleByName('user');\n\n if (!userRole)\n {\n throw new Error('Default user role not found. Run initializeAuth() first.');\n }\n\n // Create user\n const newUser = await create(users, {\n email: email || null,\n phone: phone || null,\n passwordHash,\n passwordChangeRequired: false,\n roleId: userRole.id,\n status: 'active',\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n\n // Register public key\n await registerPublicKeyService({\n userId: newUser.id,\n keyId,\n publicKey,\n fingerprint,\n algorithm,\n });\n\n return {\n userId: String(newUser.id),\n email: newUser.email || undefined,\n phone: newUser.phone || undefined,\n };\n}\n\n/**\n * Authenticate user and create session\n */\nexport async function loginService(\n params: LoginParams\n): Promise<LoginResult>\n{\n const { email, phone, password, publicKey, keyId, fingerprint, oldKeyId, algorithm } = params;\n\n // Find user\n let user: User | null;\n if (email)\n {\n user = await findOne(users, { email });\n }\n else if (phone)\n {\n user = await findOne(users, { phone });\n }\n else\n {\n throw new ValidationError('Either email or phone must be provided');\n }\n\n if (!user || !user.passwordHash)\n {\n throw new InvalidCredentialsError();\n }\n\n // Verify password\n const isValid = await verifyPassword(password, user.passwordHash);\n if (!isValid)\n {\n throw new InvalidCredentialsError();\n }\n\n // Check if user is active\n if (user.status !== 'active')\n {\n throw new AccountDisabledError(user.status);\n }\n\n // Revoke old key if provided\n if (oldKeyId)\n {\n await revokeKeyService({\n userId: user.id,\n keyId: oldKeyId,\n reason: 'Replaced by new key on login',\n });\n }\n\n // Register new public key\n await registerPublicKeyService({\n userId: user.id,\n keyId,\n publicKey,\n fingerprint,\n algorithm,\n });\n\n // Update last login\n await updateLastLoginService(user.id);\n\n return {\n userId: String(user.id),\n email: user.email || undefined,\n phone: user.phone || undefined,\n passwordChangeRequired: user.passwordChangeRequired,\n };\n}\n\n/**\n * Logout user (revoke current key)\n */\nexport async function logoutService(\n params: LogoutParams\n): Promise<void>\n{\n const { userId, keyId } = params;\n\n await revokeKeyService({\n userId,\n keyId,\n reason: 'Revoked by logout',\n });\n}\n\n/**\n * Change user password\n */\nexport async function changePasswordService(\n params: ChangePasswordParams\n): Promise<void>\n{\n const { userId, currentPassword, newPassword, passwordHash: providedHash } = params;\n\n // Get user's password hash (either provided or fetch from DB)\n let passwordHash: string | null;\n if (providedHash)\n {\n passwordHash = providedHash;\n }\n else\n {\n const user = await findOne(users, { id: userId });\n if (!user)\n {\n throw new ValidationError('User not found');\n }\n passwordHash = user.passwordHash;\n }\n\n // Verify current password\n if (!passwordHash)\n {\n throw new ValidationError('No password set for this account');\n }\n\n const isValid = await verifyPassword(currentPassword, passwordHash);\n if (!isValid)\n {\n throw new InvalidCredentialsError('Current password is incorrect');\n }\n\n // Hash new password\n const newPasswordHash = await hashPassword(newPassword);\n\n // Update password and clear passwordChangeRequired flag\n const { updateOne } = await import('@spfn/core/db');\n await updateOne(users, { id: userId }, {\n passwordHash: newPasswordHash,\n passwordChangeRequired: false,\n updatedAt: new Date(),\n });\n}","/**\n * @spfn/auth - Password Helpers\n *\n * Password hashing and verification using bcrypt\n *\n * Security:\n * - Adaptive hashing (configurable rounds)\n * - Automatic salt generation (per-password)\n * - Constant-time comparison (timing attack protection)\n * - Rainbow table protection\n */\n\nimport bcrypt from 'bcrypt';\n\n/**\n * Bcrypt salt rounds (cost factor)\n *\n * Determines computational cost: 2^rounds iterations\n * - 10 rounds: ~100ms (default, balanced)\n * - 12 rounds: ~400ms (more secure, slower)\n * - 14 rounds: ~1600ms (very secure, too slow for most apps)\n *\n * Can be configured via SPFN_AUTH_BCRYPT_SALT_ROUNDS environment variable\n */\nconst SALT_ROUNDS = parseInt(\n process.env.SPFN_AUTH_BCRYPT_SALT_ROUNDS || // New prefixed version (recommended)\n process.env.BCRYPT_SALT_ROUNDS || // Legacy fallback\n '10',\n 10\n);\n\n/**\n * Hash a plain text password using bcrypt\n *\n * Algorithm:\n * 1. Generate random salt (128-bit)\n * 2. Apply bcrypt key derivation (2^rounds iterations)\n * 3. Return $2b$rounds$[salt][hash] (60 chars)\n *\n * @param password - Plain text password to hash\n * @returns Bcrypt hash string (includes salt)\n * @throws Error if password is empty or invalid\n *\n * @example\n * ```typescript\n * const hash = await hashPassword('mySecurePassword123');\n * // Returns: \"$2b$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy\"\n * ```\n */\nexport async function hashPassword(password: string): Promise<string>\n{\n if (!password || password.length === 0)\n {\n throw new Error('Password cannot be empty');\n }\n\n return bcrypt.hash(password, SALT_ROUNDS);\n}\n\n/**\n * Verify a password against a bcrypt hash\n *\n * Uses constant-time comparison to prevent timing attacks\n * Automatically extracts salt from hash for verification\n *\n * @param password - Plain text password to verify\n * @param hash - Bcrypt hash string (from hashPassword)\n * @returns True if password matches hash\n * @throws Error if inputs are invalid\n *\n * @example\n * ```typescript\n * const isValid = await verifyPassword(\n * 'mySecurePassword123',\n * '$2b$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy'\n * );\n * // Returns: true\n * ```\n */\nexport async function verifyPassword(password: string, hash: string): Promise<boolean>\n{\n if (!password || password.length === 0)\n {\n throw new Error('Password cannot be empty');\n }\n\n if (!hash || hash.length === 0)\n {\n throw new Error('Hash cannot be empty');\n }\n\n return bcrypt.compare(password, hash);\n}\n\n/**\n * Validate password strength\n *\n * Requirements:\n * - Minimum 8 characters\n * - At least one uppercase letter\n * - At least one lowercase letter\n * - At least one number\n * - At least one special character\n *\n * @param password - Password to validate\n * @returns Validation result with error messages\n *\n * @example\n * ```typescript\n * const result = validatePasswordStrength('weak');\n * // Returns: { valid: false, errors: ['Too short', 'Missing uppercase', ...] }\n *\n * const result = validatePasswordStrength('SecurePass123!');\n * // Returns: { valid: true, errors: [] }\n * ```\n */\nexport function validatePasswordStrength(password: string): {\n valid: boolean;\n errors: string[];\n}\n{\n const errors: string[] = [];\n\n if (password.length < 8)\n {\n errors.push('Password must be at least 8 characters');\n }\n\n if (!/[A-Z]/.test(password))\n {\n errors.push('Password must contain at least one uppercase letter');\n }\n\n if (!/[a-z]/.test(password))\n {\n errors.push('Password must contain at least one lowercase letter');\n }\n\n if (!/[0-9]/.test(password))\n {\n errors.push('Password must contain at least one number');\n }\n\n if (!/[^A-Za-z0-9]/.test(password))\n {\n errors.push('Password must contain at least one special character');\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}","/**\n * @spfn/auth - Helper functions\n */\n\nexport * from './password';\nexport * from './jwt';\nexport * from './verification';\nexport * from './context';","/**\n * @spfn/auth - Verification Code Helpers\n *\n * Helper functions for email/phone verification codes\n */\n\nimport jwt from 'jsonwebtoken';\nimport { getDatabase, create } from '@spfn/core/db';\nimport { verificationCodes } from '@/server/entities/verification-codes';\nimport { eq, and } from 'drizzle-orm';\n\n/**\n * JWT secret for verification tokens\n * Must be at least 32 characters long\n */\nfunction getVerificationTokenSecret(): string\n{\n const secret =\n process.env.SPFN_AUTH_VERIFICATION_TOKEN_SECRET || // New prefixed version (recommended)\n process.env.VERIFICATION_TOKEN_SECRET || // Legacy fallback\n process.env.SPFN_AUTH_JWT_SECRET || // New JWT secret fallback\n process.env.JWT_SECRET; // Legacy JWT secret fallback\n\n if (!secret || secret.length < 32)\n {\n throw new Error('SPFN_AUTH_VERIFICATION_TOKEN_SECRET must be at least 32 characters long');\n }\n\n return secret;\n}\n\n/**\n * Verification token expiry (15 minutes)\n */\nconst VERIFICATION_TOKEN_EXPIRY = '15m';\n\n/**\n * Verification code expiry (5 minutes)\n */\nconst VERIFICATION_CODE_EXPIRY_MINUTES = 5;\n\n/**\n * Maximum verification attempts before code expires\n */\nconst MAX_VERIFICATION_ATTEMPTS = 5;\n\n/**\n * Verification token payload\n */\nexport interface VerificationTokenPayload\n{\n target: string;\n targetType: 'email' | 'phone';\n purpose: 'registration' | 'login' | 'password_reset';\n codeId: number;\n}\n\n/**\n * Generate a random 6-digit verification code\n *\n * @returns 6-digit code as string\n */\nexport function generateVerificationCode(): string\n{\n // Generate random 6-digit number (000000 - 999999)\n const code = Math.floor(Math.random() * 1000000)\n .toString()\n .padStart(6, '0');\n\n return code;\n}\n\n/**\n * Store verification code in database\n *\n * @param target - Email or phone number\n * @param targetType - Type of target (email or phone)\n * @param code - 6-digit verification code\n * @param purpose - Purpose of verification\n * @returns Created verification code record\n */\nexport async function storeVerificationCode(\n target: string,\n targetType: 'email' | 'phone',\n code: string,\n purpose: 'registration' | 'login' | 'password_reset'\n)\n{\n const db = getDatabase();\n if (!db)\n {\n throw new Error('Database not initialized');\n }\n\n // Calculate expiry time\n const expiresAt = new Date();\n expiresAt.setMinutes(expiresAt.getMinutes() + VERIFICATION_CODE_EXPIRY_MINUTES);\n\n // Create verification code record\n const record = await create(verificationCodes, {\n target,\n targetType,\n code,\n purpose,\n expiresAt,\n attempts: '0',\n });\n\n return record;\n}\n\n/**\n * Validate verification code\n *\n * @param target - Email or phone number\n * @param targetType - Type of target\n * @param code - 6-digit code to validate\n * @param purpose - Purpose of verification\n * @returns Validation result with code ID if valid\n */\nexport async function validateVerificationCode(\n target: string,\n targetType: 'email' | 'phone',\n code: string,\n purpose: 'registration' | 'login' | 'password_reset'\n): Promise<{ valid: boolean; codeId?: number; error?: string }>\n{\n const db = getDatabase();\n if (!db)\n {\n throw new Error('Database not initialized');\n }\n\n // Find the verification code\n const records = await db\n .select()\n .from(verificationCodes)\n .where(\n and(\n eq(verificationCodes.target, target),\n eq(verificationCodes.targetType, targetType),\n eq(verificationCodes.code, code),\n eq(verificationCodes.purpose, purpose)\n )\n )\n .limit(1);\n\n if (records.length === 0)\n {\n return { valid: false, error: 'Invalid verification code' };\n }\n\n const record = records[0];\n\n // Check if code is already used\n if (record.usedAt)\n {\n return { valid: false, error: 'Verification code already used' };\n }\n\n // Check if code is expired\n if (new Date() > new Date(record.expiresAt))\n {\n return { valid: false, error: 'Verification code expired' };\n }\n\n // Check attempt count\n const attempts = parseInt(record.attempts, 10);\n if (attempts >= MAX_VERIFICATION_ATTEMPTS)\n {\n return { valid: false, error: 'Too many attempts, please request a new code' };\n }\n\n // Increment attempt count\n await db\n .update(verificationCodes)\n .set({ attempts: (attempts + 1).toString() })\n .where(eq(verificationCodes.id, record.id));\n\n return { valid: true, codeId: record.id };\n}\n\n/**\n * Mark verification code as used\n *\n * @param codeId - Verification code ID\n */\nexport async function markCodeAsUsed(codeId: number): Promise<void>\n{\n const db = getDatabase();\n if (!db)\n {\n throw new Error('Database not initialized');\n }\n\n await db\n .update(verificationCodes)\n .set({ usedAt: new Date() })\n .where(eq(verificationCodes.id, codeId));\n}\n\n/**\n * Create verification token (JWT)\n *\n * @param payload - Token payload\n * @returns Signed JWT token\n */\nexport function createVerificationToken(payload: VerificationTokenPayload): string\n{\n const secret = getVerificationTokenSecret();\n return jwt.sign(payload, secret, {\n expiresIn: VERIFICATION_TOKEN_EXPIRY,\n issuer: 'spfn-auth',\n audience: 'spfn-client',\n });\n}\n\n/**\n * Validate verification token (JWT)\n *\n * @param token - JWT token to validate\n * @returns Decoded payload if valid, null otherwise\n */\nexport function validateVerificationToken(token: string): VerificationTokenPayload | null\n{\n try\n {\n const secret = getVerificationTokenSecret();\n const decoded = jwt.verify(token, secret, {\n issuer: 'spfn-auth',\n audience: 'spfn-client',\n });\n\n // Validate that decoded has required properties\n if (\n typeof decoded === 'object' &&\n decoded !== null &&\n 'target' in decoded &&\n 'targetType' in decoded &&\n 'purpose' in decoded &&\n 'codeId' in decoded\n )\n {\n return decoded as VerificationTokenPayload;\n }\n\n return null;\n }\n catch (error)\n {\n console.error('[validateVerificationToken] Error:', error);\n return null;\n }\n}\n\n/**\n * Send verification code via email\n *\n * @param email - Email address\n * @param code - 6-digit verification code\n * @param purpose - Purpose of verification\n */\nexport async function sendVerificationEmail(\n email: string,\n code: string,\n purpose: string\n): Promise<void>\n{\n // TODO: Implement email sending with your email service\n // For now, just log to console (development only)\n console.log(`[VERIFICATION EMAIL] To: ${email}, Code: ${code}, Purpose: ${purpose}`);\n\n // Example implementation with nodemailer:\n // const transporter = nodemailer.createTransport({...});\n // await transporter.sendMail({\n // from: 'noreply@yourapp.com',\n // to: email,\n // subject: 'Your Verification Code',\n // text: `Your verification code is: ${code}`,\n // html: `<p>Your verification code is: <strong>${code}</strong></p>`,\n // });\n}\n\n/**\n * Send verification code via SMS\n *\n * @param phone - Phone number in E.164 format\n * @param code - 6-digit verification code\n * @param purpose - Purpose of verification\n */\nexport async function sendVerificationSMS(\n phone: string,\n code: string,\n purpose: string\n): Promise<void>\n{\n // TODO: Implement SMS sending with your SMS service (Twilio, AWS SNS, etc.)\n // For now, just log to console (development only)\n console.log(`[VERIFICATION SMS] To: ${phone}, Code: ${code}, Purpose: ${purpose}`);\n\n // Example implementation with Twilio:\n // const client = twilio(accountSid, authToken);\n // await client.messages.create({\n // body: `Your verification code is: ${code}`,\n // from: '+1234567890',\n // to: phone,\n // });\n}","/**\n * Auth Context Helpers\n *\n * Helper functions to access authenticated user data from route context\n */\n\nimport type { Context } from 'hono';\nimport type { AuthContext } from '../middleware/authenticate.js';\n\n/**\n * Get auth context from route context\n *\n * Accepts both raw Hono Context and RouteContext with raw property\n *\n * @example\n * ```typescript\n * // With RouteContext (RPC routes)\n * app.bind(logoutContract, [authenticate], async (c) => {\n * const { user, userId, keyId } = getAuth(c);\n * // Use authenticated data...\n * });\n *\n * // With raw Context (middleware)\n * const auth = getAuth(c);\n * ```\n */\nexport function getAuth(c: Context | { raw: Context }): AuthContext\n{\n // Check if it's RouteContext with raw property\n if ('raw' in c && c.raw)\n {\n return c.raw.get('auth');\n }\n\n // Otherwise, it's raw Hono Context\n return (c as Context).get('auth');\n}\n\n/**\n * Get authenticated user from route context\n *\n * @example\n * ```typescript\n * app.bind(profileContract, [authenticate], async (c) => {\n * const user = getUser(c);\n * return c.success({ email: user.email });\n * });\n * ```\n */\nexport function getUser(c: Context | { raw: Context })\n{\n return getAuth(c).user;\n}\n\n/**\n * Get authenticated user ID from route context\n *\n * @example\n * ```typescript\n * app.bind(postsContract, [authenticate], async (c) => {\n * const userId = getUserId(c);\n * const posts = await findPosts({ authorId: userId });\n * });\n * ```\n */\nexport function getUserId(c: Context | { raw: Context }): string\n{\n return getAuth(c).userId;\n}\n\n/**\n * Get current key ID from route context\n *\n * @example\n * ```typescript\n * app.bind(rotateKeyContract, [authenticate], async (c) => {\n * const oldKeyId = getKeyId(c);\n * // Revoke old key...\n * });\n * ```\n */\nexport function getKeyId(c: Context | { raw: Context }): string\n{\n return getAuth(c).keyId;\n}","/**\n * Authentication & Authorization Error Classes\n *\n * Custom error classes for auth-specific scenarios\n */\n\nimport {\n ValidationError,\n UnauthorizedError,\n ForbiddenError,\n ConflictError\n} from '@spfn/core/errors';\n\n/**\n * Invalid Credentials Error (401)\n *\n * Thrown when login credentials are incorrect\n */\nexport class InvalidCredentialsError extends UnauthorizedError\n{\n constructor(message: string = 'Invalid credentials')\n {\n super(message);\n this.name = 'InvalidCredentialsError';\n }\n}\n\n/**\n * Invalid Token Error (401)\n *\n * Thrown when authentication token is invalid or malformed\n */\nexport class InvalidTokenError extends UnauthorizedError\n{\n constructor(message: string = 'Invalid authentication token')\n {\n super(message);\n this.name = 'InvalidTokenError';\n }\n}\n\n/**\n * Token Expired Error (401)\n *\n * Thrown when authentication token has expired\n */\nexport class TokenExpiredError extends UnauthorizedError\n{\n constructor(message: string = 'Authentication token has expired')\n {\n super(message);\n this.name = 'TokenExpiredError';\n }\n}\n\n/**\n * Key Expired Error (401)\n *\n * Thrown when public key has expired\n */\nexport class KeyExpiredError extends UnauthorizedError\n{\n constructor(message: string = 'Public key has expired')\n {\n super(message);\n this.name = 'KeyExpiredError';\n }\n}\n\n/**\n * Account Disabled Error (403)\n *\n * Thrown when user account is disabled or inactive\n */\nexport class AccountDisabledError extends ForbiddenError\n{\n constructor(status: string = 'disabled')\n {\n super(`Account is ${status}`, { details: { status } });\n this.name = 'AccountDisabledError';\n }\n}\n\n/**\n * Account Already Exists Error (409)\n *\n * Thrown when trying to register with existing email/phone\n */\nexport class AccountAlreadyExistsError extends ConflictError\n{\n constructor(identifier: string, identifierType: 'email' | 'phone')\n {\n super('Account already exists', { details: { identifier, identifierType } });\n this.name = 'AccountAlreadyExistsError';\n }\n}\n\n/**\n * Invalid Verification Code Error (400)\n *\n * Thrown when verification code is invalid, expired, or already used\n */\nexport class InvalidVerificationCodeError extends ValidationError\n{\n constructor(reason: string = 'Invalid verification code')\n {\n super(reason);\n this.name = 'InvalidVerificationCodeError';\n }\n}\n\n/**\n * Invalid Verification Token Error (400)\n *\n * Thrown when verification token is invalid or expired\n */\nexport class InvalidVerificationTokenError extends ValidationError\n{\n constructor(message: string = 'Invalid or expired verification token')\n {\n super(message);\n this.name = 'InvalidVerificationTokenError';\n }\n}\n\n/**\n * Invalid Key Fingerprint Error (400)\n *\n * Thrown when public key fingerprint doesn't match the public key\n */\nexport class InvalidKeyFingerprintError extends ValidationError\n{\n constructor(message: string = 'Invalid key fingerprint')\n {\n super(message);\n this.name = 'InvalidKeyFingerprintError';\n }\n}\n\n/**\n * Verification Token Purpose Mismatch Error (400)\n *\n * Thrown when verification token purpose doesn't match expected purpose\n */\nexport class VerificationTokenPurposeMismatchError extends ValidationError\n{\n constructor(expected: string, actual: string)\n {\n super(`Verification token is for ${actual}, but ${expected} was expected`, { details: { expected, actual } });\n this.name = 'VerificationTokenPurposeMismatchError';\n }\n}\n\n/**\n * Verification Token Target Mismatch Error (400)\n *\n * Thrown when verification token target doesn't match provided email/phone\n */\nexport class VerificationTokenTargetMismatchError extends ValidationError\n{\n constructor()\n {\n super('Verification token does not match provided email/phone');\n this.name = 'VerificationTokenTargetMismatchError';\n }\n}","/**\n * @spfn/auth - Key Service\n *\n * Handles public key registration, rotation, and revocation\n */\n\nimport { create, getDatabase } from '@spfn/core/db';\nimport { userPublicKeys } from '@/server/entities';\nimport { verifyKeyFingerprint } from '@/server/helpers/jwt';\nimport { InvalidKeyFingerprintError } from '@/server/errors';\nimport { eq, and } from 'drizzle-orm';\n\nexport interface RegisterPublicKeyParams\n{\n userId: number;\n keyId: string;\n publicKey: string;\n fingerprint: string;\n algorithm?: 'ES256' | 'RS256';\n}\n\nexport interface RotateKeyParams\n{\n userId: number;\n oldKeyId: string;\n newKeyId: string;\n newPublicKey: string;\n fingerprint: string;\n algorithm?: 'ES256' | 'RS256';\n}\n\nexport interface RotateKeyResult\n{\n success: boolean;\n keyId: string;\n}\n\nexport interface RevokeKeyParams\n{\n userId: number;\n keyId: string;\n reason: string;\n}\n\n/**\n * Helper: Calculate key expiry date (90 days from now)\n */\nfunction getKeyExpiryDate(): Date\n{\n const expiresAt = new Date();\n expiresAt.setDate(expiresAt.getDate() + 90);\n return expiresAt;\n}\n\n/**\n * Register a new public key for a user\n */\nexport async function registerPublicKeyService(\n params: RegisterPublicKeyParams\n): Promise<void>\n{\n const { userId, keyId, publicKey, fingerprint, algorithm = 'ES256' } = params;\n\n // Verify fingerprint matches public key\n const isValidFingerprint = verifyKeyFingerprint(publicKey, fingerprint);\n if (!isValidFingerprint)\n {\n throw new InvalidKeyFingerprintError();\n }\n\n // Store public key (90 days expiry)\n await create(userPublicKeys, {\n userId,\n keyId,\n publicKey,\n algorithm,\n fingerprint,\n isActive: true,\n createdAt: new Date(),\n expiresAt: getKeyExpiryDate(),\n });\n}\n\n/**\n * Rotate user's public key (revoke old, register new)\n */\nexport async function rotateKeyService(\n params: RotateKeyParams\n): Promise<RotateKeyResult>\n{\n const { userId, oldKeyId, newKeyId, newPublicKey, fingerprint, algorithm = 'ES256' } = params;\n\n // Verify fingerprint matches public key\n const isValidFingerprint = verifyKeyFingerprint(newPublicKey, fingerprint);\n if (!isValidFingerprint)\n {\n throw new InvalidKeyFingerprintError();\n }\n\n const db = getDatabase()!;\n\n // Revoke old key\n await db\n .update(userPublicKeys)\n .set({\n isActive: false,\n revokedAt: new Date(),\n revokedReason: 'Replaced by key rotation',\n })\n .where(\n and(\n eq(userPublicKeys.keyId, oldKeyId),\n eq(userPublicKeys.userId, userId)\n )\n );\n\n // Store new public key (90 days expiry)\n await create(userPublicKeys, {\n userId,\n keyId: newKeyId,\n publicKey: newPublicKey,\n algorithm,\n fingerprint,\n isActive: true,\n createdAt: new Date(),\n expiresAt: getKeyExpiryDate(),\n });\n\n return {\n success: true,\n keyId: newKeyId,\n };\n}\n\n/**\n * Revoke a user's public key\n */\nexport async function revokeKeyService(\n params: RevokeKeyParams\n): Promise<void>\n{\n const { userId, keyId, reason } = params;\n\n const db = getDatabase()!;\n\n await db\n .update(userPublicKeys)\n .set({\n isActive: false,\n revokedAt: new Date(),\n revokedReason: reason,\n })\n .where(\n and(\n eq(userPublicKeys.keyId, keyId),\n eq(userPublicKeys.userId, userId)\n )\n );\n}","/**\n * @spfn/auth - User Service\n *\n * Handles user CRUD operations\n */\n\nimport { findOne, updateOne } from '@spfn/core/db';\nimport { users, type User } from '@/server/entities';\n\n/**\n * Get user by ID\n */\nexport async function getUserByIdService(userId: number): Promise<User | null>\n{\n return await findOne(users, { id: userId });\n}\n\n/**\n * Get user by email\n */\nexport async function getUserByEmailService(email: string): Promise<User | null>\n{\n return await findOne(users, { email });\n}\n\n/**\n * Get user by phone\n */\nexport async function getUserByPhoneService(phone: string): Promise<User | null>\n{\n return await findOne(users, { phone });\n}\n\n/**\n * Update user's last login timestamp\n */\nexport async function updateLastLoginService(userId: number): Promise<void>\n{\n await updateOne(users, { id: userId }, {\n lastLoginAt: new Date(),\n });\n}\n\n/**\n * Update user data\n */\nexport async function updateUserService(\n userId: number,\n updates: Partial<Omit<User, 'id' | 'createdAt'>>\n): Promise<void>\n{\n await updateOne(users, { id: userId }, {\n ...updates,\n updatedAt: new Date(),\n });\n}","/**\n * @spfn/auth - Verification Service\n *\n * Handles OTP code generation, validation, and delivery\n */\n\nimport {\n generateVerificationCode,\n storeVerificationCode,\n validateVerificationCode,\n markCodeAsUsed,\n createVerificationToken,\n sendVerificationEmail,\n sendVerificationSMS\n} from '@/server/helpers/verification';\nimport { InvalidVerificationCodeError } from '@/server/errors';\n\nexport interface SendVerificationCodeParams\n{\n target: string;\n targetType: 'email' | 'phone';\n purpose: 'registration' | 'login' | 'password_reset';\n}\n\nexport interface SendVerificationCodeResult\n{\n success: boolean;\n expiresAt: string;\n}\n\nexport interface VerifyCodeParams\n{\n target: string;\n targetType: 'email' | 'phone';\n code: string;\n purpose: 'registration' | 'login' | 'password_reset';\n}\n\nexport interface VerifyCodeResult\n{\n valid: boolean;\n verificationToken: string;\n}\n\n/**\n * Send verification code via email or SMS\n */\nexport async function sendVerificationCodeService(\n params: SendVerificationCodeParams\n): Promise<SendVerificationCodeResult>\n{\n const { target, targetType, purpose } = params;\n\n // Generate 6-digit verification code\n const code = generateVerificationCode();\n\n // Store code in database\n const codeRecord = await storeVerificationCode(target, targetType, code, purpose);\n\n // Send code via email or SMS\n if (targetType === 'email')\n {\n await sendVerificationEmail(target, code, purpose);\n }\n else\n {\n await sendVerificationSMS(target, code, purpose);\n }\n\n return {\n success: true,\n expiresAt: codeRecord.expiresAt.toISOString(),\n };\n}\n\n/**\n * Verify OTP code and return verification token\n */\nexport async function verifyCodeService(\n params: VerifyCodeParams\n): Promise<VerifyCodeResult>\n{\n const { target, targetType, code, purpose } = params;\n\n // Validate the verification code\n const validation = await validateVerificationCode(target, targetType, code, purpose);\n\n if (!validation.valid)\n {\n throw new InvalidVerificationCodeError(validation.error || 'Invalid verification code');\n }\n\n // Mark code as used\n await markCodeAsUsed(validation.codeId!);\n\n // Create verification token (15 min validity)\n const verificationToken = createVerificationToken({\n target,\n targetType,\n purpose,\n codeId: validation.codeId!,\n });\n\n return {\n valid: true,\n verificationToken,\n };\n}\n","/**\n * @spfn/auth - Me Service\n *\n * Service for retrieving current user information\n */\n\nimport { getDatabase } from '@spfn/core/db';\nimport { users, roles, permissions, rolePermissions } from '@/server/entities';\nimport { eq, and } from 'drizzle-orm';\n\nexport interface GetMeResult\n{\n userId: string;\n email?: string;\n phone?: string;\n role: {\n id: number;\n name: string;\n displayName: string;\n priority: number;\n };\n permissions: Array<{\n id: number;\n name: string;\n displayName: string;\n category?: string;\n }>;\n}\n\n/**\n * Get current user information including role and permissions\n *\n * @param userId - User ID (string, number, or bigint)\n * @returns User info with role and permissions\n *\n * @example\n * ```typescript\n * const userInfo = await getMeService('123');\n * console.log(userInfo.role.name); // 'admin'\n * console.log(userInfo.permissions.length); // 15\n * ```\n */\nexport async function getMeService(userId: string | number | bigint): Promise<GetMeResult>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const userIdNum = typeof userId === 'string' ? Number(userId) : Number(userId);\n\n // 1. Get user with role information\n const [userWithRole] = await db\n .select({\n userId: users.id,\n email: users.email,\n phone: users.phone,\n roleId: roles.id,\n roleName: roles.name,\n roleDisplayName: roles.displayName,\n rolePriority: roles.priority,\n })\n .from(users)\n .innerJoin(roles, eq(users.roleId, roles.id))\n .where(eq(users.id, userIdNum))\n .limit(1);\n\n if (!userWithRole)\n {\n throw new Error('[Auth] User not found');\n }\n\n // 2. Get role permissions\n const rolePerms = await db\n .select({\n id: permissions.id,\n name: permissions.name,\n displayName: permissions.displayName,\n category: permissions.category,\n })\n .from(rolePermissions)\n .innerJoin(permissions, eq(rolePermissions.permissionId, permissions.id))\n .where(\n and(\n eq(rolePermissions.roleId, userWithRole.roleId),\n eq(permissions.isActive, true)\n )\n );\n\n // 3. Build response\n return {\n userId: userWithRole.userId.toString(),\n email: userWithRole.email ?? undefined,\n phone: userWithRole.phone ?? undefined,\n role: {\n id: userWithRole.roleId,\n name: userWithRole.roleName,\n displayName: userWithRole.roleDisplayName,\n priority: userWithRole.rolePriority,\n },\n permissions: rolePerms.map(perm => ({\n id: perm.id,\n name: perm.name,\n displayName: perm.displayName,\n category: perm.category ?? undefined,\n })),\n };\n}","/**\n * @spfn/auth - RBAC Initialization Service\n *\n * Initialize roles, permissions, and their mappings\n */\n\nimport { getDatabase } from '@spfn/core/db';\nimport { logger } from '@spfn/core/logger';\nimport { roles, permissions, rolePermissions } from '@/server/entities';\nimport {\n BUILTIN_ROLES,\n BUILTIN_PERMISSIONS,\n BUILTIN_ROLE_PERMISSIONS,\n} from '@/server/rbac';\nimport type { AuthInitOptions, RoleConfig, PermissionConfig } from '@/server/rbac';\nimport { eq, and, inArray } from 'drizzle-orm';\nimport { configureAuth } from '@/lib/config';\n\nconst authLogger = logger.child('@spfn/auth');\n\n/**\n * Initialize auth package with RBAC system\n *\n * Creates built-in roles, permissions, and custom configurations\n *\n * @param options - Initialization options\n *\n * @example\n * ```typescript\n * // Minimal - only built-in roles (user, admin, superadmin)\n * await initializeAuth();\n *\n * // Custom roles and permissions\n * await initializeAuth({\n * roles: [\n * { name: 'project-manager', displayName: 'Project Manager', priority: 50 },\n * { name: 'developer', displayName: 'Developer', priority: 30 },\n * ],\n * permissions: [\n * { name: 'project:create', displayName: 'Create Project', category: 'project' },\n * { name: 'task:assign', displayName: 'Assign Task', category: 'task' },\n * ],\n * rolePermissions: {\n * 'project-manager': ['project:create', 'task:assign'],\n * 'developer': ['task:complete'],\n * },\n * });\n * ```\n */\nexport async function initializeAuth(options: AuthInitOptions = {}): Promise<void>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized. Call initDatabase() first.');\n }\n\n authLogger.info('🔐 Initializing RBAC system...');\n\n // Configure global auth settings\n if (options.sessionTtl !== undefined)\n {\n configureAuth({\n sessionTtl: options.sessionTtl,\n });\n authLogger.info(`⏱️ Session TTL: ${options.sessionTtl}`);\n }\n\n // 1. Collect all roles (built-in + custom)\n const allRoles: RoleConfig[] = [\n ...Object.values(BUILTIN_ROLES),\n ...(options.roles || []),\n ];\n\n // 2. Create/update all roles\n for (const roleConfig of allRoles)\n {\n await upsertRole(roleConfig);\n }\n\n // 3. Collect all permissions (built-in + custom)\n const allPermissions: PermissionConfig[] = [\n ...Object.values(BUILTIN_PERMISSIONS),\n ...(options.permissions || []),\n ];\n\n // 4. Create/update all permissions\n for (const permConfig of allPermissions)\n {\n await upsertPermission(permConfig);\n }\n\n // 5. Collect all role-permission mappings (built-in + custom)\n const allMappings: Record<string, string[]> = { ...BUILTIN_ROLE_PERMISSIONS };\n\n // Merge custom mappings\n if (options.rolePermissions)\n {\n for (const [roleName, permNames] of Object.entries(options.rolePermissions))\n {\n if (allMappings[roleName])\n {\n // Merge with existing mappings (deduplicate)\n allMappings[roleName] = [\n ...new Set([...allMappings[roleName], ...permNames]),\n ];\n }\n else\n {\n // New role mapping\n allMappings[roleName] = permNames;\n }\n }\n }\n\n // 6. Create all role-permission mappings\n for (const [roleName, permNames] of Object.entries(allMappings))\n {\n await assignPermissionsToRole(roleName, permNames);\n }\n\n authLogger.info('✅ RBAC initialization complete');\n authLogger.info(`📊 Roles: ${allRoles.length}, Permissions: ${allPermissions.length}`);\n authLogger.info('🔒 Built-in roles: user, admin, superadmin');\n}\n\n/**\n * Create or update a role (idempotent)\n */\nasync function upsertRole(config: RoleConfig): Promise<void>\n{\n const db = getDatabase()!;\n\n const existing = await db\n .select()\n .from(roles)\n .where(eq(roles.name, config.name))\n .limit(1);\n\n if (existing.length === 0)\n {\n // Create new role\n await db.insert(roles).values({\n name: config.name,\n displayName: config.displayName,\n description: config.description,\n priority: config.priority ?? 10,\n isSystem: config.isSystem ?? false,\n isBuiltin: config.isBuiltin ?? false,\n });\n\n authLogger.info(` ✅ Created role: ${config.name}`);\n }\n else\n {\n // Update existing role (but preserve priority for built-in roles)\n const updateData: Record<string, any> = {\n displayName: config.displayName,\n description: config.description,\n };\n\n // Only update priority for non-builtin roles\n if (!existing[0].isBuiltin)\n {\n updateData.priority = config.priority ?? existing[0].priority;\n }\n\n await db\n .update(roles)\n .set(updateData)\n .where(eq(roles.id, existing[0].id));\n }\n}\n\n/**\n * Create or update a permission (idempotent)\n */\nasync function upsertPermission(config: PermissionConfig): Promise<void>\n{\n const db = getDatabase()!;\n\n const existing = await db\n .select()\n .from(permissions)\n .where(eq(permissions.name, config.name))\n .limit(1);\n\n if (existing.length === 0)\n {\n // Create new permission\n await db.insert(permissions).values({\n name: config.name,\n displayName: config.displayName,\n description: config.description,\n category: config.category,\n isSystem: config.isSystem ?? false,\n isBuiltin: config.isBuiltin ?? false,\n });\n\n authLogger.info(` ✅ Created permission: ${config.name}`);\n }\n else\n {\n // Update existing permission\n await db\n .update(permissions)\n .set({\n displayName: config.displayName,\n description: config.description,\n category: config.category,\n })\n .where(eq(permissions.id, existing[0].id));\n }\n}\n\n/**\n * Assign permissions to a role\n */\nasync function assignPermissionsToRole(roleName: string, permissionNames: string[]): Promise<void>\n{\n const db = getDatabase()!;\n\n // Get role\n const [role] = await db\n .select()\n .from(roles)\n .where(eq(roles.name, roleName))\n .limit(1);\n\n if (!role)\n {\n authLogger.warn(` ⚠️ Role not found: ${roleName}, skipping permission assignment`);\n return;\n }\n\n // Get permissions\n const perms = await db\n .select()\n .from(permissions)\n .where(inArray(permissions.name, permissionNames));\n\n if (perms.length === 0)\n {\n authLogger.warn(` ⚠️ No permissions found for role: ${roleName}`);\n return;\n }\n\n // Create mappings (skip duplicates)\n for (const perm of perms)\n {\n const existing = await db\n .select()\n .from(rolePermissions)\n .where(\n and(\n eq(rolePermissions.roleId, role.id),\n eq(rolePermissions.permissionId, perm.id)\n )\n )\n .limit(1);\n\n if (existing.length === 0)\n {\n await db.insert(rolePermissions).values({\n roleId: role.id,\n permissionId: perm.id,\n });\n }\n }\n}","/**\n * @spfn/auth - Global Configuration\n *\n * Manages global auth configuration including session TTL\n */\n\n/**\n * Cookie names used by SPFN Auth\n */\nexport const COOKIE_NAMES = {\n /** Encrypted session data (userId, privateKey, keyId, algorithm) */\n SESSION: 'spfn_session',\n /** Current key ID (for key rotation) */\n SESSION_KEY_ID: 'spfn_session_key_id',\n} as const;\n\n/**\n * Parse duration string to seconds\n *\n * Supports: '30d', '12h', '45m', '3600s', or plain number\n *\n * @example\n * parseDuration('30d') // 2592000 (30 days in seconds)\n * parseDuration('12h') // 43200\n * parseDuration('45m') // 2700\n * parseDuration('3600') // 3600\n */\nexport function parseDuration(duration: string | number): number\n{\n if (typeof duration === 'number')\n {\n return duration;\n }\n\n const match = duration.match(/^(\\d+)([dhms]?)$/);\n if (!match)\n {\n throw new Error(`Invalid duration format: ${duration}. Use format like '30d', '12h', '45m', '3600s', or plain number.`);\n }\n\n const value = parseInt(match[1], 10);\n const unit = match[2] || 's';\n\n switch (unit)\n {\n case 'd':\n return value * 24 * 60 * 60;\n case 'h':\n return value * 60 * 60;\n case 'm':\n return value * 60;\n case 's':\n return value;\n default:\n throw new Error(`Unknown duration unit: ${unit}`);\n }\n}\n\n/**\n * Auth configuration\n */\nexport interface AuthConfig\n{\n /**\n * Default session TTL in seconds or duration string\n *\n * Supports:\n * - Number: seconds (e.g., 2592000)\n * - String: '30d', '12h', '45m', '3600s'\n *\n * @default 7d (7 days)\n */\n sessionTtl?: string | number;\n}\n\n/**\n * Global auth configuration state\n */\nlet globalConfig: AuthConfig = {\n sessionTtl: '7d', // Default: 7 days\n};\n\n/**\n * Configure global auth settings\n *\n * @param config - Auth configuration\n *\n * @example\n * ```typescript\n * configureAuth({\n * sessionTtl: '30d', // 30 days\n * });\n * ```\n */\nexport function configureAuth(config: AuthConfig): void\n{\n globalConfig = {\n ...globalConfig,\n ...config,\n };\n}\n\n/**\n * Get current auth configuration\n */\nexport function getAuthConfig(): AuthConfig\n{\n return { ...globalConfig };\n}\n\n/**\n * Get session TTL in seconds\n *\n * Priority:\n * 1. Runtime override (remember parameter)\n * 2. Global config (configureAuth)\n * 3. Environment variable (SPFN_AUTH_SESSION_TTL)\n * 4. Default (7 days)\n */\nexport function getSessionTtl(override?: string | number): number\n{\n // 1. Runtime override\n if (override !== undefined)\n {\n return parseDuration(override);\n }\n\n // 2. Global config\n if (globalConfig.sessionTtl !== undefined)\n {\n return parseDuration(globalConfig.sessionTtl);\n }\n\n // 3. Environment variable\n const envTtl = process.env.SPFN_AUTH_SESSION_TTL;\n if (envTtl)\n {\n return parseDuration(envTtl);\n }\n\n // 4. Default: 7 days\n return 7 * 24 * 60 * 60;\n}","/**\n * @spfn/auth - Permission Service\n *\n * Permission checking and validation logic\n */\n\nimport { getDatabase } from '@spfn/core/db';\nimport { users, roles, permissions, rolePermissions, userPermissions } from '@/server/entities';\nimport { eq, and } from 'drizzle-orm';\n\n/**\n * Get all permissions for a user\n *\n * Combines role-based permissions with user-specific overrides\n * Handles expiration of temporary permissions\n *\n * @param userId - User ID (string, number, or bigint)\n * @returns Array of permission names\n *\n * @example\n * ```typescript\n * const perms = await getUserPermissions('123');\n * // ['auth:self:manage', 'user:read', 'post:create']\n * ```\n */\nexport async function getUserPermissions(userId: string | number | bigint): Promise<string[]>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const userIdNum = typeof userId === 'string' ? Number(userId) : Number(userId);\n\n // 1. Get user's role\n const [user] = await db\n .select({ roleId: users.roleId })\n .from(users)\n .where(eq(users.id, userIdNum))\n .limit(1);\n\n if (!user || !user.roleId)\n {\n return [];\n }\n\n const permSet = new Set<string>();\n\n // 2. Get role-based permissions\n const rolePerms = await db\n .select({ name: permissions.name })\n .from(rolePermissions)\n .innerJoin(permissions, eq(rolePermissions.permissionId, permissions.id))\n .where(\n and(\n eq(rolePermissions.roleId, user.roleId),\n eq(permissions.isActive, true)\n )\n );\n\n for (const perm of rolePerms)\n {\n permSet.add(perm.name);\n }\n\n // 3. Apply user-specific permission overrides\n const userPerms = await db\n .select({\n name: permissions.name,\n granted: userPermissions.granted,\n expiresAt: userPermissions.expiresAt,\n })\n .from(userPermissions)\n .innerJoin(permissions, eq(userPermissions.permissionId, permissions.id))\n .where(eq(userPermissions.userId, userIdNum));\n\n const now = new Date();\n for (const userPerm of userPerms)\n {\n // Skip expired permissions\n if (userPerm.expiresAt && userPerm.expiresAt < now)\n {\n continue;\n }\n\n if (userPerm.granted)\n {\n // Grant permission (add even if not in role)\n permSet.add(userPerm.name);\n }\n else\n {\n // Revoke permission (remove even if in role)\n permSet.delete(userPerm.name);\n }\n }\n\n return Array.from(permSet);\n}\n\n/**\n * Check if user has a specific permission\n *\n * @param userId - User ID\n * @param permissionName - Permission name (e.g., 'user:delete')\n * @returns true if user has permission\n *\n * @example\n * ```typescript\n * if (await hasPermission('123', 'user:delete')) {\n * // User can delete users\n * }\n * ```\n */\nexport async function hasPermission(\n userId: string | number | bigint,\n permissionName: string\n): Promise<boolean>\n{\n const perms = await getUserPermissions(userId);\n return perms.includes(permissionName);\n}\n\n/**\n * Check if user has any of the specified permissions\n *\n * @param userId - User ID\n * @param permissionNames - Array of permission names\n * @returns true if user has at least one permission\n *\n * @example\n * ```typescript\n * if (await hasAnyPermission('123', ['post:read', 'admin:access'])) {\n * // User can access content\n * }\n * ```\n */\nexport async function hasAnyPermission(\n userId: string | number | bigint,\n permissionNames: string[]\n): Promise<boolean>\n{\n const perms = await getUserPermissions(userId);\n return permissionNames.some(p => perms.includes(p));\n}\n\n/**\n * Check if user has all of the specified permissions\n *\n * @param userId - User ID\n * @param permissionNames - Array of permission names\n * @returns true if user has all permissions\n *\n * @example\n * ```typescript\n * if (await hasAllPermissions('123', ['post:write', 'post:publish'])) {\n * // User can write AND publish\n * }\n * ```\n */\nexport async function hasAllPermissions(\n userId: string | number | bigint,\n permissionNames: string[]\n): Promise<boolean>\n{\n const perms = await getUserPermissions(userId);\n return permissionNames.every(p => perms.includes(p));\n}\n\n/**\n * Check if user has a specific role\n *\n * @param userId - User ID\n * @param roleName - Role name (e.g., 'admin', 'superadmin')\n * @returns true if user has role\n *\n * @example\n * ```typescript\n * if (await hasRole('123', 'admin')) {\n * // User is admin\n * }\n * ```\n */\nexport async function hasRole(userId: string | number | bigint, roleName: string): Promise<boolean>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const userIdNum = typeof userId === 'string' ? Number(userId) : Number(userId);\n\n const [user] = await db\n .select({ roleId: users.roleId })\n .from(users)\n .where(eq(users.id, userIdNum))\n .limit(1);\n\n if (!user || !user.roleId)\n {\n return false;\n }\n\n const [role] = await db\n .select({ name: roles.name })\n .from(roles)\n .where(eq(roles.id, user.roleId))\n .limit(1);\n\n return role?.name === roleName;\n}\n\n/**\n * Check if user has any of the specified roles\n *\n * @param userId - User ID\n * @param roleNames - Array of role names\n * @returns true if user has at least one role\n */\nexport async function hasAnyRole(userId: string | number | bigint, roleNames: string[]): Promise<boolean>\n{\n for (const roleName of roleNames)\n {\n if (await hasRole(userId, roleName))\n {\n return true;\n }\n }\n\n return false;\n}","/**\n * @spfn/auth - Services Export\n *\n * All business logic services for reusability\n */\n\n// Auth Service\nexport {\n checkAccountExistsService,\n registerService,\n loginService,\n logoutService,\n changePasswordService,\n} from './auth.service';\n\nexport type {\n CheckAccountExistsParams,\n CheckAccountExistsResult,\n RegisterParams,\n RegisterResult,\n LoginParams,\n LoginResult,\n LogoutParams,\n ChangePasswordParams,\n} from './auth.service';\n\n// Verification Service\nexport {\n sendVerificationCodeService,\n verifyCodeService,\n} from './verification.service';\n\nexport type {\n SendVerificationCodeParams,\n SendVerificationCodeResult,\n VerifyCodeParams,\n VerifyCodeResult,\n} from './verification.service';\n\n// Key Service\nexport {\n registerPublicKeyService,\n rotateKeyService,\n revokeKeyService,\n} from './key.service';\n\nexport type {\n RegisterPublicKeyParams,\n RotateKeyParams,\n RotateKeyResult,\n RevokeKeyParams,\n} from './key.service';\n\n// User Service\nexport {\n getUserByIdService,\n getUserByEmailService,\n getUserByPhoneService,\n updateLastLoginService,\n updateUserService,\n} from './user.service';\n\n// Me Service\nexport {\n getMeService,\n} from './me.service';\n\nexport type {\n GetMeResult,\n} from './me.service';\n\n// RBAC Service\nexport {\n initializeAuth,\n} from './rbac.service';\n\n// Permission Service\nexport {\n getUserPermissions,\n hasPermission,\n hasAnyPermission,\n hasAllPermissions,\n hasRole,\n hasAnyRole,\n} from './permission.service';\n\n// Role Service\nexport {\n createRole,\n updateRole,\n deleteRole,\n addPermissionToRole,\n removePermissionFromRole,\n setRolePermissions,\n getAllRoles,\n getRoleByName,\n getRolePermissions,\n} from './role.service';\n\n// Invitation Service\nexport {\n createInvitation,\n getInvitationByToken,\n getInvitationWithDetails,\n validateInvitation,\n acceptInvitation,\n listInvitations,\n cancelInvitation,\n deleteInvitation,\n expireOldInvitations,\n resendInvitation,\n} from './invitation.service';","/**\n * @spfn/auth - Invitation Service\n *\n * User invitation management for invite-only registration\n */\n\nimport { getDatabase } from '@spfn/core/db';\nimport { invitations, users, roles } from '@/server/entities';\nimport type { Invitation, InvitationStatus, InvitationWithDetails } from '@/server/entities/invitations';\nimport { eq, and, lt, desc, sql } from 'drizzle-orm';\nimport { hashPassword } from '@/server/helpers';\nimport crypto from 'crypto';\n\n/**\n * Generate unique invitation token (UUID v4)\n */\nfunction generateInvitationToken(): string\n{\n return crypto.randomUUID();\n}\n\n/**\n * Calculate expiration date from now\n *\n * @param days - Number of days until expiration (default: 7)\n * @returns Expiration timestamp\n */\nfunction calculateExpiresAt(days: number = 7): Date\n{\n const expiresAt = new Date();\n expiresAt.setDate(expiresAt.getDate() + days);\n return expiresAt;\n}\n\n/**\n * Create a new invitation\n *\n * @param params - Invitation parameters\n * @returns Created invitation\n * @throws Error if validation fails\n *\n * @example\n * ```typescript\n * const invitation = await createInvitation({\n * email: 'newuser@example.com',\n * roleId: 2n,\n * invitedBy: 1n,\n * expiresInDays: 7,\n * metadata: { message: 'Welcome!' }\n * });\n * ```\n */\nexport async function createInvitation(params: {\n email: string;\n roleId: number;\n invitedBy: number;\n expiresInDays?: number;\n metadata?: Record<string, any>;\n}): Promise<Invitation>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const { email, roleId, invitedBy, expiresInDays = 7, metadata } = params;\n\n // Validate email format\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (!emailRegex.test(email))\n {\n throw new Error('Invalid email format');\n }\n\n // Check if user already exists\n const existingUser = await db\n .select()\n .from(users)\n .where(eq(users.email, email))\n .limit(1);\n\n if (existingUser.length > 0)\n {\n throw new Error('User with this email already exists');\n }\n\n // Check if there's already a pending invitation for this email\n const existingInvitation = await db\n .select()\n .from(invitations)\n .where(\n and(\n eq(invitations.email, email),\n eq(invitations.status, 'pending')\n )\n )\n .limit(1);\n\n if (existingInvitation.length > 0)\n {\n throw new Error('Pending invitation already exists for this email');\n }\n\n // Verify role exists\n const role = await db\n .select()\n .from(roles)\n .where(eq(roles.id, roleId))\n .limit(1);\n\n if (role.length === 0)\n {\n throw new Error(`Role with id ${roleId} not found`);\n }\n\n // Verify inviter exists\n const inviter = await db\n .select()\n .from(users)\n .where(eq(users.id, invitedBy))\n .limit(1);\n\n if (inviter.length === 0)\n {\n throw new Error(`User with id ${invitedBy} not found`);\n }\n\n // Generate unique token\n const token = generateInvitationToken();\n const expiresAt = calculateExpiresAt(expiresInDays);\n\n // Create invitation\n const [invitation] = await db\n .insert(invitations)\n .values({\n email,\n token,\n roleId,\n invitedBy,\n status: 'pending',\n expiresAt,\n metadata: metadata || null,\n })\n .returning();\n\n console.log(`[Auth] ✅ Created invitation: ${email} as ${role[0].name} (expires: ${expiresAt.toISOString()})`);\n\n return invitation;\n}\n\n/**\n * Get invitation by token\n *\n * @param token - Invitation token (UUID)\n * @returns Invitation or null if not found\n */\nexport async function getInvitationByToken(token: string): Promise<Invitation | null>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const result = await db\n .select()\n .from(invitations)\n .where(eq(invitations.token, token))\n .limit(1);\n\n return result[0] || null;\n}\n\n/**\n * Get invitation with role and inviter details\n *\n * @param token - Invitation token\n * @returns Invitation with joined data or null\n */\nexport async function getInvitationWithDetails(token: string): Promise<InvitationWithDetails | null>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const result = await db\n .select({\n id: invitations.id,\n email: invitations.email,\n token: invitations.token,\n roleId: invitations.roleId,\n invitedBy: invitations.invitedBy,\n status: invitations.status,\n expiresAt: invitations.expiresAt,\n acceptedAt: invitations.acceptedAt,\n cancelledAt: invitations.cancelledAt,\n metadata: invitations.metadata,\n createdAt: invitations.createdAt,\n updatedAt: invitations.updatedAt,\n role: {\n id: roles.id,\n name: roles.name,\n displayName: roles.displayName,\n },\n inviter: {\n id: users.id,\n email: users.email,\n },\n })\n .from(invitations)\n .innerJoin(roles, eq(invitations.roleId, roles.id))\n .innerJoin(users, eq(invitations.invitedBy, users.id))\n .where(eq(invitations.token, token))\n .limit(1);\n\n return result[0] || null;\n}\n\n/**\n * Validate invitation\n *\n * Checks if invitation is valid for acceptance\n *\n * @param token - Invitation token\n * @returns Validation result\n */\nexport async function validateInvitation(token: string): Promise<{\n valid: boolean;\n invitation?: Invitation;\n error?: string;\n}>\n{\n const invitation = await getInvitationByToken(token);\n\n if (!invitation)\n {\n return { valid: false, error: 'Invitation not found' };\n }\n\n if (invitation.status === 'accepted')\n {\n return { valid: false, error: 'Invitation already accepted', invitation };\n }\n\n if (invitation.status === 'cancelled')\n {\n return { valid: false, error: 'Invitation was cancelled', invitation };\n }\n\n if (invitation.status === 'expired')\n {\n return { valid: false, error: 'Invitation has expired', invitation };\n }\n\n // Check if expired by time\n if (new Date() > new Date(invitation.expiresAt))\n {\n return { valid: false, error: 'Invitation has expired', invitation };\n }\n\n return { valid: true, invitation };\n}\n\n/**\n * Accept invitation and create user account\n *\n * @param params - Acceptance parameters\n * @returns Created user info\n * @throws Error if invitation is invalid or user creation fails\n *\n * @example\n * ```typescript\n * const user = await acceptInvitation({\n * token: 'uuid-v4',\n * password: 'SecurePass123!',\n * publicKey: 'base64-der...',\n * keyId: 'uuid-v4',\n * fingerprint: 'sha256-hex',\n * algorithm: 'ES256'\n * });\n * ```\n */\nexport async function acceptInvitation(params: {\n token: string;\n password: string;\n publicKey: string;\n keyId: string;\n fingerprint: string;\n algorithm: 'ES256' | 'RS256';\n}): Promise<{\n userId: number;\n email: string;\n role: string;\n}>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const { token, password, publicKey, keyId, fingerprint, algorithm } = params;\n\n // Validate invitation\n const validation = await validateInvitation(token);\n\n if (!validation.valid || !validation.invitation)\n {\n throw new Error(validation.error || 'Invalid invitation');\n }\n\n const invitation = validation.invitation;\n\n // Get role details\n const role = await db\n .select()\n .from(roles)\n .where(eq(roles.id, invitation.roleId))\n .limit(1);\n\n if (role.length === 0)\n {\n throw new Error('Role not found');\n }\n\n // Hash password\n const passwordHash = await hashPassword(password);\n\n // Use transaction to create user and update invitation atomically\n const result = await db.transaction(async (tx) =>\n {\n // Create user\n const [newUser] = await tx\n .insert(users)\n .values({\n email: invitation.email,\n passwordHash,\n roleId: invitation.roleId,\n emailVerifiedAt: new Date(), // Auto-verify invited users\n passwordChangeRequired: false,\n status: 'active',\n })\n .returning();\n\n // Create public key for asymmetric JWT\n const { userPublicKeys } = await import('@/server/entities');\n await tx\n .insert(userPublicKeys)\n .values({\n userId: newUser.id,\n keyId,\n publicKey,\n algorithm,\n fingerprint,\n isActive: true,\n expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // 90 days\n });\n\n // Update invitation status\n await tx\n .update(invitations)\n .set({\n status: 'accepted',\n acceptedAt: new Date(),\n updatedAt: new Date(),\n })\n .where(eq(invitations.id, invitation.id));\n\n return { newUser, role: role[0] };\n });\n\n console.log(`[Auth] ✅ Invitation accepted: ${invitation.email} as ${result.role.name}`);\n\n return {\n userId: result.newUser.id,\n email: result.newUser.email!,\n role: result.role.name,\n };\n}\n\n/**\n * List invitations with filtering and pagination\n *\n * @param params - Query parameters\n * @returns Paginated invitations\n *\n * @example\n * ```typescript\n * const result = await listInvitations({\n * status: 'pending',\n * invitedBy: 1n,\n * page: 1,\n * limit: 20\n * });\n * ```\n */\nexport async function listInvitations(params: {\n status?: InvitationStatus;\n invitedBy?: number;\n page?: number;\n limit?: number;\n}): Promise<{\n invitations: InvitationWithDetails[];\n total: number;\n page: number;\n limit: number;\n totalPages: number;\n}>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const { status, invitedBy, page = 1, limit = 20 } = params;\n const offset = (page - 1) * limit;\n\n // Build where conditions\n const conditions = [];\n if (status)\n {\n conditions.push(eq(invitations.status, status));\n }\n if (invitedBy)\n {\n conditions.push(eq(invitations.invitedBy, invitedBy));\n }\n\n const whereClause = conditions.length > 0 ? and(...conditions) : undefined;\n\n // Get total count\n const countResult = await db\n .select({ count: sql<number>`count(*)` })\n .from(invitations)\n .where(whereClause);\n\n const total = Number(countResult[0]?.count || 0);\n\n // Get paginated results with joins\n const results = await db\n .select({\n id: invitations.id,\n email: invitations.email,\n token: invitations.token,\n roleId: invitations.roleId,\n invitedBy: invitations.invitedBy,\n status: invitations.status,\n expiresAt: invitations.expiresAt,\n acceptedAt: invitations.acceptedAt,\n cancelledAt: invitations.cancelledAt,\n metadata: invitations.metadata,\n createdAt: invitations.createdAt,\n updatedAt: invitations.updatedAt,\n role: {\n id: roles.id,\n name: roles.name,\n displayName: roles.displayName,\n },\n inviter: {\n id: users.id,\n email: users.email,\n },\n })\n .from(invitations)\n .innerJoin(roles, eq(invitations.roleId, roles.id))\n .innerJoin(users, eq(invitations.invitedBy, users.id))\n .where(whereClause)\n .orderBy(desc(invitations.createdAt))\n .limit(limit)\n .offset(offset);\n\n return {\n invitations: results,\n total,\n page,\n limit,\n totalPages: Math.ceil(total / limit),\n };\n}\n\n/**\n * Cancel invitation\n *\n * Only pending invitations can be cancelled\n *\n * @param id - Invitation ID\n * @param cancelledBy - User ID who cancelled\n * @param reason - Optional cancellation reason\n * @throws Error if invitation cannot be cancelled\n */\nexport async function cancelInvitation(\n id: number,\n cancelledBy: number,\n reason?: string\n): Promise<void>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n // Get invitation\n const invitation = await db\n .select()\n .from(invitations)\n .where(eq(invitations.id, id))\n .limit(1);\n\n if (invitation.length === 0)\n {\n throw new Error('Invitation not found');\n }\n\n if (invitation[0].status !== 'pending')\n {\n throw new Error(`Cannot cancel ${invitation[0].status} invitation`);\n }\n\n // Update status\n await db\n .update(invitations)\n .set({\n status: 'cancelled',\n cancelledAt: new Date(),\n updatedAt: new Date(),\n metadata: invitation[0].metadata\n ? { ...invitation[0].metadata, cancelReason: reason, cancelledBy }\n : { cancelReason: reason, cancelledBy },\n })\n .where(eq(invitations.id, id));\n\n console.log(`[Auth] ⚠️ Invitation cancelled: ${invitation[0].email} (reason: ${reason || 'none'})`);\n}\n\n/**\n * Delete invitation\n *\n * Permanently removes invitation record\n * Typically only for superadmin cleanup\n *\n * @param id - Invitation ID\n */\nexport async function deleteInvitation(id: number): Promise<void>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n await db\n .delete(invitations)\n .where(eq(invitations.id, id));\n\n console.log(`[Auth] 🗑️ Invitation deleted: ${id}`);\n}\n\n/**\n * Expire old invitations (cron job)\n *\n * Updates status of expired pending invitations\n *\n * @returns Number of invitations expired\n */\nexport async function expireOldInvitations(): Promise<number>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const now = new Date();\n\n // Find expired pending invitations\n const expiredInvitations = await db\n .select()\n .from(invitations)\n .where(\n and(\n eq(invitations.status, 'pending'),\n lt(invitations.expiresAt, now)\n )\n );\n\n if (expiredInvitations.length === 0)\n {\n return 0;\n }\n\n // Update to expired status\n await db\n .update(invitations)\n .set({\n status: 'expired',\n updatedAt: now,\n })\n .where(\n and(\n eq(invitations.status, 'pending'),\n lt(invitations.expiresAt, now)\n )\n );\n\n console.log(`[Auth] ⏰ Expired ${expiredInvitations.length} old invitations`);\n\n return expiredInvitations.length;\n}\n\n/**\n * Resend invitation email\n *\n * Extends expiration and triggers email resend\n *\n * @param id - Invitation ID\n * @param expiresInDays - New expiration period (default: 7)\n * @returns Updated invitation\n * @throws Error if invitation cannot be resent\n */\nexport async function resendInvitation(\n id: number,\n expiresInDays: number = 7\n): Promise<Invitation>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n // Get invitation\n const invitation = await db\n .select()\n .from(invitations)\n .where(eq(invitations.id, id))\n .limit(1);\n\n if (invitation.length === 0)\n {\n throw new Error('Invitation not found');\n }\n\n // Can only resend pending or expired invitations\n if (!['pending', 'expired'].includes(invitation[0].status))\n {\n throw new Error(`Cannot resend ${invitation[0].status} invitation`);\n }\n\n // Update expiration and status\n const newExpiresAt = calculateExpiresAt(expiresInDays);\n\n const [updated] = await db\n .update(invitations)\n .set({\n status: 'pending',\n expiresAt: newExpiresAt,\n updatedAt: new Date(),\n })\n .where(eq(invitations.id, id))\n .returning();\n\n console.log(`[Auth] 📧 Invitation resent: ${invitation[0].email} (new expiry: ${newExpiresAt.toISOString()})`);\n\n return updated;\n}","/**\n * @spfn/auth - Authentication Middleware\n *\n * Verify client-signed JWT token with public key\n *\n * Flow:\n * 1. Extract Authorization header\n * 2. Decode JWT to extract keyId\n * 3. Fetch public key from database\n * 4. Check key expiration\n * 5. Verify JWT signature with public key\n * 6. Validate user status\n * 7. Update last used timestamp\n * 8. Attach user to context\n *\n * Security Checks:\n * - Token signature verification\n * - Key expiration check\n * - User status check (active/inactive/suspended)\n * - Key revocation check (isActive flag)\n */\n\nimport type { Context, Next } from 'hono';\nimport { verifyClientToken } from '@/server/helpers/jwt';\nimport { findOne, getDatabase } from '@spfn/core/db';\nimport { users, userPublicKeys } from '@/server/entities';\nimport type { User } from '@/server/entities/users';\nimport {\n InvalidTokenError,\n TokenExpiredError,\n KeyExpiredError,\n AccountDisabledError,\n} from '@/server/errors';\nimport { UnauthorizedError } from '@spfn/core/errors';\nimport { eq, and } from 'drizzle-orm';\n\n// Auth context type\nexport interface AuthContext\n{\n user: User;\n userId: string;\n keyId: string;\n}\n\n// Extend Hono context with auth\ndeclare module 'hono'\n{\n interface ContextVariableMap\n {\n auth: AuthContext;\n }\n}\n\n/**\n * Authentication middleware\n *\n * Verifies client-signed JWT token using stored public key\n * Must be applied to routes that require authentication\n *\n * @example\n * ```typescript\n * // In route file\n * app.bind(logoutContract, [authenticate], async (c) => {\n * const auth = c.raw.get('auth'); // Get auth context\n * const { user, userId, keyId } = auth;\n * // Or access directly: c.raw.get('auth').user\n * });\n * ```\n */\nexport async function authenticate(c: Context, next: Next): Promise<Response | void>\n{\n // Extract Authorization header\n const authHeader = c.req.header('Authorization');\n\n // Validate Authorization header format\n if (!authHeader || !authHeader.startsWith('Bearer '))\n {\n throw new UnauthorizedError('Missing or invalid authorization header');\n }\n\n const token = authHeader.substring(7); // Remove 'Bearer ' prefix\n\n // 1. Decode JWT to extract keyId (without verification)\n // We need keyId to fetch the public key for verification\n const { decodeToken } = await import('@/server/helpers/jwt');\n const decoded = decodeToken(token);\n\n if (!decoded || !decoded.keyId)\n {\n throw new UnauthorizedError('Invalid token: missing keyId');\n }\n\n const keyId = decoded.keyId as string;\n\n // 2. Get public key from database\n // Query conditions:\n // - keyId matches (UUID)\n // - isActive = true (not revoked)\n const db = getDatabase()!;\n const [keyRecord] = await db\n .select()\n .from(userPublicKeys)\n .where(\n and(\n eq(userPublicKeys.keyId, keyId),\n eq(userPublicKeys.isActive, true)\n )\n );\n\n if (!keyRecord)\n {\n throw new UnauthorizedError('Invalid or revoked key');\n }\n\n // 3. Check key expiration\n // Keys expire after 90 days by default\n if (keyRecord.expiresAt && new Date() > keyRecord.expiresAt)\n {\n throw new KeyExpiredError();\n }\n\n // 4. Verify JWT signature with public key\n // This validates:\n // - Signature matches (client signed with private key)\n // - Token not expired (15min default)\n // - Issuer is 'spfn-client'\n try\n {\n verifyClientToken(\n token,\n keyRecord.publicKey,\n keyRecord.algorithm as 'ES256' | 'RS256'\n );\n }\n catch (err)\n {\n // Handle JWT verification errors\n if (err instanceof Error)\n {\n // Token expired (15min TTL)\n if (err.name === 'TokenExpiredError')\n {\n throw new TokenExpiredError();\n }\n\n // Invalid signature\n if (err.name === 'JsonWebTokenError')\n {\n throw new InvalidTokenError('Invalid token signature');\n }\n }\n\n // Generic authentication failure\n throw new UnauthorizedError('Authentication failed');\n }\n\n // 5. Get user from database\n const user = await findOne(users, { id: keyRecord.userId });\n if (!user)\n {\n throw new UnauthorizedError('User not found');\n }\n\n // 6. Check if user account is active\n // Status can be: active, inactive, suspended\n if (user.status !== 'active')\n {\n throw new AccountDisabledError(user.status);\n }\n\n // 7. Update last used timestamp (fire-and-forget)\n // Don't await to avoid blocking the request\n // Useful for:\n // - Security audits\n // - Detecting inactive keys\n // - Key rotation reminders\n db.update(userPublicKeys)\n .set({ lastUsedAt: new Date() })\n .where(eq(userPublicKeys.id, keyRecord.id))\n .execute()\n .catch((err: unknown) => console.error('Failed to update lastUsedAt:', err));\n\n // 8. Attach auth data to context\n // Available in downstream route handlers via c.get('auth')\n c.set('auth', {\n user,\n userId: String(user.id),\n keyId,\n });\n\n // Continue to route handler\n await next();\n}","/**\n * @spfn/auth - Permission Middleware\n *\n * Middleware functions for permission-based access control\n */\n\nimport type { Context, Next } from 'hono';\nimport { getAuth } from '@/server/helpers/context';\nimport { hasAllPermissions, hasAnyPermission } from '@/server/services/permission.service';\nimport { ForbiddenError } from '@spfn/core/errors';\n\n/**\n * Require user to have all specified permissions\n *\n * Must be used after authenticate middleware\n *\n * @param permissionNames - Permission names (e.g., 'user:delete', 'post:publish')\n * @returns Middleware function\n *\n * @example\n * ```typescript\n * app.bind(\n * deleteUserContract,\n * [authenticate, requirePermissions('user:delete')],\n * async (c) => {\n * // Only users with user:delete permission\n * }\n * );\n *\n * // Multiple permissions (all required)\n * app.bind(\n * publishPostContract,\n * [authenticate, requirePermissions('post:write', 'post:publish')],\n * async (c) => {\n * // Needs both permissions\n * }\n * );\n * ```\n */\nexport function requirePermissions(...permissionNames: string[])\n{\n return async (c: Context, next: Next): Promise<void> =>\n {\n const auth = getAuth(c);\n\n if (!auth)\n {\n throw new ForbiddenError('Authentication required');\n }\n\n const { userId } = auth;\n\n const allowed = await hasAllPermissions(userId, permissionNames);\n\n if (!allowed)\n {\n throw new ForbiddenError(\n `Missing required permissions: ${permissionNames.join(', ')}`\n );\n }\n\n await next();\n };\n}\n\n/**\n * Require user to have at least one of the specified permissions\n *\n * Must be used after authenticate middleware\n *\n * @param permissionNames - Permission names\n * @returns Middleware function\n *\n * @example\n * ```typescript\n * app.bind(\n * viewContentContract,\n * [authenticate, requireAnyPermission('content:read', 'admin:access')],\n * async (c) => {\n * // User has either content:read OR admin:access\n * }\n * );\n * ```\n */\nexport function requireAnyPermission(...permissionNames: string[])\n{\n return async (c: Context, next: Next): Promise<void> =>\n {\n const auth = getAuth(c);\n\n if (!auth)\n {\n throw new ForbiddenError('Authentication required');\n }\n\n const { userId } = auth;\n\n const allowed = await hasAnyPermission(userId, permissionNames);\n\n if (!allowed)\n {\n throw new ForbiddenError(\n `Requires one of: ${permissionNames.join(', ')}`\n );\n }\n\n await next();\n };\n}","/**\n * @spfn/auth - Role Middleware\n *\n * Middleware functions for role-based access control\n */\n\nimport type { Context, Next } from 'hono';\nimport { getAuth } from '@/server/helpers/context';\nimport { hasAnyRole } from '@/server/services/permission.service';\nimport { ForbiddenError } from '@spfn/core/errors';\n\n/**\n * Require user to have one of the specified roles\n *\n * Must be used after authenticate middleware\n *\n * @param roleNames - Role names (e.g., 'admin', 'superadmin')\n * @returns Middleware function\n *\n * @example\n * ```typescript\n * app.bind(\n * adminDashboardContract,\n * [authenticate, requireRole('admin', 'superadmin')],\n * async (c) => {\n * // Only admin or superadmin\n * }\n * );\n *\n * // Single role\n * app.bind(\n * systemConfigContract,\n * [authenticate, requireRole('superadmin')],\n * async (c) => {\n * // Only superadmin\n * }\n * );\n * ```\n */\nexport function requireRole(...roleNames: string[])\n{\n return async (c: Context, next: Next): Promise<void> =>\n {\n const auth = getAuth(c);\n\n if (!auth)\n {\n throw new ForbiddenError('Authentication required');\n }\n\n const { userId } = auth;\n\n const allowed = await hasAnyRole(userId, roleNames);\n\n if (!allowed)\n {\n throw new ForbiddenError(\n `Required roles: ${roleNames.join(', ')}`\n );\n }\n\n await next();\n };\n}","/**\n * @spfn/auth - Setup Functions\n *\n * Initial setup and admin account creation\n */\n\nimport { findOne, create } from '@spfn/core/db';\nimport { logger } from '@spfn/core/logger';\nimport { users } from '@/server/entities';\nimport { hashPassword } from '@/server/helpers';\nimport { getRoleByName } from '@/server/services/role.service';\n\nconst authLogger = logger.child('@spfn/auth');\n\n/**\n * Admin account configuration\n */\ninterface AdminAccountConfig\n{\n email: string;\n password: string;\n role?: string; // Role name (e.g., 'user', 'admin', 'superadmin')\n phone?: string;\n passwordChangeRequired?: boolean;\n}\n\n/**\n * Parse admin accounts from environment variables\n *\n * Supports three formats (in priority order):\n *\n * 1. JSON format (ADMIN_ACCOUNTS):\n * ```\n * ADMIN_ACCOUNTS='[{\"email\":\"admin@example.com\",\"password\":\"pass\",\"role\":\"superadmin\"}]'\n * ```\n *\n * 2. Comma-separated format (ADMIN_EMAILS + ADMIN_PASSWORDS + ADMIN_ROLES):\n * ```\n * ADMIN_EMAILS=admin@example.com,user@example.com\n * ADMIN_PASSWORDS=admin-pass,user-pass\n * ADMIN_ROLES=superadmin,user\n * ```\n *\n * 3. Single account format (legacy, ADMIN_EMAIL + ADMIN_PASSWORD):\n * ```\n * ADMIN_EMAIL=admin@example.com\n * ADMIN_PASSWORD=admin-password\n * ```\n *\n * @returns Array of admin account configurations\n */\nfunction parseAdminAccounts(): AdminAccountConfig[]\n{\n const accounts: AdminAccountConfig[] = [];\n\n // Method 1: JSON format (highest priority)\n if (process.env.SPFN_AUTH_ADMIN_ACCOUNTS || process.env.ADMIN_ACCOUNTS)\n {\n try\n {\n const accountsJson =\n process.env.SPFN_AUTH_ADMIN_ACCOUNTS || // New prefixed version (recommended)\n process.env.ADMIN_ACCOUNTS; // Legacy fallback\n\n const parsed = JSON.parse(accountsJson!);\n\n if (!Array.isArray(parsed))\n {\n authLogger.error('❌ SPFN_AUTH_ADMIN_ACCOUNTS must be an array');\n return accounts;\n }\n\n for (const item of parsed)\n {\n if (!item.email || !item.password)\n {\n authLogger.warn('⚠️ Skipping account: missing email or password');\n continue;\n }\n\n accounts.push({\n email: item.email,\n password: item.password,\n role: item.role || 'user',\n phone: item.phone,\n passwordChangeRequired: item.passwordChangeRequired !== false, // Default: true\n });\n }\n\n return accounts;\n }\n catch (error)\n {\n const err = error as Error;\n authLogger.error('❌ Failed to parse SPFN_AUTH_ADMIN_ACCOUNTS:', err);\n return accounts;\n }\n }\n\n // Method 2: Comma-separated format\n const adminEmails =\n process.env.SPFN_AUTH_ADMIN_EMAILS || // New prefixed version (recommended)\n process.env.ADMIN_EMAILS; // Legacy fallback\n\n if (adminEmails)\n {\n const emails = adminEmails.split(',').map(s => s.trim());\n const passwords = (\n process.env.SPFN_AUTH_ADMIN_PASSWORDS || // New prefixed version (recommended)\n process.env.ADMIN_PASSWORDS || // Legacy fallback\n ''\n ).split(',').map(s => s.trim());\n const roles = (\n process.env.SPFN_AUTH_ADMIN_ROLES || // New prefixed version (recommended)\n process.env.ADMIN_ROLES || // Legacy fallback\n ''\n ).split(',').map(s => s.trim());\n\n // Validate lengths match\n if (passwords.length !== emails.length)\n {\n authLogger.error('❌ SPFN_AUTH_ADMIN_EMAILS and SPFN_AUTH_ADMIN_PASSWORDS length mismatch');\n return accounts;\n }\n\n for (let i = 0; i < emails.length; i++)\n {\n const email = emails[i];\n const password = passwords[i];\n const role = roles[i] || 'user';\n\n if (!email || !password)\n {\n authLogger.warn(`⚠️ Skipping account ${i + 1}: missing email or password`);\n continue;\n }\n\n accounts.push({\n email,\n password,\n role,\n passwordChangeRequired: true,\n });\n }\n\n return accounts;\n }\n\n // Method 3: Single account (legacy format)\n const adminEmail =\n process.env.SPFN_AUTH_ADMIN_EMAIL || // New prefixed version (recommended)\n process.env.ADMIN_EMAIL; // Legacy fallback\n\n const adminPassword =\n process.env.SPFN_AUTH_ADMIN_PASSWORD || // New prefixed version (recommended)\n process.env.ADMIN_PASSWORD; // Legacy fallback\n\n if (adminEmail && adminPassword)\n {\n accounts.push({\n email: adminEmail,\n password: adminPassword,\n role: 'superadmin',\n passwordChangeRequired: true,\n });\n }\n\n return accounts;\n}\n\n/**\n * Ensure admin accounts exist from environment variables\n *\n * Supports multiple admin account creation via three formats:\n * 1. JSON format (SPFN_AUTH_ADMIN_ACCOUNTS)\n * 2. Comma-separated format (SPFN_AUTH_ADMIN_EMAILS + SPFN_AUTH_ADMIN_PASSWORDS + SPFN_AUTH_ADMIN_ROLES)\n * 3. Single account format (SPFN_AUTH_ADMIN_EMAIL + SPFN_AUTH_ADMIN_PASSWORD) - legacy\n *\n * Default behavior for created accounts:\n * - emailVerifiedAt: current timestamp (auto-verified)\n * - passwordChangeRequired: true (must change on first login)\n * - status: 'active'\n *\n * @example\n * ```typescript\n * // In your server startup code:\n * import { ensureAdminExists } from '@spfn/auth/server';\n *\n * await ensureAdminExists();\n * ```\n */\nexport async function ensureAdminExists(): Promise<void>\n{\n const accounts = parseAdminAccounts();\n\n // Skip if no accounts configured\n if (accounts.length === 0)\n {\n return;\n }\n\n authLogger.info(`Creating ${accounts.length} admin account(s)...`);\n\n let created = 0;\n let skipped = 0;\n let failed = 0;\n\n for (const account of accounts)\n {\n try\n {\n // Check if account already exists\n const existing = await findOne(users, { email: account.email });\n\n if (existing)\n {\n authLogger.info(`⚠️ Account already exists: ${account.email} (skipped)`);\n skipped++;\n continue;\n }\n\n // Get role ID from role name\n const roleName = account.role || 'user';\n const role = await getRoleByName(roleName);\n\n if (!role)\n {\n authLogger.error(`❌ Role '${roleName}' not found for ${account.email}. Run initializeAuth() first.`);\n failed++;\n continue;\n }\n\n // Hash password\n const passwordHash = await hashPassword(account.password);\n\n // Create admin account\n await create(users, {\n email: account.email,\n phone: account.phone || null,\n passwordHash,\n roleId: role.id,\n emailVerifiedAt: new Date(), // Auto-verify admin\n passwordChangeRequired: account.passwordChangeRequired !== false,\n status: 'active',\n });\n\n authLogger.info(`✅ Admin account created: ${account.email} (${roleName})`);\n created++;\n }\n catch (error)\n {\n const err = error as Error;\n authLogger.error(`❌ Failed to create account ${account.email}:`, err);\n failed++;\n }\n }\n\n // Summary\n authLogger.info(`📊 Summary: ${created} created, ${skipped} skipped, ${failed} failed`);\n\n if (created > 0)\n {\n authLogger.info('⚠️ Please change passwords on first login!');\n }\n}"],"mappings":"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","names":["text","boolean","index","id","timestamps","text","timestamp","id","timestamps","text","timestamp","boolean","index","id","foreignKey","text","timestamp","index","id","timestamps","text","timestamp","bigint","index","id","timestamps","text","boolean","index","id","timestamps","bigint","index","id","timestamps","bigint","boolean","text","timestamp","index","unique","id","timestamps","getDatabase","eq","and","findOne","create","ValidationError","jwt","create","getDatabase","eq","and","create","getDatabase","findOne","ValidationError","getRoleByName","create","updateOne","getDatabase","eq","and","getDatabase","eq","and","getDatabase","eq","and","getDatabase","eq","and","getDatabase","eq","and","sql","crypto","getDatabase","eq","and","userPublicKeys","sql","id","findOne","getDatabase","UnauthorizedError","eq","and","decodeToken","getDatabase","findOne","ForbiddenError","ForbiddenError","findOne","create","logger","authLogger","logger","roles","findOne","create"]}
1
+ {"version":3,"sources":["../src/server/entities/schema.ts","../src/server/entities/roles.ts","../src/server/entities/users.ts","../src/server/entities/user-social-accounts.ts","../src/server/entities/user-public-keys.ts","../src/server/entities/verification-codes.ts","../src/server/entities/invitations.ts","../src/server/entities/permissions.ts","../src/server/entities/role-permissions.ts","../src/server/entities/user-permissions.ts","../src/server/entities/index.ts","../src/server/helpers/jwt.ts","../src/server/services/role.service.ts","../src/server/rbac/builtin.ts","../src/server/services/auth.service.ts","../src/server/helpers/password.ts","../src/server/helpers/index.ts","../src/server/helpers/verification.ts","../src/server/helpers/context.ts","../src/server/errors/auth-errors.ts","../src/server/services/key.service.ts","../src/server/services/user.service.ts","../src/server/services/verification.service.ts","../src/server/services/me.service.ts","../src/server/services/rbac.service.ts","../src/lib/config.ts","../src/server/services/permission.service.ts","../src/server/services/index.ts","../src/server/services/invitation.service.ts","../src/server/middleware/authenticate.ts","../src/server/middleware/require-permission.ts","../src/server/middleware/require-role.ts","../src/server/setup.ts"],"sourcesContent":["/**\n * @spfn/auth - Database Schema Definition\n *\n * Defines the 'spfn_auth' PostgreSQL schema for all auth-related tables\n */\n\nimport { createFunctionSchema } from '@spfn/core/db';\n\n/**\n * Auth schema for all authentication and authorization tables\n * Tables: users, roles, permissions, user_invitations, etc.\n */\nexport const authSchema = createFunctionSchema('@spfn/auth');","/**\n * @spfn/auth - Roles Entity\n *\n * Role-based access control (RBAC) roles table\n *\n * Features:\n * - Built-in roles (user, admin, superadmin) - cannot be deleted\n * - System roles (preset roles) - can be deactivated\n * - Custom roles (runtime created) - fully manageable\n * - Priority-based hierarchy\n */\n\nimport { text, boolean, integer, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { authSchema } from './schema';\n\nexport const roles = authSchema.table('roles',\n {\n // Primary key\n id: id(),\n\n // Role identifier (used in code, e.g., 'admin', 'editor')\n // Must be unique, lowercase, kebab-case recommended\n name: text('name').notNull().unique(),\n\n // Display name for UI (e.g., 'Administrator', 'Content Editor')\n displayName: text('display_name').notNull(),\n\n // Role description\n description: text('description'),\n\n // Built-in role flag\n // true: Core package roles (user, admin, superadmin) - cannot be deleted\n // false: Custom or preset roles - can be deleted\n isBuiltin: boolean('is_builtin').notNull().default(false),\n\n // System role flag\n // true: Defined in code (builtin or preset) - deletion restricted\n // false: Runtime created custom role - fully manageable\n isSystem: boolean('is_system').notNull().default(false),\n\n // Active status\n // false: Deactivated role (users cannot be assigned)\n isActive: boolean('is_active').notNull().default(true),\n\n // Priority level (higher = more privileged)\n // superadmin: 100, admin: 80, user: 10\n // Used for role hierarchy and conflict resolution\n priority: integer('priority').notNull().default(10),\n\n ...timestamps(),\n },\n (table) => [\n index('roles_name_idx').on(table.name),\n index('roles_is_system_idx').on(table.isSystem),\n index('roles_is_active_idx').on(table.isActive),\n index('roles_is_builtin_idx').on(table.isBuiltin),\n index('roles_priority_idx').on(table.priority),\n ]\n);\n\n// Type exports\nexport type RoleEntity = typeof roles.$inferSelect;\nexport type NewRoleEntity = typeof roles.$inferInsert;\n\n// Legacy alias for backward compatibility\nexport type Role = RoleEntity;\nexport type NewRole = NewRoleEntity;","/**\n * @spfn/auth - Users Entity\n *\n * Main user table supporting multiple authentication methods\n *\n * Features:\n * - Email or phone-based registration\n * - Password authentication (bcrypt)\n * - OAuth support (nullable passwordHash)\n * - Role-based access control (RBAC)\n * - Account status management\n * - Email/phone verification\n */\n\nimport { text, timestamp, check, boolean, bigint, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { sql } from 'drizzle-orm';\nimport { roles } from './roles';\nimport { authSchema } from './schema';\n\nexport const users = authSchema.table('users',\n {\n // Identity\n id: id(),\n\n // Email address (unique identifier)\n // Used for: login, password reset, notifications\n email: text('email').unique(),\n\n // Phone number in E.164 international format\n // Format: +[country code][number] (e.g., +821012345678)\n // Used for: SMS login, 2FA, notifications\n phone: text('phone').unique(),\n\n // Authentication\n // Bcrypt password hash ($2b$10$[salt][hash], 60 chars)\n // Nullable to support OAuth-only accounts\n passwordHash: text('password_hash'),\n\n // Force password change on next login\n // Use cases: initial setup, security breach, policy violation\n passwordChangeRequired: boolean('password_change_required').notNull().default(false),\n\n // Authorization (Role-Based Access Control)\n // Foreign key to roles table\n // References built-in roles: user (default), admin, superadmin\n // Can also reference custom roles created at runtime\n roleId: bigint('role_id', { mode: 'number' })\n .references(() => roles.id)\n .notNull(),\n\n // Account status\n // - active: Normal operation (default)\n // - inactive: Deactivated (user request, dormant)\n // - suspended: Locked (security incident, ToS violation)\n status: text(\n 'status',\n {\n enum: ['active', 'inactive', 'suspended']\n }\n ).notNull().default('active'),\n\n // Verification timestamps\n // null = unverified, timestamp = verified at this time\n // Email verification (via verification code or magic link)\n emailVerifiedAt: timestamp('email_verified_at', { withTimezone: true }),\n\n // Phone verification (via SMS OTP)\n phoneVerifiedAt: timestamp('phone_verified_at', { withTimezone: true }),\n\n // Metadata\n // Last successful login timestamp\n // Used for: security auditing, dormant account detection\n lastLoginAt: timestamp('last_login_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Database constraints\n // Ensure at least one identifier exists (email OR phone)\n check(\n 'email_or_phone_check',\n sql`${table.email} IS NOT NULL OR ${table.phone} IS NOT NULL`\n ),\n\n // Indexes for query optimization\n index('users_email_idx').on(table.email),\n index('users_phone_idx').on(table.phone),\n index('users_status_idx').on(table.status),\n index('users_role_id_idx').on(table.roleId),\n ]\n);\n\n// Type exports\nexport type User = typeof users.$inferSelect;\nexport type NewUser = typeof users.$inferInsert;\nexport type UserStatus = 'active' | 'inactive' | 'suspended';\n\n// Helper type with computed verification status\nexport type UserWithVerification = User &\n{\n isEmailVerified: boolean;\n isPhoneVerified: boolean;\n};","/**\n * @spfn/auth - User Social Accounts Entity\n *\n * Stores OAuth connections for social login providers\n */\n\nimport { text, timestamp, uniqueIndex } from 'drizzle-orm/pg-core';\nimport { id, timestamps, foreignKey } from '@spfn/core/db';\nimport { users } from './users';\nimport { authSchema } from './schema';\n\nexport const userSocialAccounts = authSchema.table('user_social_accounts',\n {\n id: id(),\n\n // Foreign key to users\n userId: foreignKey('user', () => users.id),\n\n // Provider info\n provider: text(\n 'provider',\n {\n enum: ['google', 'github', 'kakao', 'naver']\n }\n ).notNull(),\n\n providerUserId: text('provider_user_id').notNull(),\n providerEmail: text('provider_email'),\n\n // OAuth tokens (encrypted in production)\n accessToken: text('access_token'),\n refreshToken: text('refresh_token'),\n tokenExpiresAt: timestamp('token_expires_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Unique constraint: one provider account per provider\n uniqueIndex('provider_user_unique_idx')\n .on(table.provider, table.providerUserId),\n ]\n);\n\n// Type exports\nexport type UserSocialAccount = typeof userSocialAccounts.$inferSelect;\nexport type NewUserSocialAccount = typeof userSocialAccounts.$inferInsert;\nexport type SocialProvider = 'google' | 'github' | 'kakao' | 'naver';","/**\n * @spfn/auth - User Public Keys Entity\n *\n * Stores client-generated public keys for JWT verification\n * Supports key rotation and multi-key management per user\n */\n\nimport { text, timestamp, boolean, index } from 'drizzle-orm/pg-core';\nimport { id, foreignKey } from '@spfn/core/db';\nimport { users } from './users';\nimport { authSchema } from './schema';\n\n/**\n * User Public Keys Table\n * Each user can have multiple public keys (for rotation)\n */\nexport const userPublicKeys = authSchema.table(\n 'user_public_keys',\n {\n id: id(),\n\n // User reference\n userId: foreignKey('user', () => users.id),\n\n // Key identification (client-generated UUID)\n keyId: text('key_id').notNull().unique(),\n\n // Public key in Base64-encoded DER format (SPKI)\n publicKey: text('public_key').notNull(),\n\n // Algorithm used (ES256 recommended, RS256 fallback)\n algorithm: text('algorithm', {\n enum: ['ES256', 'RS256']\n }).notNull().default('ES256'),\n\n // Key fingerprint (SHA-256 hash for quick identification)\n fingerprint: text('fingerprint').notNull(),\n\n // Key status\n isActive: boolean('is_active').notNull().default(true),\n\n // Timestamps\n createdAt: timestamp('created_at', { mode: 'date', withTimezone: true })\n .notNull()\n .defaultNow(),\n\n lastUsedAt: timestamp('last_used_at', { mode: 'date', withTimezone: true }),\n\n expiresAt: timestamp('expires_at', { mode: 'date', withTimezone: true }),\n\n // Revocation\n revokedAt: timestamp('revoked_at', { mode: 'date', withTimezone: true }),\n revokedReason: text('revoked_reason'),\n },\n (table) => [\n index('user_public_keys_user_id_idx').on(table.userId),\n index('user_public_keys_key_id_idx').on(table.keyId),\n index('user_public_keys_active_idx').on(table.isActive),\n index('user_public_keys_fingerprint_idx').on(table.fingerprint),\n ]\n);\n\nexport type UserPublicKey = typeof userPublicKeys.$inferSelect;\nexport type NewUserPublicKey = typeof userPublicKeys.$inferInsert;","/**\n * @spfn/auth - Verification Codes Entity\n *\n * Stores verification codes for email and phone verification\n * Codes expire after a configurable time period\n */\n\nimport { text, timestamp, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { authSchema } from './schema';\n\nexport const verificationCodes = authSchema.table('verification_codes',\n {\n id: id(),\n\n // Target (email or phone)\n target: text('target').notNull(), // Email address or E.164 phone number\n targetType: text(\n 'target_type',\n {\n enum: ['email', 'phone']\n }\n ).notNull(),\n\n // Code\n code: text('code').notNull(), // 6-digit code by default (configurable)\n\n // Purpose\n purpose: text(\n 'purpose',\n {\n enum: ['registration', 'login', 'password_reset', 'email_change', 'phone_change']\n }\n ).notNull(),\n\n // Expiry\n expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),\n\n // Usage tracking\n usedAt: timestamp('used_at', { withTimezone: true }),\n attempts: text('attempts').notNull().default('0'), // Track failed verification attempts\n\n ...timestamps(),\n },\n (table) => [\n // Index for quick lookup by target and purpose\n index('target_purpose_idx')\n .on(table.target, table.purpose, table.expiresAt),\n ]\n);\n\n// Type exports\nexport type VerificationCode = typeof verificationCodes.$inferSelect;\nexport type NewVerificationCode = typeof verificationCodes.$inferInsert;\nexport type VerificationTargetType = 'email' | 'phone';\nexport type VerificationPurpose = 'registration' | 'login' | 'password_reset' | 'email_change' | 'phone_change';","/**\n * @spfn/auth - User Invitations Entity\n *\n * Invitation system for invite-only user registration\n *\n * Features:\n * - Email-based invitations with unique tokens\n * - Role assignment at invitation time\n * - Expiration and status tracking\n * - Audit trail (who invited whom, when accepted)\n * - Metadata support for custom data\n */\n\nimport { text, timestamp, bigint, index, jsonb } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { roles } from './roles';\nimport { users } from './users';\nimport { authSchema } from './schema';\n\nexport const invitations = authSchema.table('user_invitations',\n {\n // Primary key\n id: id(),\n\n // Target email address for the invitation\n // Will become the user's email upon acceptance\n email: text('email').notNull(),\n\n // Unique invitation token (UUID v4)\n // Used in invitation URL: /auth/invite/{token}\n // Single-use token that expires after acceptance\n token: text('token').notNull().unique(),\n\n // Role to be assigned when invitation is accepted\n // Foreign key to roles table\n roleId: bigint('role_id', { mode: 'number' })\n .references(() => roles.id)\n .notNull(),\n\n // User who created this invitation\n // Foreign key to users table\n // Used for: audit trail, permission checks\n invitedBy: bigint('invited_by', { mode: 'number' })\n .references(() => users.id)\n .notNull(),\n\n // Invitation status\n // - pending: Invitation sent, awaiting acceptance\n // - accepted: User accepted and account created\n // - expired: Invitation expired (automatic)\n // - cancelled: Invitation cancelled by admin\n status: text(\n 'status',\n {\n enum: ['pending', 'accepted', 'expired', 'cancelled']\n }\n ).notNull().default('pending'),\n\n // Expiration timestamp (default: 7 days from creation)\n // Invitation cannot be accepted after this time\n // Background job should update status to 'expired'\n expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),\n\n // Timestamp when invitation was accepted\n // null = not yet accepted\n // Used for: audit trail, analytics\n acceptedAt: timestamp('accepted_at', { withTimezone: true }),\n\n // Timestamp when invitation was cancelled\n // null = not cancelled\n // Used for: audit trail\n cancelledAt: timestamp('cancelled_at', { withTimezone: true }),\n\n // Additional metadata (JSONB)\n // Use cases:\n // - Custom welcome message\n // - Onboarding instructions\n // - Team/department assignment\n // - Custom fields for app-specific data\n // Example: { message: \"Welcome!\", department: \"Engineering\" }\n metadata: jsonb('metadata'),\n\n ...timestamps(),\n },\n (table) => [\n // Indexes for query optimization\n index('invitations_token_idx').on(table.token),\n index('invitations_email_idx').on(table.email),\n index('invitations_status_idx').on(table.status),\n index('invitations_invited_by_idx').on(table.invitedBy),\n index('invitations_expires_at_idx').on(table.expiresAt), // For cleanup jobs\n index('invitations_role_id_idx').on(table.roleId),\n ]\n);\n\n// Type exports\nexport type Invitation = typeof invitations.$inferSelect;\nexport type NewInvitation = typeof invitations.$inferInsert;\nexport type InvitationStatus = 'pending' | 'accepted' | 'expired' | 'cancelled';\n\n// Helper type with joined data\nexport type InvitationWithDetails = Invitation &\n{\n role: {\n id: number;\n name: string;\n displayName: string;\n };\n inviter: {\n id: number;\n email: string | null;\n };\n};","/**\n * @spfn/auth - Permissions Entity\n *\n * Granular permissions for RBAC system\n *\n * Features:\n * - Built-in permissions (auth:*, user:*, rbac:*) - required for package\n * - System permissions (preset permissions) - optional\n * - Custom permissions (app-specific) - defined by developers\n * - Category grouping for organization\n */\n\nimport { text, boolean, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { authSchema } from './schema';\n\nexport const permissions = authSchema.table('permissions',\n {\n // Primary key\n id: id(),\n\n // Permission identifier (e.g., 'user:delete', 'post:publish')\n // Format: resource:action or namespace:resource:action\n // Must be unique\n name: text('name').notNull().unique(),\n\n // Display name for UI\n displayName: text('display_name').notNull(),\n\n // Permission description\n description: text('description'),\n\n // Category for grouping (e.g., 'user', 'post', 'admin', 'system')\n category: text('category'),\n\n // Built-in permission flag\n // true: Core package permissions - cannot be deleted\n // false: Custom or preset permissions\n isBuiltin: boolean('is_builtin').notNull().default(false),\n\n // System permission flag\n // true: Defined in code (builtin or preset)\n // false: Runtime created custom permission\n isSystem: boolean('is_system').notNull().default(false),\n\n // Active status\n // false: Deactivated permission (not enforced)\n isActive: boolean('is_active').notNull().default(true),\n\n ...timestamps(),\n },\n (table) => [\n index('permissions_name_idx').on(table.name),\n index('permissions_category_idx').on(table.category),\n index('permissions_is_system_idx').on(table.isSystem),\n index('permissions_is_active_idx').on(table.isActive),\n index('permissions_is_builtin_idx').on(table.isBuiltin),\n ]\n);\n\n// Type exports\nexport type PermissionEntity = typeof permissions.$inferSelect;\nexport type NewPermissionEntity = typeof permissions.$inferInsert;\n\n// Legacy alias for backward compatibility\nexport type Permission = PermissionEntity;\nexport type NewPermission = NewPermissionEntity;","/**\n * @spfn/auth - Role-Permissions Mapping Entity\n *\n * Many-to-many relationship between roles and permissions\n *\n * Usage:\n * - Defines which permissions each role has\n * - Cascade delete when role or permission is deleted\n */\n\nimport { bigint, index, unique } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { roles } from './roles';\nimport { permissions } from './permissions';\nimport { authSchema } from './schema';\n\nexport const rolePermissions = authSchema.table('role_permissions',\n {\n // Primary key\n id: id(),\n\n // Foreign key to roles table\n roleId: bigint('role_id', { mode: 'number' })\n .notNull()\n .references(() => roles.id, { onDelete: 'cascade' }),\n\n // Foreign key to permissions table\n permissionId: bigint('permission_id', { mode: 'number' })\n .notNull()\n .references(() => permissions.id, { onDelete: 'cascade' }),\n\n ...timestamps(),\n },\n (table) => [\n // Indexes for query performance\n index('role_permissions_role_id_idx').on(table.roleId),\n index('role_permissions_permission_id_idx').on(table.permissionId),\n\n // Unique constraint: one role-permission pair only\n unique('role_permissions_unique').on(table.roleId, table.permissionId),\n ]\n);\n\n// Type exports\nexport type RolePermission = typeof rolePermissions.$inferSelect;\nexport type NewRolePermission = typeof rolePermissions.$inferInsert;","/**\n * @spfn/auth - User-Permissions Override Entity\n *\n * Per-user permission grants/revocations\n *\n * Features:\n * - Grant additional permissions to specific users\n * - Revoke role-inherited permissions from specific users\n * - Temporary permissions with expiration\n * - Audit trail with reason field\n *\n * Priority:\n * User permissions override role permissions\n */\n\nimport { bigint, boolean, text, timestamp, index, unique } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { users } from './users';\nimport { permissions } from './permissions';\nimport { authSchema } from './schema';\n\nexport const userPermissions = authSchema.table('user_permissions',\n {\n // Primary key\n id: id(),\n\n // Foreign key to users table\n userId: bigint('user_id', { mode: 'number' })\n .notNull()\n .references(() => users.id, { onDelete: 'cascade' }),\n\n // Foreign key to permissions table\n permissionId: bigint('permission_id', { mode: 'number' })\n .notNull()\n .references(() => permissions.id, { onDelete: 'cascade' }),\n\n // Grant or revoke\n // true: Grant this permission (even if role doesn't have it)\n // false: Revoke this permission (even if role has it)\n granted: boolean('granted').notNull().default(true),\n\n // Reason for grant/revocation (audit trail)\n reason: text('reason'),\n\n // Expiration timestamp (optional)\n // null: Permanent override\n // timestamp: Permission expires at this time\n expiresAt: timestamp('expires_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Indexes for query performance\n index('user_permissions_user_id_idx').on(table.userId),\n index('user_permissions_permission_id_idx').on(table.permissionId),\n index('user_permissions_expires_at_idx').on(table.expiresAt),\n\n // Unique constraint: one user-permission pair only\n unique('user_permissions_unique').on(table.userId, table.permissionId),\n ]\n);\n\n// Type exports\nexport type UserPermission = typeof userPermissions.$inferSelect;\nexport type NewUserPermission = typeof userPermissions.$inferInsert;","/**\n * @spfn/auth - Database entities\n *\n * Core authentication and authorization entities\n */\n\n// Schema definition\nexport * from './schema';\n\n// User entities\nexport * from './users';\nexport * from './user-social-accounts';\nexport * from './user-public-keys';\nexport * from './verification-codes';\nexport * from './invitations';\n\n// RBAC entities\nexport * from './roles';\nexport * from './permissions';\nexport * from './role-permissions';\nexport * from './user-permissions';","/**\n * @spfn/auth - JWT Helpers\n *\n * JWT token generation and verification\n * Supports both server-signed (legacy) and client-signed (asymmetric) tokens\n *\n * Architecture:\n * - Legacy: Server signs/verifies with SPFN_AUTH_JWT_SECRET (symmetric HMAC)\n * - New: Client signs with privateKey, server verifies with publicKey (asymmetric)\n */\n\nimport jwt, { type SignOptions } from 'jsonwebtoken';\nimport crypto from 'crypto';\nimport type { SessionPayload } from '@/lib/types/api';\n\nconst JWT_SECRET =\n process.env.SPFN_AUTH_JWT_SECRET || // New prefixed version (recommended)\n process.env.JWT_SECRET || // Legacy fallback\n 'dev-secret-key-change-in-production';\n\nconst JWT_EXPIRES_IN =\n process.env.SPFN_AUTH_JWT_EXPIRES_IN || // New prefixed version (recommended)\n process.env.JWT_EXPIRES_IN || // Legacy fallback\n '7d';\n\nexport interface TokenPayload extends SessionPayload\n{\n exp?: number;\n iat?: number;\n iss?: string;\n keyId?: string;\n timestamp?: number;\n [key: string]: any;\n}\n\n/**\n * Generate a JWT token (legacy server-signed)\n *\n * @deprecated Use client-side signing with private keys instead\n * This method uses symmetric HMAC which requires sharing the secret\n *\n * @param payload - Token payload\n * @returns JWT token string\n */\nexport function generateToken(payload: SessionPayload): string\n{\n return jwt.sign(payload, JWT_SECRET, {\n expiresIn: JWT_EXPIRES_IN,\n } as SignOptions);\n}\n\n/**\n * Verify and decode a JWT token (legacy server-signed)\n *\n * @deprecated Use verifyClientToken for client-signed tokens\n * This method uses symmetric HMAC verification\n *\n * @param token - JWT token to verify\n * @returns Decoded payload\n * @throws Error if verification fails\n */\nexport function verifyToken(token: string): TokenPayload\n{\n return jwt.verify(token, JWT_SECRET) as TokenPayload;\n}\n\n/**\n * Verify client-signed JWT token with public key (DER format)\n *\n * Flow:\n * 1. Decode Base64 DER to Buffer\n * 2. Create KeyObject from DER\n * 3. Verify JWT signature with public key\n * 4. Validate issuer claim\n *\n * @param token - JWT token signed by client's private key\n * @param publicKeyB64 - Base64 encoded DER public key (SPKI format)\n * @param algorithm - Algorithm used for signing (ES256 or RS256)\n * @returns Decoded token payload\n * @throws Error if verification fails\n *\n * @example\n * ```typescript\n * const payload = verifyClientToken(\n * token,\n * 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...',\n * 'ES256'\n * );\n * ```\n */\nexport function verifyClientToken(\n token: string,\n publicKeyB64: string,\n algorithm: 'ES256' | 'RS256'\n): TokenPayload\n{\n try\n {\n // Convert Base64 DER to key object\n const publicKeyDER = Buffer.from(publicKeyB64, 'base64');\n const publicKeyObject = crypto.createPublicKey({\n key: publicKeyDER,\n format: 'der',\n type: 'spki',\n });\n\n const decoded = jwt.verify(token, publicKeyObject, {\n algorithms: [algorithm], // Prevent algorithm confusion attacks\n issuer: 'spfn-client', // Validate token issuer\n });\n\n // jwt.verify can return string, but we expect object payload\n if (typeof decoded === 'string')\n {\n throw new Error('Invalid token format: expected object payload');\n }\n\n return decoded as TokenPayload;\n }\n catch (error)\n {\n if (error instanceof jwt.TokenExpiredError)\n {\n throw new Error('Token has expired');\n }\n\n if (error instanceof jwt.JsonWebTokenError)\n {\n throw new Error('Invalid token signature');\n }\n\n throw new Error(`Token verification failed: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n\n/**\n * Decode a JWT token without verification (for debugging)\n *\n * WARNING: Does not verify signature! Only use for debugging/logging.\n * Never trust decoded data without verification.\n *\n * @param token - JWT token to decode\n * @returns Decoded payload or null if invalid\n */\nexport function decodeToken(token: string): TokenPayload | null\n{\n try\n {\n return jwt.decode(token) as TokenPayload | null;\n }\n catch\n {\n return null;\n }\n}\n\n/**\n * Verify public key fingerprint matches\n *\n * Used during registration/login to ensure the public key wasn't tampered with\n * during transmission.\n *\n * Security:\n * - Client sends: publicKey + fingerprint\n * - Server calculates: SHA-256(publicKey)\n * - Server compares: calculated === received\n *\n * @param publicKeyB64 - Base64 encoded DER public key\n * @param expectedFingerprint - SHA-256 hex fingerprint (64 chars)\n * @returns True if fingerprint matches\n *\n * @example\n * ```typescript\n * const isValid = verifyKeyFingerprint(\n * publicKey,\n * 'a1b2c3d4e5f6...' // 64-char hex string\n * );\n * if (!isValid) {\n * throw new Error('Public key fingerprint mismatch');\n * }\n * ```\n */\nexport function verifyKeyFingerprint(\n publicKeyB64: string,\n expectedFingerprint: string\n): boolean\n{\n try\n {\n const publicKeyDER = Buffer.from(publicKeyB64, 'base64');\n const fingerprint = crypto\n .createHash('sha256')\n .update(publicKeyDER)\n .digest('hex');\n\n return fingerprint === expectedFingerprint;\n }\n catch (error)\n {\n console.error('Failed to verify key fingerprint:', error);\n return false;\n }\n}","/**\n * @spfn/auth - Role Service\n *\n * Role management functions for runtime role creation and modification\n */\n\nimport { getDatabase } from '@spfn/core/db';\nimport { roles, permissions, rolePermissions } from '@/server/entities';\nimport type { Role } from '@/server/entities/roles';\nimport { eq, and } from 'drizzle-orm';\n\n/**\n * Create a new custom role\n *\n * @param data - Role configuration\n * @returns Created role\n * @throws Error if role name already exists\n *\n * @example\n * ```typescript\n * const role = await createRole({\n * name: 'content-creator',\n * displayName: 'Content Creator',\n * description: 'Can create and publish content',\n * priority: 20,\n * permissionIds: [1n, 2n, 3n],\n * });\n * ```\n */\nexport async function createRole(data: {\n name: string;\n displayName: string;\n description?: string;\n priority?: number;\n permissionIds?: number[];\n}): Promise<Role>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n // Check for duplicate name\n const existing = await db\n .select()\n .from(roles)\n .where(eq(roles.name, data.name))\n .limit(1);\n\n if (existing.length > 0)\n {\n throw new Error(`Role with name '${data.name}' already exists`);\n }\n\n // Create role\n const [newRole] = await db\n .insert(roles)\n .values({\n name: data.name,\n displayName: data.displayName,\n description: data.description,\n priority: data.priority ?? 10,\n isSystem: false, // Custom roles are never system roles\n isBuiltin: false,\n })\n .returning();\n\n // Assign permissions if provided\n if (data.permissionIds && data.permissionIds.length > 0)\n {\n const mappings = data.permissionIds.map(permId => ({\n roleId: newRole.id,\n permissionId: Number(permId),\n }));\n\n await db.insert(rolePermissions).values(mappings);\n }\n\n console.log(`[Auth] ✅ Created custom role: ${data.name}`);\n\n return newRole;\n}\n\n/**\n * Update an existing role\n *\n * @param roleId - Role ID\n * @param data - Update data\n * @returns Updated role\n * @throws Error if role is built-in (cannot modify)\n *\n * @example\n * ```typescript\n * await updateRole(1n, {\n * displayName: 'Senior Content Creator',\n * priority: 25,\n * });\n * ```\n */\nexport async function updateRole(\n roleId: number,\n data: {\n displayName?: string;\n description?: string;\n priority?: number;\n isActive?: boolean;\n }\n): Promise<Role>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const roleIdNum = Number(roleId);\n\n // Get role\n const [role] = await db\n .select()\n .from(roles)\n .where(eq(roles.id, roleIdNum))\n .limit(1);\n\n if (!role)\n {\n throw new Error('Role not found');\n }\n\n // Cannot modify built-in role priority\n if (role.isBuiltin && data.priority !== undefined)\n {\n throw new Error('Cannot modify priority of built-in roles');\n }\n\n // Update role\n const [updated] = await db\n .update(roles)\n .set(data)\n .where(eq(roles.id, roleIdNum))\n .returning();\n\n return updated;\n}\n\n/**\n * Delete a role\n *\n * @param roleId - Role ID\n * @throws Error if role is built-in or system role\n *\n * @example\n * ```typescript\n * await deleteRole(5n); // Delete custom role\n * ```\n */\nexport async function deleteRole(roleId: number): Promise<void>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const roleIdNum = Number(roleId);\n\n // Get role\n const [role] = await db\n .select()\n .from(roles)\n .where(eq(roles.id, roleIdNum))\n .limit(1);\n\n if (!role)\n {\n throw new Error('Role not found');\n }\n\n // Cannot delete built-in roles\n if (role.isBuiltin)\n {\n throw new Error(`Cannot delete built-in role: ${role.name}`);\n }\n\n // Cannot delete system roles (optional protection)\n if (role.isSystem)\n {\n throw new Error(`Cannot delete system role: ${role.name}. Deactivate it instead.`);\n }\n\n // Delete role (cascade will remove role_permissions)\n await db.delete(roles).where(eq(roles.id, roleIdNum));\n\n console.log(`[Auth] 🗑️ Deleted role: ${role.name}`);\n}\n\n/**\n * Add permission to role\n *\n * @param roleId - Role ID\n * @param permissionId - Permission ID\n *\n * @example\n * ```typescript\n * await addPermissionToRole(1n, 5n);\n * ```\n */\nexport async function addPermissionToRole(roleId: number, permissionId: number): Promise<void>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const roleIdNum = Number(roleId);\n const permissionIdNum = Number(permissionId);\n\n // Check if mapping already exists\n const existing = await db\n .select()\n .from(rolePermissions)\n .where(\n and(\n eq(rolePermissions.roleId, roleIdNum),\n eq(rolePermissions.permissionId, permissionIdNum)\n )\n )\n .limit(1);\n\n if (existing.length > 0)\n {\n return; // Already exists\n }\n\n // Create mapping\n await db.insert(rolePermissions).values({\n roleId: roleIdNum,\n permissionId: permissionIdNum,\n });\n}\n\n/**\n * Remove permission from role\n *\n * @param roleId - Role ID\n * @param permissionId - Permission ID\n *\n * @example\n * ```typescript\n * await removePermissionFromRole(1n, 5n);\n * ```\n */\nexport async function removePermissionFromRole(roleId: number, permissionId: number): Promise<void>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const roleIdNum = Number(roleId);\n const permissionIdNum = Number(permissionId);\n\n await db\n .delete(rolePermissions)\n .where(\n and(\n eq(rolePermissions.roleId, roleIdNum),\n eq(rolePermissions.permissionId, permissionIdNum)\n )\n );\n}\n\n/**\n * Set permissions for a role (replaces all existing permissions)\n *\n * @param roleId - Role ID\n * @param permissionIds - Array of permission IDs\n *\n * @example\n * ```typescript\n * await setRolePermissions(1n, [1n, 2n, 3n]);\n * ```\n */\nexport async function setRolePermissions(roleId: number, permissionIds: number[]): Promise<void>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const roleIdNum = Number(roleId);\n\n // Delete existing mappings\n await db.delete(rolePermissions).where(eq(rolePermissions.roleId, roleIdNum));\n\n // Create new mappings\n if (permissionIds.length > 0)\n {\n const mappings = permissionIds.map(permId => ({\n roleId: roleIdNum,\n permissionId: Number(permId),\n }));\n\n await db.insert(rolePermissions).values(mappings);\n }\n}\n\n/**\n * Get all roles\n *\n * @param includeInactive - Include inactive roles\n * @returns Array of roles\n *\n * @example\n * ```typescript\n * const roles = await getAllRoles();\n * ```\n */\nexport async function getAllRoles(includeInactive = false): Promise<Role[]>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const query = db.select().from(roles);\n\n if (!includeInactive)\n {\n return query.where(eq(roles.isActive, true));\n }\n\n return query;\n}\n\n/**\n * Get role by name\n *\n * @param name - Role name\n * @returns Role or null\n *\n * @example\n * ```typescript\n * const role = await getRoleByName('admin');\n * ```\n */\nexport async function getRoleByName(name: string): Promise<Role | null>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const [role] = await db\n .select()\n .from(roles)\n .where(eq(roles.name, name))\n .limit(1);\n\n return role || null;\n}\n\n/**\n * Get role permissions\n *\n * @param roleId - Role ID\n * @returns Array of permission names\n *\n * @example\n * ```typescript\n * const perms = await getRolePermissions(1n);\n * // ['user:read', 'user:write']\n * ```\n */\nexport async function getRolePermissions(roleId: number): Promise<string[]>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const roleIdNum = Number(roleId);\n\n const perms = await db\n .select({ name: permissions.name })\n .from(rolePermissions)\n .innerJoin(permissions, eq(rolePermissions.permissionId, permissions.id))\n .where(eq(rolePermissions.roleId, roleIdNum));\n\n return perms.map(p => p.name);\n}","/**\n * @spfn/auth - Built-in Roles and Permissions\n *\n * Core roles and permissions required by the auth package\n * These cannot be deleted and are automatically created on initialization\n */\n\nimport type { RoleConfig, PermissionConfig } from './types';\n\n/**\n * Built-in roles (required by package)\n * These roles are always created and cannot be deleted\n */\nexport const BUILTIN_ROLES: Record<string, RoleConfig> = {\n SUPERADMIN: {\n name: 'superadmin',\n displayName: 'Super Administrator',\n description: 'Full system access and RBAC management',\n priority: 100,\n isSystem: true,\n isBuiltin: true,\n },\n ADMIN: {\n name: 'admin',\n displayName: 'Administrator',\n description: 'User management and organization administration',\n priority: 80,\n isSystem: true,\n isBuiltin: true,\n },\n USER: {\n name: 'user',\n displayName: 'User',\n description: 'Default user role with basic permissions',\n priority: 10,\n isSystem: true,\n isBuiltin: true,\n },\n} as const;\n\n/**\n * Built-in permissions (required by package)\n * These permissions are always created and cannot be deleted\n */\nexport const BUILTIN_PERMISSIONS: Record<string, PermissionConfig> = {\n // Self-service auth management\n AUTH_SELF_MANAGE: {\n name: 'auth:self:manage',\n displayName: 'Manage Own Auth',\n description: 'Change own password, rotate keys, manage own sessions',\n category: 'auth',\n isSystem: true,\n isBuiltin: true,\n },\n\n // User management (admin functions)\n USER_READ: {\n name: 'user:read',\n displayName: 'Read Users',\n description: 'View user information and list users',\n category: 'user',\n isSystem: true,\n isBuiltin: true,\n },\n USER_WRITE: {\n name: 'user:write',\n displayName: 'Write Users',\n description: 'Create and update user accounts',\n category: 'user',\n isSystem: true,\n isBuiltin: true,\n },\n USER_DELETE: {\n name: 'user:delete',\n displayName: 'Delete Users',\n description: 'Delete user accounts',\n category: 'user',\n isSystem: true,\n isBuiltin: true,\n },\n USER_INVITE: {\n name: 'user:invite',\n displayName: 'Invite Users',\n description: 'Create and send user invitations',\n category: 'user',\n isSystem: true,\n isBuiltin: true,\n },\n\n // RBAC management (superadmin functions)\n RBAC_ROLE_MANAGE: {\n name: 'rbac:role:manage',\n displayName: 'Manage Roles',\n description: 'Create, update, and delete roles',\n category: 'rbac',\n isSystem: true,\n isBuiltin: true,\n },\n RBAC_PERMISSION_MANAGE: {\n name: 'rbac:permission:manage',\n displayName: 'Manage Permissions',\n description: 'Assign permissions to roles and users',\n category: 'rbac',\n isSystem: true,\n isBuiltin: true,\n },\n} as const;\n\n/**\n * Built-in role-permission mappings\n * Defines default permissions for each built-in role\n */\nexport const BUILTIN_ROLE_PERMISSIONS: Record<string, string[]> = {\n superadmin: [\n 'auth:self:manage',\n 'user:read',\n 'user:write',\n 'user:delete',\n 'user:invite',\n 'rbac:role:manage',\n 'rbac:permission:manage',\n ],\n admin: [\n 'auth:self:manage',\n 'user:read',\n 'user:write',\n 'user:delete',\n 'user:invite',\n ],\n user: [\n 'auth:self:manage',\n ],\n} as const;\n\nexport type BuiltinRoleName = keyof typeof BUILTIN_ROLE_PERMISSIONS;\nexport type BuiltinPermissionName = typeof BUILTIN_PERMISSIONS[keyof typeof BUILTIN_PERMISSIONS]['name'];","/**\n * @spfn/auth - Auth Service\n *\n * Core authentication logic: registration, login, logout, password management\n */\n\nimport { findOne, create } from '@spfn/core/db';\nimport { ValidationError } from '@spfn/core/errors';\nimport { users, type User } from '@/server/entities';\nimport { hashPassword, verifyPassword } from '@/server/helpers';\nimport { validateVerificationToken } from '@/server/helpers/verification';\nimport {\n InvalidCredentialsError,\n AccountDisabledError,\n AccountAlreadyExistsError,\n InvalidVerificationTokenError,\n VerificationTokenPurposeMismatchError,\n VerificationTokenTargetMismatchError,\n} from '@/server/errors';\nimport { registerPublicKeyService, revokeKeyService } from './key.service';\nimport { updateLastLoginService } from './user.service';\n\nexport interface CheckAccountExistsParams\n{\n email?: string;\n phone?: string;\n}\n\nexport interface CheckAccountExistsResult\n{\n exists: boolean;\n identifier: string;\n identifierType: 'email' | 'phone';\n}\n\nexport interface RegisterParams\n{\n email?: string;\n phone?: string;\n verificationToken: string;\n password: string;\n publicKey: string;\n keyId: string;\n fingerprint: string;\n algorithm?: 'ES256' | 'RS256';\n}\n\nexport interface RegisterResult\n{\n userId: string;\n email?: string;\n phone?: string;\n}\n\nexport interface LoginParams\n{\n email?: string;\n phone?: string;\n password: string;\n publicKey: string;\n keyId: string;\n fingerprint: string;\n oldKeyId?: string;\n algorithm?: 'ES256' | 'RS256';\n}\n\nexport interface LoginResult\n{\n userId: string;\n email?: string;\n phone?: string;\n passwordChangeRequired: boolean;\n}\n\nexport interface LogoutParams\n{\n userId: number;\n keyId: string;\n}\n\nexport interface ChangePasswordParams\n{\n userId: number;\n currentPassword: string;\n newPassword: string;\n passwordHash?: string; // Optional: pass user's password hash to avoid re-fetch\n}\n\n/**\n * Check if an account exists by email or phone\n */\nexport async function checkAccountExistsService(\n params: CheckAccountExistsParams\n): Promise<CheckAccountExistsResult>\n{\n const { email, phone } = params;\n\n let identifier: string;\n let identifierType: 'email' | 'phone';\n let user: User | null;\n\n if (email)\n {\n identifier = email;\n identifierType = 'email';\n user = await findOne(users, { email });\n }\n else if (phone)\n {\n identifier = phone;\n identifierType = 'phone';\n user = await findOne(users, { phone });\n }\n else\n {\n throw new ValidationError('Either email or phone must be provided');\n }\n\n return {\n exists: !!user,\n identifier,\n identifierType,\n };\n}\n\n/**\n * Register a new user account\n */\nexport async function registerService(\n params: RegisterParams\n): Promise<RegisterResult>\n{\n const { email, phone, verificationToken, password, publicKey, keyId, fingerprint, algorithm } = params;\n\n // Validate verification token\n const tokenPayload = validateVerificationToken(verificationToken);\n if (!tokenPayload)\n {\n throw new InvalidVerificationTokenError();\n }\n\n // Verify that token purpose is registration\n if (tokenPayload.purpose !== 'registration')\n {\n throw new VerificationTokenPurposeMismatchError('registration', tokenPayload.purpose);\n }\n\n // Verify that token target matches provided email/phone\n const providedTarget = email || phone;\n if (tokenPayload.target !== providedTarget)\n {\n throw new VerificationTokenTargetMismatchError();\n }\n\n // Verify that token targetType matches\n const providedTargetType = email ? 'email' : 'phone';\n if (tokenPayload.targetType !== providedTargetType)\n {\n throw new VerificationTokenTargetMismatchError();\n }\n\n // Check if user already exists\n let existingUser: User | null;\n if (email)\n {\n existingUser = await findOne(users, { email });\n }\n else if (phone)\n {\n existingUser = await findOne(users, { phone });\n }\n else\n {\n throw new ValidationError('Either email or phone must be provided');\n }\n\n if (existingUser)\n {\n const identifierType = email ? 'email' : 'phone';\n throw new AccountAlreadyExistsError(email || phone!, identifierType);\n }\n\n // Hash password\n const passwordHash = await hashPassword(password);\n\n // Get default user role\n const { getRoleByName } = await import('./role.service');\n const userRole = await getRoleByName('user');\n\n if (!userRole)\n {\n throw new Error('Default user role not found. Run initializeAuth() first.');\n }\n\n // Create user\n const newUser = await create(users, {\n email: email || null,\n phone: phone || null,\n passwordHash,\n passwordChangeRequired: false,\n roleId: userRole.id,\n status: 'active',\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n\n // Register public key\n await registerPublicKeyService({\n userId: newUser.id,\n keyId,\n publicKey,\n fingerprint,\n algorithm,\n });\n\n return {\n userId: String(newUser.id),\n email: newUser.email || undefined,\n phone: newUser.phone || undefined,\n };\n}\n\n/**\n * Authenticate user and create session\n */\nexport async function loginService(\n params: LoginParams\n): Promise<LoginResult>\n{\n const { email, phone, password, publicKey, keyId, fingerprint, oldKeyId, algorithm } = params;\n\n // Find user\n let user: User | null;\n if (email)\n {\n user = await findOne(users, { email });\n }\n else if (phone)\n {\n user = await findOne(users, { phone });\n }\n else\n {\n throw new ValidationError('Either email or phone must be provided');\n }\n\n if (!user || !user.passwordHash)\n {\n throw new InvalidCredentialsError();\n }\n\n console.log('user', user);\n console.log('패스워드: ', password);\n\n // Verify password\n const isValid = await verifyPassword(password, user.passwordHash);\n if (!isValid)\n {\n throw new InvalidCredentialsError();\n }\n\n // Check if user is active\n if (user.status !== 'active')\n {\n throw new AccountDisabledError(user.status);\n }\n\n // Revoke old key if provided\n if (oldKeyId)\n {\n await revokeKeyService({\n userId: user.id,\n keyId: oldKeyId,\n reason: 'Replaced by new key on login',\n });\n }\n\n // Register new public key\n await registerPublicKeyService({\n userId: user.id,\n keyId,\n publicKey,\n fingerprint,\n algorithm,\n });\n\n // Update last login\n await updateLastLoginService(user.id);\n\n return {\n userId: String(user.id),\n email: user.email || undefined,\n phone: user.phone || undefined,\n passwordChangeRequired: user.passwordChangeRequired,\n };\n}\n\n/**\n * Logout user (revoke current key)\n */\nexport async function logoutService(\n params: LogoutParams\n): Promise<void>\n{\n const { userId, keyId } = params;\n\n await revokeKeyService({\n userId,\n keyId,\n reason: 'Revoked by logout',\n });\n}\n\n/**\n * Change user password\n */\nexport async function changePasswordService(\n params: ChangePasswordParams\n): Promise<void>\n{\n const { userId, currentPassword, newPassword, passwordHash: providedHash } = params;\n\n // Get user's password hash (either provided or fetch from DB)\n let passwordHash: string | null;\n if (providedHash)\n {\n passwordHash = providedHash;\n }\n else\n {\n const user = await findOne(users, { id: userId });\n if (!user)\n {\n throw new ValidationError('User not found');\n }\n passwordHash = user.passwordHash;\n }\n\n // Verify current password\n if (!passwordHash)\n {\n throw new ValidationError('No password set for this account');\n }\n\n const isValid = await verifyPassword(currentPassword, passwordHash);\n if (!isValid)\n {\n throw new InvalidCredentialsError('Current password is incorrect');\n }\n\n // Hash new password\n const newPasswordHash = await hashPassword(newPassword);\n\n // Update password and clear passwordChangeRequired flag\n const { updateOne } = await import('@spfn/core/db');\n await updateOne(users, { id: userId }, {\n passwordHash: newPasswordHash,\n passwordChangeRequired: false,\n updatedAt: new Date(),\n });\n}","/**\n * @spfn/auth - Password Helpers\n *\n * Password hashing and verification using bcrypt\n *\n * Security:\n * - Adaptive hashing (configurable rounds)\n * - Automatic salt generation (per-password)\n * - Constant-time comparison (timing attack protection)\n * - Rainbow table protection\n */\n\nimport bcrypt from 'bcrypt';\n\n/**\n * Bcrypt salt rounds (cost factor)\n *\n * Determines computational cost: 2^rounds iterations\n * - 10 rounds: ~100ms (default, balanced)\n * - 12 rounds: ~400ms (more secure, slower)\n * - 14 rounds: ~1600ms (very secure, too slow for most apps)\n *\n * Can be configured via SPFN_AUTH_BCRYPT_SALT_ROUNDS environment variable\n */\nconst SALT_ROUNDS = parseInt(\n process.env.SPFN_AUTH_BCRYPT_SALT_ROUNDS || // New prefixed version (recommended)\n process.env.BCRYPT_SALT_ROUNDS || // Legacy fallback\n '10',\n 10\n);\n\n/**\n * Hash a plain text password using bcrypt\n *\n * Algorithm:\n * 1. Generate random salt (128-bit)\n * 2. Apply bcrypt key derivation (2^rounds iterations)\n * 3. Return $2b$rounds$[salt][hash] (60 chars)\n *\n * @param password - Plain text password to hash\n * @returns Bcrypt hash string (includes salt)\n * @throws Error if password is empty or invalid\n *\n * @example\n * ```typescript\n * const hash = await hashPassword('mySecurePassword123');\n * // Returns: \"$2b$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy\"\n * ```\n */\nexport async function hashPassword(password: string): Promise<string>\n{\n if (!password || password.length === 0)\n {\n throw new Error('Password cannot be empty');\n }\n\n return bcrypt.hash(password, SALT_ROUNDS);\n}\n\n/**\n * Verify a password against a bcrypt hash\n *\n * Uses constant-time comparison to prevent timing attacks\n * Automatically extracts salt from hash for verification\n *\n * @param password - Plain text password to verify\n * @param hash - Bcrypt hash string (from hashPassword)\n * @returns True if password matches hash\n * @throws Error if inputs are invalid\n *\n * @example\n * ```typescript\n * const isValid = await verifyPassword(\n * 'mySecurePassword123',\n * '$2b$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy'\n * );\n * // Returns: true\n * ```\n */\nexport async function verifyPassword(password: string, hash: string): Promise<boolean>\n{\n if (!password || password.length === 0)\n {\n throw new Error('Password cannot be empty');\n }\n\n if (!hash || hash.length === 0)\n {\n throw new Error('Hash cannot be empty');\n }\n\n return bcrypt.compare(password, hash);\n}\n\n/**\n * Validate password strength\n *\n * Requirements:\n * - Minimum 8 characters\n * - At least one uppercase letter\n * - At least one lowercase letter\n * - At least one number\n * - At least one special character\n *\n * @param password - Password to validate\n * @returns Validation result with error messages\n *\n * @example\n * ```typescript\n * const result = validatePasswordStrength('weak');\n * // Returns: { valid: false, errors: ['Too short', 'Missing uppercase', ...] }\n *\n * const result = validatePasswordStrength('SecurePass123!');\n * // Returns: { valid: true, errors: [] }\n * ```\n */\nexport function validatePasswordStrength(password: string): {\n valid: boolean;\n errors: string[];\n}\n{\n const errors: string[] = [];\n\n if (password.length < 8)\n {\n errors.push('Password must be at least 8 characters');\n }\n\n if (!/[A-Z]/.test(password))\n {\n errors.push('Password must contain at least one uppercase letter');\n }\n\n if (!/[a-z]/.test(password))\n {\n errors.push('Password must contain at least one lowercase letter');\n }\n\n if (!/[0-9]/.test(password))\n {\n errors.push('Password must contain at least one number');\n }\n\n if (!/[^A-Za-z0-9]/.test(password))\n {\n errors.push('Password must contain at least one special character');\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}","/**\n * @spfn/auth - Helper functions\n */\n\nexport * from './password';\nexport * from './jwt';\nexport * from './verification';\nexport * from './context';","/**\n * @spfn/auth - Verification Code Helpers\n *\n * Helper functions for email/phone verification codes\n */\n\nimport jwt from 'jsonwebtoken';\nimport { getDatabase, create } from '@spfn/core/db';\nimport { verificationCodes } from '@/server/entities/verification-codes';\nimport { eq, and } from 'drizzle-orm';\n\n/**\n * JWT secret for verification tokens\n * Must be at least 32 characters long\n */\nfunction getVerificationTokenSecret(): string\n{\n const secret =\n process.env.SPFN_AUTH_VERIFICATION_TOKEN_SECRET || // New prefixed version (recommended)\n process.env.VERIFICATION_TOKEN_SECRET || // Legacy fallback\n process.env.SPFN_AUTH_JWT_SECRET || // New JWT secret fallback\n process.env.JWT_SECRET; // Legacy JWT secret fallback\n\n if (!secret || secret.length < 32)\n {\n throw new Error('SPFN_AUTH_VERIFICATION_TOKEN_SECRET must be at least 32 characters long');\n }\n\n return secret;\n}\n\n/**\n * Verification token expiry (15 minutes)\n */\nconst VERIFICATION_TOKEN_EXPIRY = '15m';\n\n/**\n * Verification code expiry (5 minutes)\n */\nconst VERIFICATION_CODE_EXPIRY_MINUTES = 5;\n\n/**\n * Maximum verification attempts before code expires\n */\nconst MAX_VERIFICATION_ATTEMPTS = 5;\n\n/**\n * Verification token payload\n */\nexport interface VerificationTokenPayload\n{\n target: string;\n targetType: 'email' | 'phone';\n purpose: 'registration' | 'login' | 'password_reset';\n codeId: number;\n}\n\n/**\n * Generate a random 6-digit verification code\n *\n * @returns 6-digit code as string\n */\nexport function generateVerificationCode(): string\n{\n // Generate random 6-digit number (000000 - 999999)\n const code = Math.floor(Math.random() * 1000000)\n .toString()\n .padStart(6, '0');\n\n return code;\n}\n\n/**\n * Store verification code in database\n *\n * @param target - Email or phone number\n * @param targetType - Type of target (email or phone)\n * @param code - 6-digit verification code\n * @param purpose - Purpose of verification\n * @returns Created verification code record\n */\nexport async function storeVerificationCode(\n target: string,\n targetType: 'email' | 'phone',\n code: string,\n purpose: 'registration' | 'login' | 'password_reset'\n)\n{\n const db = getDatabase();\n if (!db)\n {\n throw new Error('Database not initialized');\n }\n\n // Calculate expiry time\n const expiresAt = new Date();\n expiresAt.setMinutes(expiresAt.getMinutes() + VERIFICATION_CODE_EXPIRY_MINUTES);\n\n // Create verification code record\n const record = await create(verificationCodes, {\n target,\n targetType,\n code,\n purpose,\n expiresAt,\n attempts: '0',\n });\n\n return record;\n}\n\n/**\n * Validate verification code\n *\n * @param target - Email or phone number\n * @param targetType - Type of target\n * @param code - 6-digit code to validate\n * @param purpose - Purpose of verification\n * @returns Validation result with code ID if valid\n */\nexport async function validateVerificationCode(\n target: string,\n targetType: 'email' | 'phone',\n code: string,\n purpose: 'registration' | 'login' | 'password_reset'\n): Promise<{ valid: boolean; codeId?: number; error?: string }>\n{\n const db = getDatabase();\n if (!db)\n {\n throw new Error('Database not initialized');\n }\n\n // Find the verification code\n const records = await db\n .select()\n .from(verificationCodes)\n .where(\n and(\n eq(verificationCodes.target, target),\n eq(verificationCodes.targetType, targetType),\n eq(verificationCodes.code, code),\n eq(verificationCodes.purpose, purpose)\n )\n )\n .limit(1);\n\n if (records.length === 0)\n {\n return { valid: false, error: 'Invalid verification code' };\n }\n\n const record = records[0];\n\n // Check if code is already used\n if (record.usedAt)\n {\n return { valid: false, error: 'Verification code already used' };\n }\n\n // Check if code is expired\n if (new Date() > new Date(record.expiresAt))\n {\n return { valid: false, error: 'Verification code expired' };\n }\n\n // Check attempt count\n const attempts = parseInt(record.attempts, 10);\n if (attempts >= MAX_VERIFICATION_ATTEMPTS)\n {\n return { valid: false, error: 'Too many attempts, please request a new code' };\n }\n\n // Increment attempt count\n await db\n .update(verificationCodes)\n .set({ attempts: (attempts + 1).toString() })\n .where(eq(verificationCodes.id, record.id));\n\n return { valid: true, codeId: record.id };\n}\n\n/**\n * Mark verification code as used\n *\n * @param codeId - Verification code ID\n */\nexport async function markCodeAsUsed(codeId: number): Promise<void>\n{\n const db = getDatabase();\n if (!db)\n {\n throw new Error('Database not initialized');\n }\n\n await db\n .update(verificationCodes)\n .set({ usedAt: new Date() })\n .where(eq(verificationCodes.id, codeId));\n}\n\n/**\n * Create verification token (JWT)\n *\n * @param payload - Token payload\n * @returns Signed JWT token\n */\nexport function createVerificationToken(payload: VerificationTokenPayload): string\n{\n const secret = getVerificationTokenSecret();\n return jwt.sign(payload, secret, {\n expiresIn: VERIFICATION_TOKEN_EXPIRY,\n issuer: 'spfn-auth',\n audience: 'spfn-client',\n });\n}\n\n/**\n * Validate verification token (JWT)\n *\n * @param token - JWT token to validate\n * @returns Decoded payload if valid, null otherwise\n */\nexport function validateVerificationToken(token: string): VerificationTokenPayload | null\n{\n try\n {\n const secret = getVerificationTokenSecret();\n const decoded = jwt.verify(token, secret, {\n issuer: 'spfn-auth',\n audience: 'spfn-client',\n });\n\n // Validate that decoded has required properties\n if (\n typeof decoded === 'object' &&\n decoded !== null &&\n 'target' in decoded &&\n 'targetType' in decoded &&\n 'purpose' in decoded &&\n 'codeId' in decoded\n )\n {\n return decoded as VerificationTokenPayload;\n }\n\n return null;\n }\n catch (error)\n {\n console.error('[validateVerificationToken] Error:', error);\n return null;\n }\n}\n\n/**\n * Send verification code via email\n *\n * @param email - Email address\n * @param code - 6-digit verification code\n * @param purpose - Purpose of verification\n */\nexport async function sendVerificationEmail(\n email: string,\n code: string,\n purpose: string\n): Promise<void>\n{\n // TODO: Implement email sending with your email service\n // For now, just log to console (development only)\n console.log(`[VERIFICATION EMAIL] To: ${email}, Code: ${code}, Purpose: ${purpose}`);\n\n // Example implementation with nodemailer:\n // const transporter = nodemailer.createTransport({...});\n // await transporter.sendMail({\n // from: 'noreply@yourapp.com',\n // to: email,\n // subject: 'Your Verification Code',\n // text: `Your verification code is: ${code}`,\n // html: `<p>Your verification code is: <strong>${code}</strong></p>`,\n // });\n}\n\n/**\n * Send verification code via SMS\n *\n * @param phone - Phone number in E.164 format\n * @param code - 6-digit verification code\n * @param purpose - Purpose of verification\n */\nexport async function sendVerificationSMS(\n phone: string,\n code: string,\n purpose: string\n): Promise<void>\n{\n // TODO: Implement SMS sending with your SMS service (Twilio, AWS SNS, etc.)\n // For now, just log to console (development only)\n console.log(`[VERIFICATION SMS] To: ${phone}, Code: ${code}, Purpose: ${purpose}`);\n\n // Example implementation with Twilio:\n // const client = twilio(accountSid, authToken);\n // await client.messages.create({\n // body: `Your verification code is: ${code}`,\n // from: '+1234567890',\n // to: phone,\n // });\n}","/**\n * Auth Context Helpers\n *\n * Helper functions to access authenticated user data from route context\n */\n\nimport type { Context } from 'hono';\nimport type { AuthContext } from '../middleware/authenticate.js';\n\n/**\n * Get auth context from route context\n *\n * Accepts both raw Hono Context and RouteContext with raw property\n *\n * @example\n * ```typescript\n * // With RouteContext (RPC routes)\n * app.bind(logoutContract, [authenticate], async (c) => {\n * const { user, userId, keyId } = getAuth(c);\n * // Use authenticated data...\n * });\n *\n * // With raw Context (middleware)\n * const auth = getAuth(c);\n * ```\n */\nexport function getAuth(c: Context | { raw: Context }): AuthContext\n{\n // Check if it's RouteContext with raw property\n if ('raw' in c && c.raw)\n {\n return c.raw.get('auth');\n }\n\n // Otherwise, it's raw Hono Context\n return (c as Context).get('auth');\n}\n\n/**\n * Get authenticated user from route context\n *\n * @example\n * ```typescript\n * app.bind(profileContract, [authenticate], async (c) => {\n * const user = getUser(c);\n * return c.success({ email: user.email });\n * });\n * ```\n */\nexport function getUser(c: Context | { raw: Context })\n{\n return getAuth(c).user;\n}\n\n/**\n * Get authenticated user ID from route context\n *\n * @example\n * ```typescript\n * app.bind(postsContract, [authenticate], async (c) => {\n * const userId = getUserId(c);\n * const posts = await findPosts({ authorId: userId });\n * });\n * ```\n */\nexport function getUserId(c: Context | { raw: Context }): string\n{\n return getAuth(c).userId;\n}\n\n/**\n * Get current key ID from route context\n *\n * @example\n * ```typescript\n * app.bind(rotateKeyContract, [authenticate], async (c) => {\n * const oldKeyId = getKeyId(c);\n * // Revoke old key...\n * });\n * ```\n */\nexport function getKeyId(c: Context | { raw: Context }): string\n{\n return getAuth(c).keyId;\n}","/**\n * Authentication & Authorization Error Classes\n *\n * Custom error classes for auth-specific scenarios\n */\n\nimport {\n ValidationError,\n UnauthorizedError,\n ForbiddenError,\n ConflictError\n} from '@spfn/core/errors';\n\n/**\n * Invalid Credentials Error (401)\n *\n * Thrown when login credentials are incorrect\n */\nexport class InvalidCredentialsError extends UnauthorizedError\n{\n constructor(message: string = 'Invalid credentials')\n {\n super(message);\n this.name = 'InvalidCredentialsError';\n }\n}\n\n/**\n * Invalid Token Error (401)\n *\n * Thrown when authentication token is invalid or malformed\n */\nexport class InvalidTokenError extends UnauthorizedError\n{\n constructor(message: string = 'Invalid authentication token')\n {\n super(message);\n this.name = 'InvalidTokenError';\n }\n}\n\n/**\n * Token Expired Error (401)\n *\n * Thrown when authentication token has expired\n */\nexport class TokenExpiredError extends UnauthorizedError\n{\n constructor(message: string = 'Authentication token has expired')\n {\n super(message);\n this.name = 'TokenExpiredError';\n }\n}\n\n/**\n * Key Expired Error (401)\n *\n * Thrown when public key has expired\n */\nexport class KeyExpiredError extends UnauthorizedError\n{\n constructor(message: string = 'Public key has expired')\n {\n super(message);\n this.name = 'KeyExpiredError';\n }\n}\n\n/**\n * Account Disabled Error (403)\n *\n * Thrown when user account is disabled or inactive\n */\nexport class AccountDisabledError extends ForbiddenError\n{\n constructor(status: string = 'disabled')\n {\n super(`Account is ${status}`, { details: { status } });\n this.name = 'AccountDisabledError';\n }\n}\n\n/**\n * Account Already Exists Error (409)\n *\n * Thrown when trying to register with existing email/phone\n */\nexport class AccountAlreadyExistsError extends ConflictError\n{\n constructor(identifier: string, identifierType: 'email' | 'phone')\n {\n super('Account already exists', { details: { identifier, identifierType } });\n this.name = 'AccountAlreadyExistsError';\n }\n}\n\n/**\n * Invalid Verification Code Error (400)\n *\n * Thrown when verification code is invalid, expired, or already used\n */\nexport class InvalidVerificationCodeError extends ValidationError\n{\n constructor(reason: string = 'Invalid verification code')\n {\n super(reason);\n this.name = 'InvalidVerificationCodeError';\n }\n}\n\n/**\n * Invalid Verification Token Error (400)\n *\n * Thrown when verification token is invalid or expired\n */\nexport class InvalidVerificationTokenError extends ValidationError\n{\n constructor(message: string = 'Invalid or expired verification token')\n {\n super(message);\n this.name = 'InvalidVerificationTokenError';\n }\n}\n\n/**\n * Invalid Key Fingerprint Error (400)\n *\n * Thrown when public key fingerprint doesn't match the public key\n */\nexport class InvalidKeyFingerprintError extends ValidationError\n{\n constructor(message: string = 'Invalid key fingerprint')\n {\n super(message);\n this.name = 'InvalidKeyFingerprintError';\n }\n}\n\n/**\n * Verification Token Purpose Mismatch Error (400)\n *\n * Thrown when verification token purpose doesn't match expected purpose\n */\nexport class VerificationTokenPurposeMismatchError extends ValidationError\n{\n constructor(expected: string, actual: string)\n {\n super(`Verification token is for ${actual}, but ${expected} was expected`, { details: { expected, actual } });\n this.name = 'VerificationTokenPurposeMismatchError';\n }\n}\n\n/**\n * Verification Token Target Mismatch Error (400)\n *\n * Thrown when verification token target doesn't match provided email/phone\n */\nexport class VerificationTokenTargetMismatchError extends ValidationError\n{\n constructor()\n {\n super('Verification token does not match provided email/phone');\n this.name = 'VerificationTokenTargetMismatchError';\n }\n}","/**\n * @spfn/auth - Key Service\n *\n * Handles public key registration, rotation, and revocation\n */\n\nimport { create, getDatabase } from '@spfn/core/db';\nimport { userPublicKeys } from '@/server/entities';\nimport { verifyKeyFingerprint } from '@/server/helpers/jwt';\nimport { InvalidKeyFingerprintError } from '@/server/errors';\nimport { eq, and } from 'drizzle-orm';\n\nexport interface RegisterPublicKeyParams\n{\n userId: number;\n keyId: string;\n publicKey: string;\n fingerprint: string;\n algorithm?: 'ES256' | 'RS256';\n}\n\nexport interface RotateKeyParams\n{\n userId: number;\n oldKeyId: string;\n newKeyId: string;\n newPublicKey: string;\n fingerprint: string;\n algorithm?: 'ES256' | 'RS256';\n}\n\nexport interface RotateKeyResult\n{\n success: boolean;\n keyId: string;\n}\n\nexport interface RevokeKeyParams\n{\n userId: number;\n keyId: string;\n reason: string;\n}\n\n/**\n * Helper: Calculate key expiry date (90 days from now)\n */\nfunction getKeyExpiryDate(): Date\n{\n const expiresAt = new Date();\n expiresAt.setDate(expiresAt.getDate() + 90);\n return expiresAt;\n}\n\n/**\n * Register a new public key for a user\n */\nexport async function registerPublicKeyService(\n params: RegisterPublicKeyParams\n): Promise<void>\n{\n const { userId, keyId, publicKey, fingerprint, algorithm = 'ES256' } = params;\n\n // Verify fingerprint matches public key\n const isValidFingerprint = verifyKeyFingerprint(publicKey, fingerprint);\n if (!isValidFingerprint)\n {\n throw new InvalidKeyFingerprintError();\n }\n\n // Store public key (90 days expiry)\n await create(userPublicKeys, {\n userId,\n keyId,\n publicKey,\n algorithm,\n fingerprint,\n isActive: true,\n createdAt: new Date(),\n expiresAt: getKeyExpiryDate(),\n });\n}\n\n/**\n * Rotate user's public key (revoke old, register new)\n */\nexport async function rotateKeyService(\n params: RotateKeyParams\n): Promise<RotateKeyResult>\n{\n const { userId, oldKeyId, newKeyId, newPublicKey, fingerprint, algorithm = 'ES256' } = params;\n\n // Verify fingerprint matches public key\n const isValidFingerprint = verifyKeyFingerprint(newPublicKey, fingerprint);\n if (!isValidFingerprint)\n {\n throw new InvalidKeyFingerprintError();\n }\n\n const db = getDatabase()!;\n\n // Revoke old key\n await db\n .update(userPublicKeys)\n .set({\n isActive: false,\n revokedAt: new Date(),\n revokedReason: 'Replaced by key rotation',\n })\n .where(\n and(\n eq(userPublicKeys.keyId, oldKeyId),\n eq(userPublicKeys.userId, userId)\n )\n );\n\n // Store new public key (90 days expiry)\n await create(userPublicKeys, {\n userId,\n keyId: newKeyId,\n publicKey: newPublicKey,\n algorithm,\n fingerprint,\n isActive: true,\n createdAt: new Date(),\n expiresAt: getKeyExpiryDate(),\n });\n\n return {\n success: true,\n keyId: newKeyId,\n };\n}\n\n/**\n * Revoke a user's public key\n */\nexport async function revokeKeyService(\n params: RevokeKeyParams\n): Promise<void>\n{\n const { userId, keyId, reason } = params;\n\n const db = getDatabase()!;\n\n await db\n .update(userPublicKeys)\n .set({\n isActive: false,\n revokedAt: new Date(),\n revokedReason: reason,\n })\n .where(\n and(\n eq(userPublicKeys.keyId, keyId),\n eq(userPublicKeys.userId, userId)\n )\n );\n}","/**\n * @spfn/auth - User Service\n *\n * Handles user CRUD operations\n */\n\nimport { findOne, updateOne } from '@spfn/core/db';\nimport { users, type User } from '@/server/entities';\n\n/**\n * Get user by ID\n */\nexport async function getUserByIdService(userId: number): Promise<User | null>\n{\n return await findOne(users, { id: userId });\n}\n\n/**\n * Get user by email\n */\nexport async function getUserByEmailService(email: string): Promise<User | null>\n{\n return await findOne(users, { email });\n}\n\n/**\n * Get user by phone\n */\nexport async function getUserByPhoneService(phone: string): Promise<User | null>\n{\n return await findOne(users, { phone });\n}\n\n/**\n * Update user's last login timestamp\n */\nexport async function updateLastLoginService(userId: number): Promise<void>\n{\n await updateOne(users, { id: userId }, {\n lastLoginAt: new Date(),\n });\n}\n\n/**\n * Update user data\n */\nexport async function updateUserService(\n userId: number,\n updates: Partial<Omit<User, 'id' | 'createdAt'>>\n): Promise<void>\n{\n await updateOne(users, { id: userId }, {\n ...updates,\n updatedAt: new Date(),\n });\n}","/**\n * @spfn/auth - Verification Service\n *\n * Handles OTP code generation, validation, and delivery\n */\n\nimport {\n generateVerificationCode,\n storeVerificationCode,\n validateVerificationCode,\n markCodeAsUsed,\n createVerificationToken,\n sendVerificationEmail,\n sendVerificationSMS\n} from '@/server/helpers/verification';\nimport { InvalidVerificationCodeError } from '@/server/errors';\n\nexport interface SendVerificationCodeParams\n{\n target: string;\n targetType: 'email' | 'phone';\n purpose: 'registration' | 'login' | 'password_reset';\n}\n\nexport interface SendVerificationCodeResult\n{\n success: boolean;\n expiresAt: string;\n}\n\nexport interface VerifyCodeParams\n{\n target: string;\n targetType: 'email' | 'phone';\n code: string;\n purpose: 'registration' | 'login' | 'password_reset';\n}\n\nexport interface VerifyCodeResult\n{\n valid: boolean;\n verificationToken: string;\n}\n\n/**\n * Send verification code via email or SMS\n */\nexport async function sendVerificationCodeService(\n params: SendVerificationCodeParams\n): Promise<SendVerificationCodeResult>\n{\n const { target, targetType, purpose } = params;\n\n // Generate 6-digit verification code\n const code = generateVerificationCode();\n\n // Store code in database\n const codeRecord = await storeVerificationCode(target, targetType, code, purpose);\n\n // Send code via email or SMS\n if (targetType === 'email')\n {\n await sendVerificationEmail(target, code, purpose);\n }\n else\n {\n await sendVerificationSMS(target, code, purpose);\n }\n\n return {\n success: true,\n expiresAt: codeRecord.expiresAt.toISOString(),\n };\n}\n\n/**\n * Verify OTP code and return verification token\n */\nexport async function verifyCodeService(\n params: VerifyCodeParams\n): Promise<VerifyCodeResult>\n{\n const { target, targetType, code, purpose } = params;\n\n // Validate the verification code\n const validation = await validateVerificationCode(target, targetType, code, purpose);\n\n if (!validation.valid)\n {\n throw new InvalidVerificationCodeError(validation.error || 'Invalid verification code');\n }\n\n // Mark code as used\n await markCodeAsUsed(validation.codeId!);\n\n // Create verification token (15 min validity)\n const verificationToken = createVerificationToken({\n target,\n targetType,\n purpose,\n codeId: validation.codeId!,\n });\n\n return {\n valid: true,\n verificationToken,\n };\n}\n","/**\n * @spfn/auth - Me Service\n *\n * Service for retrieving current user information\n */\n\nimport { getDatabase } from '@spfn/core/db';\nimport { users, roles, permissions, rolePermissions } from '@/server/entities';\nimport { eq, and } from 'drizzle-orm';\n\nexport interface GetMeResult\n{\n userId: string;\n email?: string;\n phone?: string;\n role: {\n id: number;\n name: string;\n displayName: string;\n priority: number;\n };\n permissions: Array<{\n id: number;\n name: string;\n displayName: string;\n category?: string;\n }>;\n}\n\n/**\n * Get current user information including role and permissions\n *\n * @param userId - User ID (string, number, or bigint)\n * @returns User info with role and permissions\n *\n * @example\n * ```typescript\n * const userInfo = await getMeService('123');\n * console.log(userInfo.role.name); // 'admin'\n * console.log(userInfo.permissions.length); // 15\n * ```\n */\nexport async function getMeService(userId: string | number | bigint): Promise<GetMeResult>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const userIdNum = typeof userId === 'string' ? Number(userId) : Number(userId);\n\n // 1. Get user with role information\n const [userWithRole] = await db\n .select({\n userId: users.id,\n email: users.email,\n phone: users.phone,\n roleId: roles.id,\n roleName: roles.name,\n roleDisplayName: roles.displayName,\n rolePriority: roles.priority,\n })\n .from(users)\n .innerJoin(roles, eq(users.roleId, roles.id))\n .where(eq(users.id, userIdNum))\n .limit(1);\n\n if (!userWithRole)\n {\n throw new Error('[Auth] User not found');\n }\n\n // 2. Get role permissions\n const rolePerms = await db\n .select({\n id: permissions.id,\n name: permissions.name,\n displayName: permissions.displayName,\n category: permissions.category,\n })\n .from(rolePermissions)\n .innerJoin(permissions, eq(rolePermissions.permissionId, permissions.id))\n .where(\n and(\n eq(rolePermissions.roleId, userWithRole.roleId),\n eq(permissions.isActive, true)\n )\n );\n\n // 3. Build response\n return {\n userId: userWithRole.userId.toString(),\n email: userWithRole.email ?? undefined,\n phone: userWithRole.phone ?? undefined,\n role: {\n id: userWithRole.roleId,\n name: userWithRole.roleName,\n displayName: userWithRole.roleDisplayName,\n priority: userWithRole.rolePriority,\n },\n permissions: rolePerms.map(perm => ({\n id: perm.id,\n name: perm.name,\n displayName: perm.displayName,\n category: perm.category ?? undefined,\n })),\n };\n}","/**\n * @spfn/auth - RBAC Initialization Service\n *\n * Initialize roles, permissions, and their mappings\n */\n\nimport { getDatabase } from '@spfn/core/db';\nimport { logger } from '@spfn/core/logger';\nimport { roles, permissions, rolePermissions } from '@/server/entities';\nimport {\n BUILTIN_ROLES,\n BUILTIN_PERMISSIONS,\n BUILTIN_ROLE_PERMISSIONS,\n} from '@/server/rbac';\nimport type { AuthInitOptions, RoleConfig, PermissionConfig } from '@/server/rbac';\nimport { eq, and, inArray } from 'drizzle-orm';\nimport { configureAuth } from '@/lib/config';\n\nconst authLogger = logger.child('@spfn/auth');\n\n/**\n * Initialize auth package with RBAC system\n *\n * Creates built-in roles, permissions, and custom configurations\n *\n * @param options - Initialization options\n *\n * @example\n * ```typescript\n * // Minimal - only built-in roles (user, admin, superadmin)\n * await initializeAuth();\n *\n * // Custom roles and permissions\n * await initializeAuth({\n * roles: [\n * { name: 'project-manager', displayName: 'Project Manager', priority: 50 },\n * { name: 'developer', displayName: 'Developer', priority: 30 },\n * ],\n * permissions: [\n * { name: 'project:create', displayName: 'Create Project', category: 'project' },\n * { name: 'task:assign', displayName: 'Assign Task', category: 'task' },\n * ],\n * rolePermissions: {\n * 'project-manager': ['project:create', 'task:assign'],\n * 'developer': ['task:complete'],\n * },\n * });\n * ```\n */\nexport async function initializeAuth(options: AuthInitOptions = {}): Promise<void>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized. Call initDatabase() first.');\n }\n\n authLogger.info('🔐 Initializing RBAC system...');\n\n // Configure global auth settings\n if (options.sessionTtl !== undefined)\n {\n configureAuth({\n sessionTtl: options.sessionTtl,\n });\n authLogger.info(`⏱️ Session TTL: ${options.sessionTtl}`);\n }\n\n // 1. Collect all roles (built-in + custom)\n const allRoles: RoleConfig[] = [\n ...Object.values(BUILTIN_ROLES),\n ...(options.roles || []),\n ];\n\n // 2. Create/update all roles\n for (const roleConfig of allRoles)\n {\n await upsertRole(roleConfig);\n }\n\n // 3. Collect all permissions (built-in + custom)\n const allPermissions: PermissionConfig[] = [\n ...Object.values(BUILTIN_PERMISSIONS),\n ...(options.permissions || []),\n ];\n\n // 4. Create/update all permissions\n for (const permConfig of allPermissions)\n {\n await upsertPermission(permConfig);\n }\n\n // 5. Collect all role-permission mappings (built-in + custom)\n const allMappings: Record<string, string[]> = { ...BUILTIN_ROLE_PERMISSIONS };\n\n // Merge custom mappings\n if (options.rolePermissions)\n {\n for (const [roleName, permNames] of Object.entries(options.rolePermissions))\n {\n if (allMappings[roleName])\n {\n // Merge with existing mappings (deduplicate)\n allMappings[roleName] = [\n ...new Set([...allMappings[roleName], ...permNames]),\n ];\n }\n else\n {\n // New role mapping\n allMappings[roleName] = permNames;\n }\n }\n }\n\n // 6. Create all role-permission mappings\n for (const [roleName, permNames] of Object.entries(allMappings))\n {\n await assignPermissionsToRole(roleName, permNames);\n }\n\n authLogger.info('✅ RBAC initialization complete');\n authLogger.info(`📊 Roles: ${allRoles.length}, Permissions: ${allPermissions.length}`);\n authLogger.info('🔒 Built-in roles: user, admin, superadmin');\n}\n\n/**\n * Create or update a role (idempotent)\n */\nasync function upsertRole(config: RoleConfig): Promise<void>\n{\n const db = getDatabase()!;\n\n const existing = await db\n .select()\n .from(roles)\n .where(eq(roles.name, config.name))\n .limit(1);\n\n if (existing.length === 0)\n {\n // Create new role\n await db.insert(roles).values({\n name: config.name,\n displayName: config.displayName,\n description: config.description,\n priority: config.priority ?? 10,\n isSystem: config.isSystem ?? false,\n isBuiltin: config.isBuiltin ?? false,\n });\n\n authLogger.info(` ✅ Created role: ${config.name}`);\n }\n else\n {\n // Update existing role (but preserve priority for built-in roles)\n const updateData: Record<string, any> = {\n displayName: config.displayName,\n description: config.description,\n };\n\n // Only update priority for non-builtin roles\n if (!existing[0].isBuiltin)\n {\n updateData.priority = config.priority ?? existing[0].priority;\n }\n\n await db\n .update(roles)\n .set(updateData)\n .where(eq(roles.id, existing[0].id));\n }\n}\n\n/**\n * Create or update a permission (idempotent)\n */\nasync function upsertPermission(config: PermissionConfig): Promise<void>\n{\n const db = getDatabase()!;\n\n const existing = await db\n .select()\n .from(permissions)\n .where(eq(permissions.name, config.name))\n .limit(1);\n\n if (existing.length === 0)\n {\n // Create new permission\n await db.insert(permissions).values({\n name: config.name,\n displayName: config.displayName,\n description: config.description,\n category: config.category,\n isSystem: config.isSystem ?? false,\n isBuiltin: config.isBuiltin ?? false,\n });\n\n authLogger.info(` ✅ Created permission: ${config.name}`);\n }\n else\n {\n // Update existing permission\n await db\n .update(permissions)\n .set({\n displayName: config.displayName,\n description: config.description,\n category: config.category,\n })\n .where(eq(permissions.id, existing[0].id));\n }\n}\n\n/**\n * Assign permissions to a role\n */\nasync function assignPermissionsToRole(roleName: string, permissionNames: string[]): Promise<void>\n{\n const db = getDatabase()!;\n\n // Get role\n const [role] = await db\n .select()\n .from(roles)\n .where(eq(roles.name, roleName))\n .limit(1);\n\n if (!role)\n {\n authLogger.warn(` ⚠️ Role not found: ${roleName}, skipping permission assignment`);\n return;\n }\n\n // Get permissions\n const perms = await db\n .select()\n .from(permissions)\n .where(inArray(permissions.name, permissionNames));\n\n if (perms.length === 0)\n {\n authLogger.warn(` ⚠️ No permissions found for role: ${roleName}`);\n return;\n }\n\n // Create mappings (skip duplicates)\n for (const perm of perms)\n {\n const existing = await db\n .select()\n .from(rolePermissions)\n .where(\n and(\n eq(rolePermissions.roleId, role.id),\n eq(rolePermissions.permissionId, perm.id)\n )\n )\n .limit(1);\n\n if (existing.length === 0)\n {\n await db.insert(rolePermissions).values({\n roleId: role.id,\n permissionId: perm.id,\n });\n }\n }\n}","/**\n * @spfn/auth - Global Configuration\n *\n * Manages global auth configuration including session TTL\n */\n\n/**\n * Cookie names used by SPFN Auth\n */\nexport const COOKIE_NAMES = {\n /** Encrypted session data (userId, privateKey, keyId, algorithm) */\n SESSION: 'spfn_session',\n /** Current key ID (for key rotation) */\n SESSION_KEY_ID: 'spfn_session_key_id',\n} as const;\n\n/**\n * Parse duration string to seconds\n *\n * Supports: '30d', '12h', '45m', '3600s', or plain number\n *\n * @example\n * parseDuration('30d') // 2592000 (30 days in seconds)\n * parseDuration('12h') // 43200\n * parseDuration('45m') // 2700\n * parseDuration('3600') // 3600\n */\nexport function parseDuration(duration: string | number): number\n{\n if (typeof duration === 'number')\n {\n return duration;\n }\n\n const match = duration.match(/^(\\d+)([dhms]?)$/);\n if (!match)\n {\n throw new Error(`Invalid duration format: ${duration}. Use format like '30d', '12h', '45m', '3600s', or plain number.`);\n }\n\n const value = parseInt(match[1], 10);\n const unit = match[2] || 's';\n\n switch (unit)\n {\n case 'd':\n return value * 24 * 60 * 60;\n case 'h':\n return value * 60 * 60;\n case 'm':\n return value * 60;\n case 's':\n return value;\n default:\n throw new Error(`Unknown duration unit: ${unit}`);\n }\n}\n\n/**\n * Auth configuration\n */\nexport interface AuthConfig\n{\n /**\n * Default session TTL in seconds or duration string\n *\n * Supports:\n * - Number: seconds (e.g., 2592000)\n * - String: '30d', '12h', '45m', '3600s'\n *\n * @default 7d (7 days)\n */\n sessionTtl?: string | number;\n}\n\n/**\n * Global auth configuration state\n */\nlet globalConfig: AuthConfig = {\n sessionTtl: '7d', // Default: 7 days\n};\n\n/**\n * Configure global auth settings\n *\n * @param config - Auth configuration\n *\n * @example\n * ```typescript\n * configureAuth({\n * sessionTtl: '30d', // 30 days\n * });\n * ```\n */\nexport function configureAuth(config: AuthConfig): void\n{\n globalConfig = {\n ...globalConfig,\n ...config,\n };\n}\n\n/**\n * Get current auth configuration\n */\nexport function getAuthConfig(): AuthConfig\n{\n return { ...globalConfig };\n}\n\n/**\n * Get session TTL in seconds\n *\n * Priority:\n * 1. Runtime override (remember parameter)\n * 2. Global config (configureAuth)\n * 3. Environment variable (SPFN_AUTH_SESSION_TTL)\n * 4. Default (7 days)\n */\nexport function getSessionTtl(override?: string | number): number\n{\n // 1. Runtime override\n if (override !== undefined)\n {\n return parseDuration(override);\n }\n\n // 2. Global config\n if (globalConfig.sessionTtl !== undefined)\n {\n return parseDuration(globalConfig.sessionTtl);\n }\n\n // 3. Environment variable\n const envTtl = process.env.SPFN_AUTH_SESSION_TTL;\n if (envTtl)\n {\n return parseDuration(envTtl);\n }\n\n // 4. Default: 7 days\n return 7 * 24 * 60 * 60;\n}","/**\n * @spfn/auth - Permission Service\n *\n * Permission checking and validation logic\n */\n\nimport { getDatabase } from '@spfn/core/db';\nimport { users, roles, permissions, rolePermissions, userPermissions } from '@/server/entities';\nimport { eq, and } from 'drizzle-orm';\n\n/**\n * Get all permissions for a user\n *\n * Combines role-based permissions with user-specific overrides\n * Handles expiration of temporary permissions\n *\n * @param userId - User ID (string, number, or bigint)\n * @returns Array of permission names\n *\n * @example\n * ```typescript\n * const perms = await getUserPermissions('123');\n * // ['auth:self:manage', 'user:read', 'post:create']\n * ```\n */\nexport async function getUserPermissions(userId: string | number | bigint): Promise<string[]>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const userIdNum = typeof userId === 'string' ? Number(userId) : Number(userId);\n\n // 1. Get user's role\n const [user] = await db\n .select({ roleId: users.roleId })\n .from(users)\n .where(eq(users.id, userIdNum))\n .limit(1);\n\n if (!user || !user.roleId)\n {\n return [];\n }\n\n const permSet = new Set<string>();\n\n // 2. Get role-based permissions\n const rolePerms = await db\n .select({ name: permissions.name })\n .from(rolePermissions)\n .innerJoin(permissions, eq(rolePermissions.permissionId, permissions.id))\n .where(\n and(\n eq(rolePermissions.roleId, user.roleId),\n eq(permissions.isActive, true)\n )\n );\n\n for (const perm of rolePerms)\n {\n permSet.add(perm.name);\n }\n\n // 3. Apply user-specific permission overrides\n const userPerms = await db\n .select({\n name: permissions.name,\n granted: userPermissions.granted,\n expiresAt: userPermissions.expiresAt,\n })\n .from(userPermissions)\n .innerJoin(permissions, eq(userPermissions.permissionId, permissions.id))\n .where(eq(userPermissions.userId, userIdNum));\n\n const now = new Date();\n for (const userPerm of userPerms)\n {\n // Skip expired permissions\n if (userPerm.expiresAt && userPerm.expiresAt < now)\n {\n continue;\n }\n\n if (userPerm.granted)\n {\n // Grant permission (add even if not in role)\n permSet.add(userPerm.name);\n }\n else\n {\n // Revoke permission (remove even if in role)\n permSet.delete(userPerm.name);\n }\n }\n\n return Array.from(permSet);\n}\n\n/**\n * Check if user has a specific permission\n *\n * @param userId - User ID\n * @param permissionName - Permission name (e.g., 'user:delete')\n * @returns true if user has permission\n *\n * @example\n * ```typescript\n * if (await hasPermission('123', 'user:delete')) {\n * // User can delete users\n * }\n * ```\n */\nexport async function hasPermission(\n userId: string | number | bigint,\n permissionName: string\n): Promise<boolean>\n{\n const perms = await getUserPermissions(userId);\n return perms.includes(permissionName);\n}\n\n/**\n * Check if user has any of the specified permissions\n *\n * @param userId - User ID\n * @param permissionNames - Array of permission names\n * @returns true if user has at least one permission\n *\n * @example\n * ```typescript\n * if (await hasAnyPermission('123', ['post:read', 'admin:access'])) {\n * // User can access content\n * }\n * ```\n */\nexport async function hasAnyPermission(\n userId: string | number | bigint,\n permissionNames: string[]\n): Promise<boolean>\n{\n const perms = await getUserPermissions(userId);\n return permissionNames.some(p => perms.includes(p));\n}\n\n/**\n * Check if user has all of the specified permissions\n *\n * @param userId - User ID\n * @param permissionNames - Array of permission names\n * @returns true if user has all permissions\n *\n * @example\n * ```typescript\n * if (await hasAllPermissions('123', ['post:write', 'post:publish'])) {\n * // User can write AND publish\n * }\n * ```\n */\nexport async function hasAllPermissions(\n userId: string | number | bigint,\n permissionNames: string[]\n): Promise<boolean>\n{\n const perms = await getUserPermissions(userId);\n return permissionNames.every(p => perms.includes(p));\n}\n\n/**\n * Check if user has a specific role\n *\n * @param userId - User ID\n * @param roleName - Role name (e.g., 'admin', 'superadmin')\n * @returns true if user has role\n *\n * @example\n * ```typescript\n * if (await hasRole('123', 'admin')) {\n * // User is admin\n * }\n * ```\n */\nexport async function hasRole(userId: string | number | bigint, roleName: string): Promise<boolean>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const userIdNum = typeof userId === 'string' ? Number(userId) : Number(userId);\n\n const [user] = await db\n .select({ roleId: users.roleId })\n .from(users)\n .where(eq(users.id, userIdNum))\n .limit(1);\n\n if (!user || !user.roleId)\n {\n return false;\n }\n\n const [role] = await db\n .select({ name: roles.name })\n .from(roles)\n .where(eq(roles.id, user.roleId))\n .limit(1);\n\n return role?.name === roleName;\n}\n\n/**\n * Check if user has any of the specified roles\n *\n * @param userId - User ID\n * @param roleNames - Array of role names\n * @returns true if user has at least one role\n */\nexport async function hasAnyRole(userId: string | number | bigint, roleNames: string[]): Promise<boolean>\n{\n for (const roleName of roleNames)\n {\n if (await hasRole(userId, roleName))\n {\n return true;\n }\n }\n\n return false;\n}","/**\n * @spfn/auth - Services Export\n *\n * All business logic services for reusability\n */\n\n// Auth Service\nexport {\n checkAccountExistsService,\n registerService,\n loginService,\n logoutService,\n changePasswordService,\n} from './auth.service';\n\nexport type {\n CheckAccountExistsParams,\n CheckAccountExistsResult,\n RegisterParams,\n RegisterResult,\n LoginParams,\n LoginResult,\n LogoutParams,\n ChangePasswordParams,\n} from './auth.service';\n\n// Verification Service\nexport {\n sendVerificationCodeService,\n verifyCodeService,\n} from './verification.service';\n\nexport type {\n SendVerificationCodeParams,\n SendVerificationCodeResult,\n VerifyCodeParams,\n VerifyCodeResult,\n} from './verification.service';\n\n// Key Service\nexport {\n registerPublicKeyService,\n rotateKeyService,\n revokeKeyService,\n} from './key.service';\n\nexport type {\n RegisterPublicKeyParams,\n RotateKeyParams,\n RotateKeyResult,\n RevokeKeyParams,\n} from './key.service';\n\n// User Service\nexport {\n getUserByIdService,\n getUserByEmailService,\n getUserByPhoneService,\n updateLastLoginService,\n updateUserService,\n} from './user.service';\n\n// Me Service\nexport {\n getMeService,\n} from './me.service';\n\nexport type {\n GetMeResult,\n} from './me.service';\n\n// RBAC Service\nexport {\n initializeAuth,\n} from './rbac.service';\n\n// Permission Service\nexport {\n getUserPermissions,\n hasPermission,\n hasAnyPermission,\n hasAllPermissions,\n hasRole,\n hasAnyRole,\n} from './permission.service';\n\n// Role Service\nexport {\n createRole,\n updateRole,\n deleteRole,\n addPermissionToRole,\n removePermissionFromRole,\n setRolePermissions,\n getAllRoles,\n getRoleByName,\n getRolePermissions,\n} from './role.service';\n\n// Invitation Service\nexport {\n createInvitation,\n getInvitationByToken,\n getInvitationWithDetails,\n validateInvitation,\n acceptInvitation,\n listInvitations,\n cancelInvitation,\n deleteInvitation,\n expireOldInvitations,\n resendInvitation,\n} from './invitation.service';","/**\n * @spfn/auth - Invitation Service\n *\n * User invitation management for invite-only registration\n */\n\nimport { getDatabase } from '@spfn/core/db';\nimport { invitations, users, roles } from '@/server/entities';\nimport type { Invitation, InvitationStatus, InvitationWithDetails } from '@/server/entities/invitations';\nimport { eq, and, lt, desc, sql } from 'drizzle-orm';\nimport { hashPassword } from '@/server/helpers';\nimport crypto from 'crypto';\n\n/**\n * Generate unique invitation token (UUID v4)\n */\nfunction generateInvitationToken(): string\n{\n return crypto.randomUUID();\n}\n\n/**\n * Calculate expiration date from now\n *\n * @param days - Number of days until expiration (default: 7)\n * @returns Expiration timestamp\n */\nfunction calculateExpiresAt(days: number = 7): Date\n{\n const expiresAt = new Date();\n expiresAt.setDate(expiresAt.getDate() + days);\n return expiresAt;\n}\n\n/**\n * Create a new invitation\n *\n * @param params - Invitation parameters\n * @returns Created invitation\n * @throws Error if validation fails\n *\n * @example\n * ```typescript\n * const invitation = await createInvitation({\n * email: 'newuser@example.com',\n * roleId: 2n,\n * invitedBy: 1n,\n * expiresInDays: 7,\n * metadata: { message: 'Welcome!' }\n * });\n * ```\n */\nexport async function createInvitation(params: {\n email: string;\n roleId: number;\n invitedBy: number;\n expiresInDays?: number;\n metadata?: Record<string, any>;\n}): Promise<Invitation>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const { email, roleId, invitedBy, expiresInDays = 7, metadata } = params;\n\n // Validate email format\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (!emailRegex.test(email))\n {\n throw new Error('Invalid email format');\n }\n\n // Check if user already exists\n const existingUser = await db\n .select()\n .from(users)\n .where(eq(users.email, email))\n .limit(1);\n\n if (existingUser.length > 0)\n {\n throw new Error('User with this email already exists');\n }\n\n // Check if there's already a pending invitation for this email\n const existingInvitation = await db\n .select()\n .from(invitations)\n .where(\n and(\n eq(invitations.email, email),\n eq(invitations.status, 'pending')\n )\n )\n .limit(1);\n\n if (existingInvitation.length > 0)\n {\n throw new Error('Pending invitation already exists for this email');\n }\n\n // Verify role exists\n const role = await db\n .select()\n .from(roles)\n .where(eq(roles.id, roleId))\n .limit(1);\n\n if (role.length === 0)\n {\n throw new Error(`Role with id ${roleId} not found`);\n }\n\n // Verify inviter exists\n const inviter = await db\n .select()\n .from(users)\n .where(eq(users.id, invitedBy))\n .limit(1);\n\n if (inviter.length === 0)\n {\n throw new Error(`User with id ${invitedBy} not found`);\n }\n\n // Generate unique token\n const token = generateInvitationToken();\n const expiresAt = calculateExpiresAt(expiresInDays);\n\n // Create invitation\n const [invitation] = await db\n .insert(invitations)\n .values({\n email,\n token,\n roleId,\n invitedBy,\n status: 'pending',\n expiresAt,\n metadata: metadata || null,\n })\n .returning();\n\n console.log(`[Auth] ✅ Created invitation: ${email} as ${role[0].name} (expires: ${expiresAt.toISOString()})`);\n\n return invitation;\n}\n\n/**\n * Get invitation by token\n *\n * @param token - Invitation token (UUID)\n * @returns Invitation or null if not found\n */\nexport async function getInvitationByToken(token: string): Promise<Invitation | null>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const result = await db\n .select()\n .from(invitations)\n .where(eq(invitations.token, token))\n .limit(1);\n\n return result[0] || null;\n}\n\n/**\n * Get invitation with role and inviter details\n *\n * @param token - Invitation token\n * @returns Invitation with joined data or null\n */\nexport async function getInvitationWithDetails(token: string): Promise<InvitationWithDetails | null>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const result = await db\n .select({\n id: invitations.id,\n email: invitations.email,\n token: invitations.token,\n roleId: invitations.roleId,\n invitedBy: invitations.invitedBy,\n status: invitations.status,\n expiresAt: invitations.expiresAt,\n acceptedAt: invitations.acceptedAt,\n cancelledAt: invitations.cancelledAt,\n metadata: invitations.metadata,\n createdAt: invitations.createdAt,\n updatedAt: invitations.updatedAt,\n role: {\n id: roles.id,\n name: roles.name,\n displayName: roles.displayName,\n },\n inviter: {\n id: users.id,\n email: users.email,\n },\n })\n .from(invitations)\n .innerJoin(roles, eq(invitations.roleId, roles.id))\n .innerJoin(users, eq(invitations.invitedBy, users.id))\n .where(eq(invitations.token, token))\n .limit(1);\n\n return result[0] || null;\n}\n\n/**\n * Validate invitation\n *\n * Checks if invitation is valid for acceptance\n *\n * @param token - Invitation token\n * @returns Validation result\n */\nexport async function validateInvitation(token: string): Promise<{\n valid: boolean;\n invitation?: Invitation;\n error?: string;\n}>\n{\n const invitation = await getInvitationByToken(token);\n\n if (!invitation)\n {\n return { valid: false, error: 'Invitation not found' };\n }\n\n if (invitation.status === 'accepted')\n {\n return { valid: false, error: 'Invitation already accepted', invitation };\n }\n\n if (invitation.status === 'cancelled')\n {\n return { valid: false, error: 'Invitation was cancelled', invitation };\n }\n\n if (invitation.status === 'expired')\n {\n return { valid: false, error: 'Invitation has expired', invitation };\n }\n\n // Check if expired by time\n if (new Date() > new Date(invitation.expiresAt))\n {\n return { valid: false, error: 'Invitation has expired', invitation };\n }\n\n return { valid: true, invitation };\n}\n\n/**\n * Accept invitation and create user account\n *\n * @param params - Acceptance parameters\n * @returns Created user info\n * @throws Error if invitation is invalid or user creation fails\n *\n * @example\n * ```typescript\n * const user = await acceptInvitation({\n * token: 'uuid-v4',\n * password: 'SecurePass123!',\n * publicKey: 'base64-der...',\n * keyId: 'uuid-v4',\n * fingerprint: 'sha256-hex',\n * algorithm: 'ES256'\n * });\n * ```\n */\nexport async function acceptInvitation(params: {\n token: string;\n password: string;\n publicKey: string;\n keyId: string;\n fingerprint: string;\n algorithm: 'ES256' | 'RS256';\n}): Promise<{\n userId: number;\n email: string;\n role: string;\n}>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const { token, password, publicKey, keyId, fingerprint, algorithm } = params;\n\n // Validate invitation\n const validation = await validateInvitation(token);\n\n if (!validation.valid || !validation.invitation)\n {\n throw new Error(validation.error || 'Invalid invitation');\n }\n\n const invitation = validation.invitation;\n\n // Get role details\n const role = await db\n .select()\n .from(roles)\n .where(eq(roles.id, invitation.roleId))\n .limit(1);\n\n if (role.length === 0)\n {\n throw new Error('Role not found');\n }\n\n // Hash password\n const passwordHash = await hashPassword(password);\n\n // Use transaction to create user and update invitation atomically\n const result = await db.transaction(async (tx) =>\n {\n // Create user\n const [newUser] = await tx\n .insert(users)\n .values({\n email: invitation.email,\n passwordHash,\n roleId: invitation.roleId,\n emailVerifiedAt: new Date(), // Auto-verify invited users\n passwordChangeRequired: false,\n status: 'active',\n })\n .returning();\n\n // Create public key for asymmetric JWT\n const { userPublicKeys } = await import('@/server/entities');\n await tx\n .insert(userPublicKeys)\n .values({\n userId: newUser.id,\n keyId,\n publicKey,\n algorithm,\n fingerprint,\n isActive: true,\n expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // 90 days\n });\n\n // Update invitation status\n await tx\n .update(invitations)\n .set({\n status: 'accepted',\n acceptedAt: new Date(),\n updatedAt: new Date(),\n })\n .where(eq(invitations.id, invitation.id));\n\n return { newUser, role: role[0] };\n });\n\n console.log(`[Auth] ✅ Invitation accepted: ${invitation.email} as ${result.role.name}`);\n\n return {\n userId: result.newUser.id,\n email: result.newUser.email!,\n role: result.role.name,\n };\n}\n\n/**\n * List invitations with filtering and pagination\n *\n * @param params - Query parameters\n * @returns Paginated invitations\n *\n * @example\n * ```typescript\n * const result = await listInvitations({\n * status: 'pending',\n * invitedBy: 1n,\n * page: 1,\n * limit: 20\n * });\n * ```\n */\nexport async function listInvitations(params: {\n status?: InvitationStatus;\n invitedBy?: number;\n page?: number;\n limit?: number;\n}): Promise<{\n invitations: InvitationWithDetails[];\n total: number;\n page: number;\n limit: number;\n totalPages: number;\n}>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const { status, invitedBy, page = 1, limit = 20 } = params;\n const offset = (page - 1) * limit;\n\n // Build where conditions\n const conditions = [];\n if (status)\n {\n conditions.push(eq(invitations.status, status));\n }\n if (invitedBy)\n {\n conditions.push(eq(invitations.invitedBy, invitedBy));\n }\n\n const whereClause = conditions.length > 0 ? and(...conditions) : undefined;\n\n // Get total count\n const countResult = await db\n .select({ count: sql<number>`count(*)` })\n .from(invitations)\n .where(whereClause);\n\n const total = Number(countResult[0]?.count || 0);\n\n // Get paginated results with joins\n const results = await db\n .select({\n id: invitations.id,\n email: invitations.email,\n token: invitations.token,\n roleId: invitations.roleId,\n invitedBy: invitations.invitedBy,\n status: invitations.status,\n expiresAt: invitations.expiresAt,\n acceptedAt: invitations.acceptedAt,\n cancelledAt: invitations.cancelledAt,\n metadata: invitations.metadata,\n createdAt: invitations.createdAt,\n updatedAt: invitations.updatedAt,\n role: {\n id: roles.id,\n name: roles.name,\n displayName: roles.displayName,\n },\n inviter: {\n id: users.id,\n email: users.email,\n },\n })\n .from(invitations)\n .innerJoin(roles, eq(invitations.roleId, roles.id))\n .innerJoin(users, eq(invitations.invitedBy, users.id))\n .where(whereClause)\n .orderBy(desc(invitations.createdAt))\n .limit(limit)\n .offset(offset);\n\n return {\n invitations: results,\n total,\n page,\n limit,\n totalPages: Math.ceil(total / limit),\n };\n}\n\n/**\n * Cancel invitation\n *\n * Only pending invitations can be cancelled\n *\n * @param id - Invitation ID\n * @param cancelledBy - User ID who cancelled\n * @param reason - Optional cancellation reason\n * @throws Error if invitation cannot be cancelled\n */\nexport async function cancelInvitation(\n id: number,\n cancelledBy: number,\n reason?: string\n): Promise<void>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n // Get invitation\n const invitation = await db\n .select()\n .from(invitations)\n .where(eq(invitations.id, id))\n .limit(1);\n\n if (invitation.length === 0)\n {\n throw new Error('Invitation not found');\n }\n\n if (invitation[0].status !== 'pending')\n {\n throw new Error(`Cannot cancel ${invitation[0].status} invitation`);\n }\n\n // Update status\n await db\n .update(invitations)\n .set({\n status: 'cancelled',\n cancelledAt: new Date(),\n updatedAt: new Date(),\n metadata: invitation[0].metadata\n ? { ...invitation[0].metadata, cancelReason: reason, cancelledBy }\n : { cancelReason: reason, cancelledBy },\n })\n .where(eq(invitations.id, id));\n\n console.log(`[Auth] ⚠️ Invitation cancelled: ${invitation[0].email} (reason: ${reason || 'none'})`);\n}\n\n/**\n * Delete invitation\n *\n * Permanently removes invitation record\n * Typically only for superadmin cleanup\n *\n * @param id - Invitation ID\n */\nexport async function deleteInvitation(id: number): Promise<void>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n await db\n .delete(invitations)\n .where(eq(invitations.id, id));\n\n console.log(`[Auth] 🗑️ Invitation deleted: ${id}`);\n}\n\n/**\n * Expire old invitations (cron job)\n *\n * Updates status of expired pending invitations\n *\n * @returns Number of invitations expired\n */\nexport async function expireOldInvitations(): Promise<number>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n const now = new Date();\n\n // Find expired pending invitations\n const expiredInvitations = await db\n .select()\n .from(invitations)\n .where(\n and(\n eq(invitations.status, 'pending'),\n lt(invitations.expiresAt, now)\n )\n );\n\n if (expiredInvitations.length === 0)\n {\n return 0;\n }\n\n // Update to expired status\n await db\n .update(invitations)\n .set({\n status: 'expired',\n updatedAt: now,\n })\n .where(\n and(\n eq(invitations.status, 'pending'),\n lt(invitations.expiresAt, now)\n )\n );\n\n console.log(`[Auth] ⏰ Expired ${expiredInvitations.length} old invitations`);\n\n return expiredInvitations.length;\n}\n\n/**\n * Resend invitation email\n *\n * Extends expiration and triggers email resend\n *\n * @param id - Invitation ID\n * @param expiresInDays - New expiration period (default: 7)\n * @returns Updated invitation\n * @throws Error if invitation cannot be resent\n */\nexport async function resendInvitation(\n id: number,\n expiresInDays: number = 7\n): Promise<Invitation>\n{\n const db = getDatabase();\n\n if (!db)\n {\n throw new Error('[Auth] Database not initialized');\n }\n\n // Get invitation\n const invitation = await db\n .select()\n .from(invitations)\n .where(eq(invitations.id, id))\n .limit(1);\n\n if (invitation.length === 0)\n {\n throw new Error('Invitation not found');\n }\n\n // Can only resend pending or expired invitations\n if (!['pending', 'expired'].includes(invitation[0].status))\n {\n throw new Error(`Cannot resend ${invitation[0].status} invitation`);\n }\n\n // Update expiration and status\n const newExpiresAt = calculateExpiresAt(expiresInDays);\n\n const [updated] = await db\n .update(invitations)\n .set({\n status: 'pending',\n expiresAt: newExpiresAt,\n updatedAt: new Date(),\n })\n .where(eq(invitations.id, id))\n .returning();\n\n console.log(`[Auth] 📧 Invitation resent: ${invitation[0].email} (new expiry: ${newExpiresAt.toISOString()})`);\n\n return updated;\n}","/**\n * @spfn/auth - Authentication Middleware\n *\n * Verify client-signed JWT token with public key\n *\n * Flow:\n * 1. Extract Authorization header\n * 2. Decode JWT to extract keyId\n * 3. Fetch public key from database\n * 4. Check key expiration\n * 5. Verify JWT signature with public key\n * 6. Validate user status\n * 7. Update last used timestamp\n * 8. Attach user to context\n *\n * Security Checks:\n * - Token signature verification\n * - Key expiration check\n * - User status check (active/inactive/suspended)\n * - Key revocation check (isActive flag)\n */\n\nimport type { Context, Next } from 'hono';\nimport { verifyClientToken } from '@/server/helpers/jwt';\nimport { findOne, getDatabase } from '@spfn/core/db';\nimport { users, userPublicKeys } from '@/server/entities';\nimport type { User } from '@/server/entities/users';\nimport {\n InvalidTokenError,\n TokenExpiredError,\n KeyExpiredError,\n AccountDisabledError,\n} from '@/server/errors';\nimport { UnauthorizedError } from '@spfn/core/errors';\nimport { eq, and } from 'drizzle-orm';\n\n// Auth context type\nexport interface AuthContext\n{\n user: User;\n userId: string;\n keyId: string;\n}\n\n// Extend Hono context with auth\ndeclare module 'hono'\n{\n interface ContextVariableMap\n {\n auth: AuthContext;\n }\n}\n\n/**\n * Authentication middleware\n *\n * Verifies client-signed JWT token using stored public key\n * Must be applied to routes that require authentication\n *\n * @example\n * ```typescript\n * // In route file\n * app.bind(logoutContract, [authenticate], async (c) => {\n * const auth = c.raw.get('auth'); // Get auth context\n * const { user, userId, keyId } = auth;\n * // Or access directly: c.raw.get('auth').user\n * });\n * ```\n */\nexport async function authenticate(c: Context, next: Next): Promise<Response | void>\n{\n // Extract Authorization header\n const authHeader = c.req.header('Authorization');\n\n // Validate Authorization header format\n if (!authHeader || !authHeader.startsWith('Bearer '))\n {\n throw new UnauthorizedError('Missing or invalid authorization header');\n }\n\n const token = authHeader.substring(7); // Remove 'Bearer ' prefix\n\n // 1. Decode JWT to extract keyId (without verification)\n // We need keyId to fetch the public key for verification\n const { decodeToken } = await import('@/server/helpers/jwt');\n const decoded = decodeToken(token);\n\n if (!decoded || !decoded.keyId)\n {\n throw new UnauthorizedError('Invalid token: missing keyId');\n }\n\n const keyId = decoded.keyId as string;\n\n // 2. Get public key from database\n // Query conditions:\n // - keyId matches (UUID)\n // - isActive = true (not revoked)\n const db = getDatabase()!;\n const [keyRecord] = await db\n .select()\n .from(userPublicKeys)\n .where(\n and(\n eq(userPublicKeys.keyId, keyId),\n eq(userPublicKeys.isActive, true)\n )\n );\n\n if (!keyRecord)\n {\n throw new UnauthorizedError('Invalid or revoked key');\n }\n\n // 3. Check key expiration\n // Keys expire after 90 days by default\n if (keyRecord.expiresAt && new Date() > keyRecord.expiresAt)\n {\n throw new KeyExpiredError();\n }\n\n // 4. Verify JWT signature with public key\n // This validates:\n // - Signature matches (client signed with private key)\n // - Token not expired (15min default)\n // - Issuer is 'spfn-client'\n try\n {\n verifyClientToken(\n token,\n keyRecord.publicKey,\n keyRecord.algorithm as 'ES256' | 'RS256'\n );\n }\n catch (err)\n {\n // Handle JWT verification errors\n if (err instanceof Error)\n {\n // Token expired (15min TTL)\n if (err.name === 'TokenExpiredError')\n {\n throw new TokenExpiredError();\n }\n\n // Invalid signature\n if (err.name === 'JsonWebTokenError')\n {\n throw new InvalidTokenError('Invalid token signature');\n }\n }\n\n // Generic authentication failure\n throw new UnauthorizedError('Authentication failed');\n }\n\n // 5. Get user from database\n const user = await findOne(users, { id: keyRecord.userId });\n if (!user)\n {\n throw new UnauthorizedError('User not found');\n }\n\n // 6. Check if user account is active\n // Status can be: active, inactive, suspended\n if (user.status !== 'active')\n {\n throw new AccountDisabledError(user.status);\n }\n\n // 7. Update last used timestamp (fire-and-forget)\n // Don't await to avoid blocking the request\n // Useful for:\n // - Security audits\n // - Detecting inactive keys\n // - Key rotation reminders\n db.update(userPublicKeys)\n .set({ lastUsedAt: new Date() })\n .where(eq(userPublicKeys.id, keyRecord.id))\n .execute()\n .catch((err: unknown) => console.error('Failed to update lastUsedAt:', err));\n\n // 8. Attach auth data to context\n // Available in downstream route handlers via c.get('auth')\n c.set('auth', {\n user,\n userId: String(user.id),\n keyId,\n });\n\n // Continue to route handler\n await next();\n}","/**\n * @spfn/auth - Permission Middleware\n *\n * Middleware functions for permission-based access control\n */\n\nimport type { Context, Next } from 'hono';\nimport { getAuth } from '@/server/helpers/context';\nimport { hasAllPermissions, hasAnyPermission } from '@/server/services/permission.service';\nimport { ForbiddenError } from '@spfn/core/errors';\n\n/**\n * Require user to have all specified permissions\n *\n * Must be used after authenticate middleware\n *\n * @param permissionNames - Permission names (e.g., 'user:delete', 'post:publish')\n * @returns Middleware function\n *\n * @example\n * ```typescript\n * app.bind(\n * deleteUserContract,\n * [authenticate, requirePermissions('user:delete')],\n * async (c) => {\n * // Only users with user:delete permission\n * }\n * );\n *\n * // Multiple permissions (all required)\n * app.bind(\n * publishPostContract,\n * [authenticate, requirePermissions('post:write', 'post:publish')],\n * async (c) => {\n * // Needs both permissions\n * }\n * );\n * ```\n */\nexport function requirePermissions(...permissionNames: string[])\n{\n return async (c: Context, next: Next): Promise<void> =>\n {\n const auth = getAuth(c);\n\n if (!auth)\n {\n throw new ForbiddenError('Authentication required');\n }\n\n const { userId } = auth;\n\n const allowed = await hasAllPermissions(userId, permissionNames);\n\n if (!allowed)\n {\n throw new ForbiddenError(\n `Missing required permissions: ${permissionNames.join(', ')}`\n );\n }\n\n await next();\n };\n}\n\n/**\n * Require user to have at least one of the specified permissions\n *\n * Must be used after authenticate middleware\n *\n * @param permissionNames - Permission names\n * @returns Middleware function\n *\n * @example\n * ```typescript\n * app.bind(\n * viewContentContract,\n * [authenticate, requireAnyPermission('content:read', 'admin:access')],\n * async (c) => {\n * // User has either content:read OR admin:access\n * }\n * );\n * ```\n */\nexport function requireAnyPermission(...permissionNames: string[])\n{\n return async (c: Context, next: Next): Promise<void> =>\n {\n const auth = getAuth(c);\n\n if (!auth)\n {\n throw new ForbiddenError('Authentication required');\n }\n\n const { userId } = auth;\n\n const allowed = await hasAnyPermission(userId, permissionNames);\n\n if (!allowed)\n {\n throw new ForbiddenError(\n `Requires one of: ${permissionNames.join(', ')}`\n );\n }\n\n await next();\n };\n}","/**\n * @spfn/auth - Role Middleware\n *\n * Middleware functions for role-based access control\n */\n\nimport type { Context, Next } from 'hono';\nimport { getAuth } from '@/server/helpers/context';\nimport { hasAnyRole } from '@/server/services/permission.service';\nimport { ForbiddenError } from '@spfn/core/errors';\n\n/**\n * Require user to have one of the specified roles\n *\n * Must be used after authenticate middleware\n *\n * @param roleNames - Role names (e.g., 'admin', 'superadmin')\n * @returns Middleware function\n *\n * @example\n * ```typescript\n * app.bind(\n * adminDashboardContract,\n * [authenticate, requireRole('admin', 'superadmin')],\n * async (c) => {\n * // Only admin or superadmin\n * }\n * );\n *\n * // Single role\n * app.bind(\n * systemConfigContract,\n * [authenticate, requireRole('superadmin')],\n * async (c) => {\n * // Only superadmin\n * }\n * );\n * ```\n */\nexport function requireRole(...roleNames: string[])\n{\n return async (c: Context, next: Next): Promise<void> =>\n {\n const auth = getAuth(c);\n\n if (!auth)\n {\n throw new ForbiddenError('Authentication required');\n }\n\n const { userId } = auth;\n\n const allowed = await hasAnyRole(userId, roleNames);\n\n if (!allowed)\n {\n throw new ForbiddenError(\n `Required roles: ${roleNames.join(', ')}`\n );\n }\n\n await next();\n };\n}","/**\n * @spfn/auth - Setup Functions\n *\n * Initial setup and admin account creation\n */\n\nimport { findOne, create } from '@spfn/core/db';\nimport { logger } from '@spfn/core/logger';\nimport { users } from '@/server/entities';\nimport { hashPassword } from '@/server/helpers';\nimport { getRoleByName } from '@/server/services/role.service';\n\nconst authLogger = logger.child('@spfn/auth');\n\n/**\n * Admin account configuration\n */\ninterface AdminAccountConfig\n{\n email: string;\n password: string;\n role?: string; // Role name (e.g., 'user', 'admin', 'superadmin')\n phone?: string;\n passwordChangeRequired?: boolean;\n}\n\n/**\n * Parse admin accounts from environment variables\n *\n * Supports three formats (in priority order):\n *\n * 1. JSON format (ADMIN_ACCOUNTS):\n * ```\n * ADMIN_ACCOUNTS='[{\"email\":\"admin@example.com\",\"password\":\"pass\",\"role\":\"superadmin\"}]'\n * ```\n *\n * 2. Comma-separated format (ADMIN_EMAILS + ADMIN_PASSWORDS + ADMIN_ROLES):\n * ```\n * ADMIN_EMAILS=admin@example.com,user@example.com\n * ADMIN_PASSWORDS=admin-pass,user-pass\n * ADMIN_ROLES=superadmin,user\n * ```\n *\n * 3. Single account format (legacy, ADMIN_EMAIL + ADMIN_PASSWORD):\n * ```\n * ADMIN_EMAIL=admin@example.com\n * ADMIN_PASSWORD=admin-password\n * ```\n *\n * @returns Array of admin account configurations\n */\nfunction parseAdminAccounts(): AdminAccountConfig[]\n{\n const accounts: AdminAccountConfig[] = [];\n\n // Method 1: JSON format (highest priority)\n if (process.env.SPFN_AUTH_ADMIN_ACCOUNTS || process.env.ADMIN_ACCOUNTS)\n {\n try\n {\n const accountsJson =\n process.env.SPFN_AUTH_ADMIN_ACCOUNTS || // New prefixed version (recommended)\n process.env.ADMIN_ACCOUNTS; // Legacy fallback\n\n const parsed = JSON.parse(accountsJson!);\n\n if (!Array.isArray(parsed))\n {\n authLogger.error('❌ SPFN_AUTH_ADMIN_ACCOUNTS must be an array');\n return accounts;\n }\n\n for (const item of parsed)\n {\n if (!item.email || !item.password)\n {\n authLogger.warn('⚠️ Skipping account: missing email or password');\n continue;\n }\n\n accounts.push({\n email: item.email,\n password: item.password,\n role: item.role || 'user',\n phone: item.phone,\n passwordChangeRequired: item.passwordChangeRequired !== false, // Default: true\n });\n }\n\n return accounts;\n }\n catch (error)\n {\n const err = error as Error;\n authLogger.error('❌ Failed to parse SPFN_AUTH_ADMIN_ACCOUNTS:', err);\n return accounts;\n }\n }\n\n // Method 2: Comma-separated format\n const adminEmails =\n process.env.SPFN_AUTH_ADMIN_EMAILS || // New prefixed version (recommended)\n process.env.ADMIN_EMAILS; // Legacy fallback\n\n if (adminEmails)\n {\n const emails = adminEmails.split(',').map(s => s.trim());\n const passwords = (\n process.env.SPFN_AUTH_ADMIN_PASSWORDS || // New prefixed version (recommended)\n process.env.ADMIN_PASSWORDS || // Legacy fallback\n ''\n ).split(',').map(s => s.trim());\n const roles = (\n process.env.SPFN_AUTH_ADMIN_ROLES || // New prefixed version (recommended)\n process.env.ADMIN_ROLES || // Legacy fallback\n ''\n ).split(',').map(s => s.trim());\n\n // Validate lengths match\n if (passwords.length !== emails.length)\n {\n authLogger.error('❌ SPFN_AUTH_ADMIN_EMAILS and SPFN_AUTH_ADMIN_PASSWORDS length mismatch');\n return accounts;\n }\n\n for (let i = 0; i < emails.length; i++)\n {\n const email = emails[i];\n const password = passwords[i];\n const role = roles[i] || 'user';\n\n if (!email || !password)\n {\n authLogger.warn(`⚠️ Skipping account ${i + 1}: missing email or password`);\n continue;\n }\n\n accounts.push({\n email,\n password,\n role,\n passwordChangeRequired: true,\n });\n }\n\n return accounts;\n }\n\n // Method 3: Single account (legacy format)\n const adminEmail =\n process.env.SPFN_AUTH_ADMIN_EMAIL || // New prefixed version (recommended)\n process.env.ADMIN_EMAIL; // Legacy fallback\n\n const adminPassword =\n process.env.SPFN_AUTH_ADMIN_PASSWORD || // New prefixed version (recommended)\n process.env.ADMIN_PASSWORD; // Legacy fallback\n\n if (adminEmail && adminPassword)\n {\n accounts.push({\n email: adminEmail,\n password: adminPassword,\n role: 'superadmin',\n passwordChangeRequired: true,\n });\n }\n\n return accounts;\n}\n\n/**\n * Ensure admin accounts exist from environment variables\n *\n * Supports multiple admin account creation via three formats:\n * 1. JSON format (SPFN_AUTH_ADMIN_ACCOUNTS)\n * 2. Comma-separated format (SPFN_AUTH_ADMIN_EMAILS + SPFN_AUTH_ADMIN_PASSWORDS + SPFN_AUTH_ADMIN_ROLES)\n * 3. Single account format (SPFN_AUTH_ADMIN_EMAIL + SPFN_AUTH_ADMIN_PASSWORD) - legacy\n *\n * Default behavior for created accounts:\n * - emailVerifiedAt: current timestamp (auto-verified)\n * - passwordChangeRequired: true (must change on first login)\n * - status: 'active'\n *\n * @example\n * ```typescript\n * // In your server startup code:\n * import { ensureAdminExists } from '@spfn/auth/server';\n *\n * await ensureAdminExists();\n * ```\n */\nexport async function ensureAdminExists(): Promise<void>\n{\n const accounts = parseAdminAccounts();\n\n // Skip if no accounts configured\n if (accounts.length === 0)\n {\n return;\n }\n\n authLogger.info(`Creating ${accounts.length} admin account(s)...`);\n\n let created = 0;\n let skipped = 0;\n let failed = 0;\n\n for (const account of accounts)\n {\n authLogger.info(`Creating ${account.email} admin account(s)...`);\n\n try\n {\n // Check if account already exists\n const existing = await findOne(users, { email: account.email });\n\n if (existing)\n {\n authLogger.info(`⚠️ Account already exists: ${account.email} (skipped)`);\n skipped++;\n continue;\n }\n\n // Get role ID from role name\n const roleName = account.role || 'user';\n const role = await getRoleByName(roleName);\n\n if (!role)\n {\n authLogger.error(`❌ Role '${roleName}' not found for ${account.email}. Run initializeAuth() first.`);\n failed++;\n continue;\n }\n\n // Hash password\n const passwordHash = await hashPassword(account.password);\n\n // Create admin account\n await create(users, {\n email: account.email,\n phone: account.phone || null,\n passwordHash,\n roleId: role.id,\n emailVerifiedAt: new Date(), // Auto-verify admin\n passwordChangeRequired: account.passwordChangeRequired !== false,\n status: 'active',\n });\n\n authLogger.info(`✅ Admin account created: ${account.email} (${roleName})`);\n created++;\n }\n catch (error)\n {\n const err = error as Error;\n authLogger.error(`❌ Failed to create account ${account.email}:`, err);\n failed++;\n }\n }\n\n // Summary\n authLogger.info(`📊 Summary: ${created} created, ${skipped} skipped, ${failed} failed`);\n\n if (created > 0)\n {\n authLogger.info('⚠️ Please change passwords on first login!');\n }\n}"],"mappings":"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","names":["text","boolean","index","id","timestamps","text","timestamp","id","timestamps","text","timestamp","boolean","index","id","foreignKey","text","timestamp","index","id","timestamps","text","timestamp","bigint","index","id","timestamps","text","boolean","index","id","timestamps","bigint","index","id","timestamps","bigint","boolean","text","timestamp","index","unique","id","timestamps","getDatabase","eq","and","findOne","create","ValidationError","jwt","create","getDatabase","eq","and","create","getDatabase","findOne","ValidationError","getRoleByName","create","updateOne","getDatabase","eq","and","getDatabase","eq","and","getDatabase","eq","and","getDatabase","eq","and","getDatabase","eq","and","sql","crypto","getDatabase","eq","and","userPublicKeys","sql","id","findOne","getDatabase","UnauthorizedError","eq","and","decodeToken","getDatabase","findOne","ForbiddenError","ForbiddenError","findOne","create","logger","authLogger","logger","roles","findOne","create"]}