modelence 0.26.0-nd.dev.1 → 0.26.0-nd.dev.10

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/system/index.ts","../src/db/client.ts","../src/websocket/socketio/server.ts","../src/auth/disposableEmails.ts","../src/auth/validators.ts","../src/auth/utils.ts","../src/app/emailConfig.ts","../src/app/authConfig.ts","../src/auth/login.ts","../src/rate-limit/db.ts","../src/rate-limit/rules.ts","../src/auth/profile.ts","../src/auth/types.ts","../src/auth/unlinkOAuthProvider.ts","../src/auth/templates/emailVerficationTemplate.ts","../src/auth/verification.ts","../src/auth/signup.ts","../src/auth/resetPassword.ts","../src/auth/templates/magicLinkTemplate.ts","../src/auth/magicLink.ts","../src/auth/loginWithOAuth.ts","../src/auth/user.ts","../src/config/local.ts","../src/app/instance.ts","../src/app/backendApi.ts","../src/config/sync.ts","../src/lock/db.ts","../src/lock/helpers.ts","../src/cron/jobs.ts","../src/data/resolveStores.ts","../src/lock/module.ts","../src/migration/db.ts","../src/migration/index.ts","../src/rate-limit/index.ts","../src/files/index.ts","../src/ssr/escapeJsonForScript.ts","../src/viteServer.ts","../src/app/managedEmailProvider.ts","../src/app/securityConfig.ts","../src/app/websocketConfig.ts","../src/app/index.ts","../src/auth/deleteUser.ts","../src/websocket/serverChannel.ts","../src/app/email.ts","../src/auth/providers/mobileRedirect.ts","../src/auth/providers/oauthErrorRedirect.ts","../src/auth/providers/oauthCommon.ts","../src/auth/providers/google.ts","../src/auth/providers/github.ts","../src/routes/handler.ts","../src/app/server.ts"],"names":["system_default","Module","isSetupRequired","client","connect","mongodbUri","getMongodbUri","maxPoolSize","MongoClient","package_default","err","getClient","socketServer","COLLECTION","ADAPTER_TTL_SECONDS","init","httpServer","channels","mongodbClient","isMultiInstance","getConfig","mongoCollection","error","retryError","SocketServer","createAdapter","socket","next","token","authenticate","handleLiveQueryDisconnect","channelName","category","authorized","channel","payload","handleSubscribeLiveQuery","handleUnsubscribeLiveQuery","broadcast","id","data","server_default","isDisposableEmail","email","emailParts","domain","dbDisposableEmailDomains","updateDisposableEmailListCron","time","response","domains","now","batchSize","i","batch","MIN_HANDLE_LENGTH","MAX_HANDLE_LENGTH","HANDLE_REGEX","MIN_PASSWORD_LENGTH","MAX_PASSWORD_LENGTH","MAX_EMAIL_LENGTH","trimmedNonEmptyString","opts","z","trimmedOptionalString","val","handleSchema","profileFieldsSchema","validateProfileFields","fields","result","issue","path","msg","ValidationError","validatePassword","value","validateEmail","validateHandle","isDuplicateEmailError","MongoServerError","resolveUrl","baseUrl","configuredUrl","serializeUserForClient","userDoc","findAvailableHandle","baseHandle","truncatedBase","usersCollection","MAX_SUFFIX_VALUE","suffix","suffixStr","candidate","MAX_RANDOM_ATTEMPTS","attempt","randomSuffix","randomBytes","resolveUniqueHandle","rawHandle","throwOnConflict","handle","emailConfig","setEmailConfig","newEmailConfig","getEmailConfig","authConfig","setAuthConfig","newAuthConfig","getAuthConfig","isEmailVerificationRequired","SALT_ROUNDS","DUMMY_PASSWORD_HASH","bcrypt","handleLoginWithPassword","args","user","session","connectionInfo","res","ip","consumeRateLimit","password","passwordHash","hashToCompare","isValidPassword","incorrectCredentialsError","e","AuthError","setSessionUser","setAuthTokenCookie","handleLogout","clearSessionUser","clearAuthTokenCookie","dbRateLimits","Store","schema","allRules","initRateLimits","rateLimits","options","bucket","type","message","rules","rule","createError","RateLimitError","checkRateLimitRule","createRateLimitError","filter","record","currentWindowStart","count","modifier","getCount","prevWindowStart","currentWindowCount","prevWindowCount","prevWindowWeight","weight","getOwnProfile","props","profile","handleUpdateProfile","rawProps","isHandleUnchanged","_ignoredHandle","propsWithoutHandle","update","setFields","unsetFields","key","mongoUpdate","clearedFields","SUPPORTED_OAUTH_PROVIDERS","handleUnlinkOAuthProvider","provider","methods","otherMethods","otherMethodGuard","emailVerificationTemplate","name","verificationUrl","USER_COLLATION","verifyEmailToken","tokenDoc","emailVerificationTokensCollection","updatedUserDoc","existingEmailDoc","handleVerifyEmail","params","emailVerifiedRedirectUrl","authToken","createSession","sendVerificationEmail","userId","requestBaseUrl","emailProvider","verificationToken","expiresAt","htmlTemplate","textContent","htmlToText","resendVerificationResponse","handleResendEmailVerification","emailDoc","handleSignupWithPassword","signupProps","firstName","lastName","avatarUrl","profileFields","resolvedHandle","generated","hash","userDocument","RESET_PASSWORD_COOKIE","RESET_PASSWORD_COOKIE_PATH","RESET_PASSWORD_COOKIE_OPTIONS","findResetTokenDoc","rawToken","resetPasswordTokensCollection","hashToken","claimResetTokenById","defaultPasswordResetTemplate","resetUrl","passwordResetSent","handleSendResetPasswordToken","resetToken","createdAt","handleResetPasswordLanding","resetPasswordUrl","resetTokenDoc","handleResetPassword","context","cookieToken","clearCookie","invalidateAllUserSessions","userEmails","address","magicLinkTokensCollection","magicLinkTemplate","magicLinkUrl","code","MAGIC_LINK_COOKIE","MAGIC_LINK_COOKIE_PATH","MAGIC_LINK_COOKIE_OPTIONS","MAGIC_LINK_EXPIRY_MINUTES","ONE_TIME_CODE_LENGTH","MAX_CODE_ATTEMPTS","fireAuthHook","invoke","hookError","isMagicLinkEnabled","isMagicLinkSignupEnabled","findMagicLinkTokenDoc","claimMagicLinkTokenById","restoreClaimedMagicLinkToken","claimedToken","restoreError","magicLinkSent","handleSendMagicLink","fromAddress","magicLinkToken","oneTimeCode","randomInt","handleMagicLinkLanding","magicLinkPageUrl","authenticateMagicLinkUser","signupEnabled","isSignupAttempt","upsertResult","upsertMagicLinkUser","isNew","currentUserDoc","wasUnverified","hookName","hook","canInsert","runUpsert","doc","handleLoginWithMagicLink","req","handleLoginWithOneTimeCode","invalidCodeError","handleLoginWithOAuth","codeVerifier","claimed","consumeOAuthExchangeCode","ObjectId","ruleKey","defaultAuthRateLimits","collectOverrides","config","overrides","buckets","buildAuthRateLimits","defaults","overrideByKey","merged","consumed","def","override","user_default","localConfigMap","formatLocalConfigValue","numValue","getConfigsFromEnvMap","configMap","configSchema","configs","envVar","configKey","configSchemaEntry","getLocalSiteUrl","port","getLocalConfigs","variant","instanceId","getMachineKey","dir","os","file","existing","fs","randomUUID","getInstanceId","createHash","isDevRuntime","runtime","isLocalRuntime","getContainerId","connectCloudBackend","cronJobsMetadata","stores","roles","containerId","takeover","dataStores","store","callCloudApi","cloudError","detail","fetchConfigs","syncStatus","endpoint","method","MODELENCE_SERVICE_ENDPOINT","MODELENCE_SERVICE_TOKEN","parsed","messageDetail","errorField","isSyncing","SYNC_INTERVAL","startConfigSync","sync","syncConfig","loadRemoteConfigs","loadConfigs","getSchema","locksCollection","lockCache","DEFAULT_CACHE_DURATION","INSTANCE_ID","DEFAULT_LOCK_DURATION","lockHeartbeats","isDuplicateKeyError","MongoError","hasKeyPatternField","field","shouldFallbackToLegacyStrategy","resource","existingLock","tryAcquireLockById","staleThresholdDate","deleteLegacyLock","selector","stopLockHeartbeat","heartbeatKey","heartbeat","startLockHeartbeat","lockDuration","heartbeatInterval","existingHeartbeat","scheduleRefresh","acquireLock","isLockAcquired","logDebug","successfulLockCacheDuration","failedLockCacheDuration","bypassCache","acquireLockById","releaseLock","cronJobs","cronJobsInterval","cronJobsCollection","defineCronJob","alias","description","interval","timeout","handler","startCronJobs","aliasList","aliasSelector","cronJobRecords","job","tickCronJobs","state","timeoutError","captureError","runCronJob","transaction","startTransaction","handleCronJobCompletion","getCronJobsMetadata","jobs_default","resolveStores","uniqueStores","rootToTail","root","effectiveStores","nameToRoot","tail","module_default","dbMigrations","runMigrations","migrations","lockMode","logInfo","versions","version","existingVersions","existingVersionSet","pendingMigrations","outputStr","maxSize","truncatedOutput","startMigrations","migration_default","rate_limit_default","getUploadUrl","filePath","contentType","visibility","deleteFile","downloadFile","getFileUrl","files_default","escapeJsonForScript","json","ch","CLIENT_BUILD_DIR","SSR_BUILD_DIR","SSR_ENTRY_VIRTUAL_PATH","ROOT_PLACEHOLDER_REGEX","HEAD_CLOSE_TAG","ViteServer","createServer","ssrEntryPath","installSsrCallMethodTransport","staticOptions","staticFolders","express","isDocumentRequest","template","snapshot","renderSsrTreeStream","getCallContext","cssModule","callContext","cssAssets","sendEarlyHints","sessionState","pipe","getQueryState","rawPrelude","rootOpenTag","rawEpilogue","splitTemplateAtRoot","prelude","injectStylesheets","url","templatePath","_getSsrSnapshot","accept","pathname","lastSegment","dotIndex","ext","match","epilogue","linksHtml","headCloseIndex","links","target","loadUserViteConfig","appDir","loadConfigFromFile","safelyMergeConfig","baseConfig","userConfig","mergedConfig","mergeConfig","seenPlugins","plugin","pluginName","eslintConfigFile","plugins","reactPlugin","modelenceAssetPlugin","eslintPlugin","defineConfig","viteServer","extractFromName","from","normalizeAddressList","buildRequestBody","describeManagedEmailError","body","managedEmailProvider","securityConfig","normalizeOrigin","entry","trimmed","normalizeAllowedOrigins","allowedOrigins","setSecurityConfig","newSecurityConfig","validated","getSecurityConfig","websocketConfig","setWebsocketConfig","newWebsocketConfig","getWebsocketConfig","startApp","modules","defaultRoles","server","auth","security","websocket","ssr","dotenv","hasRemoteBackend","trackAppStart","module","systemModules","session_default","combinedModules","markAppStarted","initSystemMethods","initCustomMethods","initRoles","getConfigSchema","setSchema","rawStores","getStores","getChannels","defineCronJobs","getRateLimits","storesToInit","environmentId","appAlias","environmentAlias","telemetry","setMetadata","allStoresToInit","initStores","createIndexesAndMigrationsWithLock","initMetrics","startServer","createQuery","createMutation","_createSystemQuery","_createSystemMutation","warnIndexCreationFailure","storeName","MIGRATIONS_LOCK_RESOURCE","blockingStores","backgroundStores","createStoreIndexes","backgroundIndexCreationPromise","migrationPromise","backgroundIndexesResult","migrationResult","reconcileMode","absoluteKey","cronAlias","cronJobParams","serviceEndpoint","appDetails","getAppDetails","modelencePackageJson","packageJsonPath","packageJsonContent","clearTokens","disableUser","deleteUser","ServerChannel","canAccessChannel","websocketProvider","logError","sendEmail","FORBIDDEN_SCHEMES","parseRedirectUrl","getAllowedMobileRedirectUrls","fromConfig","fromAuthConfig","isAllowedMobileRedirectUrl","hasUserinfo","allowed","normalizePath","OUTCOME_PARAMS","buildMobileRedirect","redirectUri","ERROR_PARAMS","buildOAuthErrorRedirect","siteUrl","toOAuthOutcome","isBoundMobileOutcome","outcome","shouldFireLoginHooks","resolveUserIdFromLinkNonce","nonce","consumeLinkNonce","sendOAuthError","statusCode","errorMessage","errorCode","redirectUrl","html","authenticateUser","issueOAuthExchangeCode","handleExistingProviderLogin","userData","existingUser","handleExistingEmailLogin","existingUserByEmail","profileUpdate","updatedUser","handleNewUserSignup","newUser","getRedirectUri","handleOAuthUserAuthentication","clearOAuthLinkCookie","safelyCallHook","encodeOAuthState","mode","linkedUserId","platform","codeChallenge","decodeOAuthState","storedState","stateValue","encodedRedirectUri","validateOAuthStateAndGetMode","stateCookieName","decoded","mobileOutcome","resolveDecodedMobileOutcome","resolveMobileOutcomeFromCookie","describeRejectedRedirectUri","protocol","resolveMobileRedirectRequest","rawChallenge","hasValidChallenge","prepareOAuthInitiation","mobileRequest","mobileRedirectUri","handleOAuthProviderLink","resolvedUserId","providerField","currentUser","existingProviderId","validateOAuthCode","exchangeCodeForToken","clientId","clientSecret","tokenResponse","fetchGoogleUserInfo","accessToken","userInfoResponse","handleGoogleAuthenticationCallback","stateResult","googleClientId","googleClientSecret","tokenData","googleUser","getRouter","googleAuthRouter","Router","checkGoogleEnabled","_req","googleEnabled","initiation","authUrl","google_default","fetchGitHubUserInfo","fetchGitHubUserEmails","getGitHubUserEmail","githubUser","handleGitHubAuthenticationCallback","githubClientId","githubClientSecret","githubEmail","nameParts","githubAuthRouter","checkGitHubEnabled","githubEnabled","githubScopes","scopes","s","github_default","createRouteHandler","redactSensitive","ModelenceError","getBodyParserMiddleware","middlewares","jsonOptions","urlencodedOptions","rawOptions","defaultRawOptions","registerModuleRoutes","app","cors","route","handlers","globalProcessListenersRegistered","trustedProxies","trustProxy","parseTrustedProxies","cookieParser","securityHeadersMiddleware","corsPreflightMiddleware","corsRouteMiddleware","issueLinkNonce","methodName","sanitizeResult","runMethod","getResponseTypeMap","handleMethodError","http","keepAliveTimeoutMs","reason","promise","isOAuthCallback","clientInfo","getClientIp","getRequestBaseUrl","getUnauthenticatedRoles","parseZodError","parsingError","zodError","flattened","fieldMessages","errors","formMessages","frameAncestors","hasCustomAncestors","ancestors","applyCorsHeaders","origin","requestedHeaders","isPreflight","isAllowed","forwardedHost","host","entries","isPeerTrusted","peerIp","trust","clientIpHeader","forwardedIp","clientIp","normalizeIp","directIp"],"mappings":"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","file":"chunk-SLTP5FXB.js","sourcesContent":["import { Module } from '../app/module';\nimport { isSetupRequired } from '../app/setupStatus';\n\nexport default new Module('_system', {\n queries: {\n /*\n Polled by the built-in setup screen (dev only) so it can dismiss itself\n once the project is connected and the dev server restarted. Public and\n unauthenticated by design — it exposes a single boolean that is always\n false outside development.\n */\n setupStatus: async () => ({ setupRequired: isSetupRequired() }),\n },\n configSchema: {\n mongodbUri: {\n type: 'secret',\n isPublic: false,\n default: '',\n },\n mongodbPoolSize: {\n type: 'number',\n isPublic: false,\n default: 10,\n },\n 'env.type': {\n type: 'string',\n isPublic: true,\n default: '',\n },\n 'site.url': {\n type: 'string',\n isPublic: true,\n default: '',\n },\n multiInstance: {\n type: 'boolean',\n isPublic: false,\n default: false,\n },\n },\n});\n","import { MongoClient } from 'mongodb';\nimport systemModule from '../system';\nimport packageJson from '../../package.json';\n\nlet client: MongoClient | null = null;\n\nexport async function connect() {\n if (client) return client;\n\n const mongodbUri = getMongodbUri();\n if (!mongodbUri) {\n throw new Error('MongoDB URI is not set');\n }\n\n const maxPoolSize = systemModule.getConfig('mongodbPoolSize');\n\n client = new MongoClient(mongodbUri, {\n driverInfo: {\n name: 'Modelence',\n version: packageJson.version,\n },\n ignoreUndefined: true,\n maxPoolSize,\n });\n\n try {\n // Connect the client to the server\n await client.connect();\n // Send a ping to confirm a successful connection\n await client.db('admin').command({ ping: 1 });\n console.log('Pinged your deployment. You successfully connected to MongoDB!');\n return client;\n } catch (err) {\n console.error(err);\n client = null;\n throw err;\n }\n}\n\nexport function getMongodbUri() {\n return systemModule.getConfig('mongodbUri') || undefined;\n}\n\nexport function getClient() {\n return client;\n}\n\n// export async function closeConnection() {\n// if (client) {\n// await client.close();\n// client = null;\n// }\n// }\n","import { Server } from 'http';\nimport { Server as SocketServer, Socket } from 'socket.io';\nimport { createAdapter } from '@socket.io/mongo-adapter';\nimport { authenticate } from '@/auth';\nimport { getConfig } from '@/config/server';\nimport { getClient } from '@/db/client';\nimport { WebsocketServerProvider } from '../types';\nimport { ServerChannel } from '../serverChannel';\nimport {\n handleSubscribeLiveQuery,\n handleUnsubscribeLiveQuery,\n handleLiveQueryDisconnect,\n} from '@/live-query';\nimport type { Collection, Document } from 'mongodb';\n\nlet socketServer: SocketServer | null = null;\n\nconst COLLECTION = '_modelenceSocketio';\nconst ADAPTER_TTL_SECONDS = 60;\n\nexport async function init({\n httpServer,\n channels,\n}: {\n httpServer: Server;\n channels: ServerChannel[];\n}) {\n const mongodbClient = getClient();\n const isMultiInstance = Boolean(getConfig('_system.multiInstance'));\n\n console.log('Initializing Socket.IO server...');\n\n let mongoCollection: Collection<Document> | null = null;\n\n if (isMultiInstance && mongodbClient) {\n mongoCollection = mongodbClient.db().collection(COLLECTION);\n\n try {\n await mongoCollection.createIndex(\n { createdAt: 1 },\n { expireAfterSeconds: ADAPTER_TTL_SECONDS, background: true }\n );\n } catch (error: unknown) {\n if (error instanceof Error && 'code' in error && (error as { code: number }).code === 85) {\n try {\n await mongoCollection.dropIndex('createdAt_1');\n await mongoCollection.createIndex(\n { createdAt: 1 },\n { expireAfterSeconds: ADAPTER_TTL_SECONDS, background: true }\n );\n } catch (retryError: unknown) {\n console.error(\n 'Failed to recreate index on MongoDB collection for Socket.IO:',\n retryError\n );\n }\n } else {\n console.error('Failed to create index on MongoDB collection for Socket.IO:', error);\n }\n }\n }\n\n socketServer = new SocketServer(httpServer, {\n cors: {\n origin: '*',\n methods: ['GET', 'POST'],\n },\n adapter: mongoCollection ? createAdapter(mongoCollection) : undefined,\n transports: ['websocket'],\n perMessageDeflate: false,\n });\n\n socketServer.on('error', (error) => {\n console.error('Socket.IO error:', error);\n });\n\n socketServer.use(async (socket, next) => {\n const token = socket.handshake.auth.token;\n\n try {\n socket.data = await authenticate(token);\n next();\n } catch (error) {\n next(error instanceof Error ? error : new Error(String(error)));\n }\n });\n\n socketServer.on('connection', (socket: Socket) => {\n socket.on('disconnect', () => {\n handleLiveQueryDisconnect(socket);\n });\n\n socket.on('joinChannel', async (channelName: string) => {\n const [category] = channelName.split(':');\n let authorized = false;\n\n for (const channel of channels) {\n if (channel.category === category) {\n if (!channel.canAccessChannel || (await channel.canAccessChannel(socket.data))) {\n void socket.join(channelName);\n authorized = true;\n socket.emit('joinedChannel', channelName);\n }\n break; // Found matching channel - stop searching\n }\n }\n\n if (!authorized) {\n socket.emit('joinError', { channel: channelName, error: 'Access denied' });\n }\n });\n\n socket.on('leaveChannel', (channelName: string) => {\n void socket.leave(channelName);\n console.log(`User ${socket.id} left channel ${channelName}`);\n socket.emit('leftChannel', channelName);\n });\n\n socket.on('subscribeLiveQuery', (payload) => handleSubscribeLiveQuery(socket, payload));\n socket.on('unsubscribeLiveQuery', (payload) => handleUnsubscribeLiveQuery(socket, payload));\n });\n\n console.log('Socket.IO server initialized');\n}\n\nfunction broadcast<T>({ category, id, data }: { category: string; id: string; data: T }) {\n socketServer?.to(`${category}:${id}`).emit(category, data);\n}\n\nexport default {\n init,\n broadcast,\n} as WebsocketServerProvider;\n","import { time } from '../time';\nimport { dbDisposableEmailDomains } from './db';\n\nexport async function isDisposableEmail(email: string): Promise<boolean> {\n const emailParts = email.toLowerCase().trim().split('@');\n if (emailParts.length !== 2) {\n return false;\n }\n\n const domain = emailParts[1];\n const result = await dbDisposableEmailDomains.findOne({ domain });\n return Boolean(result);\n}\n\nexport const updateDisposableEmailListCron = {\n interval: time.days(1),\n async handler() {\n const response = await fetch(\n 'https://disposable.github.io/disposable-email-domains/domains.txt'\n );\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const domainsText = await response.text();\n\n const domains = domainsText\n .split('\\n')\n .map((domain) => domain.trim().toLowerCase())\n .filter((domain) => domain.length > 0);\n\n const now = new Date();\n\n // Insert domains in batches to avoid overwhelming the database\n const batchSize = 500;\n for (let i = 0; i < domains.length; i += batchSize) {\n const batch = domains.slice(i, i + batchSize);\n\n try {\n await dbDisposableEmailDomains.insertMany(\n batch.map((domain) => ({\n domain,\n addedAt: now,\n }))\n );\n } catch (error: unknown) {\n // MongoDB throws BulkWriteError when some documents are duplicates\n if (\n error &&\n typeof error === 'object' &&\n 'name' in error &&\n error.name === 'MongoBulkWriteError'\n ) {\n // console.warn(`Error inserting batch starting at index ${i}:`, error.message);\n }\n }\n }\n },\n};\n","import { z } from 'zod';\nimport { UpdateProfileProps } from '../methods/types';\nimport { ValidationError } from '../error';\n\nexport const MIN_HANDLE_LENGTH = 3;\nexport const MAX_HANDLE_LENGTH = 50;\nexport const HANDLE_REGEX = /^[a-zA-Z0-9_-]+$/;\n\nexport const MIN_PASSWORD_LENGTH = 8;\nexport const MAX_PASSWORD_LENGTH = 128;\n\nexport const MAX_EMAIL_LENGTH = 254;\n\n// Reusable string validators\nconst trimmedNonEmptyString = (opts: { min?: number; max: number }) =>\n z\n .string()\n .trim()\n .min(opts.min ?? 1, { message: `must be at least ${opts.min ?? 1} characters` })\n .max(opts.max, { message: `must be at most ${opts.max} characters` });\n\nconst trimmedOptionalString = (opts: { max: number }) =>\n z\n .string()\n .trim()\n .max(opts.max, { message: `must be at most ${opts.max} characters` })\n .transform((val) => (val === '' ? undefined : val))\n .optional();\n\nconst handleSchema = trimmedNonEmptyString({\n min: MIN_HANDLE_LENGTH,\n max: MAX_HANDLE_LENGTH,\n}).regex(HANDLE_REGEX, {\n message: 'must contain only letters, numbers, underscores, and hyphens',\n});\n\n// Base schema (used for both full & partial validation)\nconst profileFieldsSchema = z\n .object({\n firstName: trimmedOptionalString({ max: 50 }),\n lastName: trimmedOptionalString({ max: 50 }),\n avatarUrl: trimmedOptionalString({ max: 400 }),\n handle: handleSchema,\n })\n .strict();\n\n/**\n * Validates and trims profile fields against the defined rules.\n *\n * For each field present in `fields`:\n * - Trims whitespace\n * - Sets fields to `undefined` when cleared (empty string)\n * - Enforces min/max length constraints\n *\n * @returns An object containing only the validated, trimmed fields that were provided.\n */\nexport function validateProfileFields(\n fields: Partial<UpdateProfileProps>\n): Partial<UpdateProfileProps> {\n const result = profileFieldsSchema.partial().safeParse(fields);\n\n if (!result.success) {\n const issue = result.error.issues[0];\n const path = issue.path.join('.');\n const msg = path ? `${path}: ${issue.message}` : issue.message;\n throw new ValidationError(msg);\n }\n\n return result.data;\n}\n\nexport function validatePassword(value: string) {\n return z\n .string()\n .min(MIN_PASSWORD_LENGTH, {\n message: `Password must contain at least ${MIN_PASSWORD_LENGTH} characters`,\n })\n .max(MAX_PASSWORD_LENGTH, {\n message: `Password must be at most ${MAX_PASSWORD_LENGTH} characters`,\n })\n .parse(value);\n}\n\nexport function validateEmail(value: string) {\n return z\n .string()\n .max(MAX_EMAIL_LENGTH, {\n message: `Email must be at most ${MAX_EMAIL_LENGTH} characters`,\n })\n .email({ message: 'Invalid email address' })\n .parse(value)\n .toLowerCase();\n}\n\nexport function validateHandle(value: string) {\n return handleSchema.parse(value);\n}\n","import { randomBytes } from 'crypto';\nimport { MongoServerError } from 'mongodb';\nimport { usersCollection } from './db';\nimport { User } from './types';\nimport { validateHandle, MAX_HANDLE_LENGTH, MIN_HANDLE_LENGTH } from './validators';\n\n/**\n * Detects the duplicate-key error thrown when the unique `emails.address` index\n * rejects a concurrent second insert for the same email. The check-then-insert\n * in the signup paths can interleave, so this is the last line of defense that\n * turns a lost race into a clean \"already exists\" instead of an unhandled 500.\n */\nexport function isDuplicateEmailError(error: unknown): boolean {\n return (\n error instanceof MongoServerError &&\n error.code === 11000 &&\n // keyPattern is present on write errors and names the violated index's keys.\n typeof error.keyPattern === 'object' &&\n error.keyPattern !== null &&\n 'emails.address' in error.keyPattern\n );\n}\n\n/**\n * Resolves a configured redirect URL against the site base URL: absolute URLs\n * are used as-is, relative ones are appended to the base, and a missing value\n * falls back to the base URL itself.\n */\nexport function resolveUrl(baseUrl: string, configuredUrl?: string): string {\n if (!configuredUrl) {\n return baseUrl;\n }\n\n if (configuredUrl.startsWith('http://') || configuredUrl.startsWith('https://')) {\n return configuredUrl;\n }\n\n // Handle relative URL\n return `${baseUrl}${configuredUrl.startsWith('/') ? '' : '/'}${configuredUrl}`;\n}\n\nexport function serializeUserForClient(userDoc: User) {\n return {\n id: userDoc._id,\n handle: userDoc.handle,\n roles: userDoc.roles || [],\n firstName: userDoc.firstName ?? undefined,\n lastName: userDoc.lastName ?? undefined,\n avatarUrl: userDoc.avatarUrl ?? undefined,\n };\n}\n\n/**\nFinds an available handle by appending incremental suffixes (_2, _3, …)\nto the base handle until a unique one is found.\nThe base handle is truncated so that the suffixed candidate never exceeds MAX_HANDLE_LENGTH.\n */\nasync function findAvailableHandle(baseHandle: string): Promise<string> {\n // Truncate base handle to MAX_HANDLE_LENGTH so the unsuffixed form is valid.\n const truncatedBase = baseHandle.slice(0, MAX_HANDLE_LENGTH);\n\n // Check the unsuffixed base handle first.\n try {\n const firstCheck = await usersCollection.findOne(\n { handle: truncatedBase },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (!firstCheck) {\n return truncatedBase;\n }\n } catch (err) {\n throw new Error(`Database error while checking handle availability: ${err}`);\n }\n\n // Try sequential suffixes _2 through _51 (50 attempts).\n const MAX_SUFFIX_VALUE = 51;\n\n for (let suffix = 2; suffix <= MAX_SUFFIX_VALUE; suffix++) {\n const suffixStr = `_${suffix}`;\n const candidate = `${truncatedBase.slice(0, MAX_HANDLE_LENGTH - suffixStr.length)}${suffixStr}`;\n\n try {\n const conflict = await usersCollection.findOne(\n { handle: candidate },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (!conflict) {\n return candidate;\n }\n } catch (err) {\n throw new Error(`Database error while checking handle \"${candidate}\": ${err}`);\n }\n }\n\n // Fallback: sequential suffixes exhausted — use random hex suffixes.\n // Limit the number of attempts to avoid an infinite loop in case of persistent DB issues\n const MAX_RANDOM_ATTEMPTS = 10;\n\n for (let attempt = 0; attempt < MAX_RANDOM_ATTEMPTS; attempt++) {\n const randomSuffix = `_${randomBytes(3).toString('hex')}`;\n const candidate = `${truncatedBase.slice(0, MAX_HANDLE_LENGTH - randomSuffix.length)}${randomSuffix}`;\n\n try {\n const conflict = await usersCollection.findOne(\n { handle: candidate },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (!conflict) {\n return candidate;\n }\n } catch (err) {\n throw new Error(`Database error while checking handle \"${candidate}\": ${err}`);\n }\n }\n\n throw new Error(\n `Could not generate a unique handle for base \"${baseHandle}\" after exhausting all attempts.`\n );\n}\n\n/**\n * Resolves a unique handle for a new user.\n *\n * If the caller supplied a handle it is validated and checked for uniqueness.\n * - When `throwOnConflict` is true (default), an error is thrown if the handle is taken.\n * - When `throwOnConflict` is false, a numeric suffix (_2, _3, …) is appended to make\n * it unique. This is intended for system-generated handles (e.g. from `generateHandle`).\n *\n * If no handle is supplied, the local part of the email address is used as the base handle,\n * and a numeric suffix is appended until an unused handle is found.\n */\nexport async function resolveUniqueHandle(\n rawHandle: string | undefined,\n email: string,\n { throwOnConflict = true }: { throwOnConflict?: boolean } = {}\n): Promise<string> {\n if (rawHandle !== undefined && rawHandle !== null && String(rawHandle).trim() !== '') {\n // Caller explicitly provided a handle – trim and validate it.\n const handle = validateHandle(String(rawHandle).trim());\n\n if (throwOnConflict) {\n const existing = await usersCollection.findOne(\n { handle },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (existing) {\n throw new Error('Handle already taken.');\n }\n\n return handle;\n }\n\n // System-generated handle – find an available variant with suffix if needed.\n return findAvailableHandle(handle);\n }\n\n // Derive handle from the email local-part (everything before '@').\n // Sanitize non-alphanumeric/underscore/hyphen characters to satisfy HANDLE_REGEX.\n // Truncate to MAX_HANDLE_LENGTH since RFC 5321 allows local parts up to 64 chars.\n const rawBase = email.split('@')[0].replace(/[^a-zA-Z0-9_-]/g, '_');\n const baseHandle = rawBase.padEnd(MIN_HANDLE_LENGTH, '_').slice(0, MAX_HANDLE_LENGTH);\n return findAvailableHandle(baseHandle);\n}\n","import type { EmailProvider } from '../types';\n\nexport type EmailConfig = {\n provider?: EmailProvider;\n from?: string;\n verification?: {\n subject?: string;\n template?: (props: { name: string; email: string; verificationUrl: string }) => string;\n redirectUrl?: string;\n };\n passwordReset?: {\n subject?: string;\n template?: (props: { name: string; email: string; resetUrl: string }) => string;\n redirectUrl?: string;\n };\n magicLink?: {\n subject?: string;\n /**\n * Custom email template. Receives both credentials for the sign-in:\n * `magicLinkUrl` (the clickable link) and `code` (the typed one-time\n * code for `loginWithOneTimeCode`) — render either or both.\n */\n template?: (props: {\n name: string;\n email: string;\n magicLinkUrl: string;\n code: string;\n }) => string;\n /** SPA page the landing route redirects to; it must call `loginWithMagicLink()`. */\n redirectUrl?: string;\n };\n // @deprecated use verification.redirectUrl instead\n emailVerifiedRedirectUrl?: string;\n};\n\nlet emailConfig: EmailConfig = Object.freeze({});\n\nexport function setEmailConfig(newEmailConfig: EmailConfig) {\n emailConfig = Object.freeze(Object.assign({}, emailConfig, newEmailConfig));\n}\n\nexport function getEmailConfig() {\n return emailConfig;\n}\n","import { AuthErrorProps, AuthSuccessProps, OAuthErrorInfo, User } from '@/auth/types';\nimport { ConnectionInfo, UpdateProfileProps, SignupProps } from '@/methods/types';\nimport type { RateLimitType } from '@/rate-limit/types';\n\n/**\n * A single rate-limit rule for an authentication bucket. The `bucket` is\n * implied by which auth action you're configuring (e.g. `signup`), so callers\n * only specify the actor type, window size, and limit.\n *\n * @example\n * ```typescript\n * import { time } from 'modelence';\n *\n * const rule: AuthRateLimitOverride = {\n * type: 'ip',\n * window: time.minutes(15),\n * limit: 10,\n * };\n * ```\n */\nexport type AuthRateLimitOverride = {\n /** Identifier type of the actor this rule applies to. */\n type: RateLimitType;\n /** Time window size in milliseconds. Use `time.minutes(15)` etc. */\n window: number;\n /** Maximum allowed hits within the window. */\n limit: number;\n};\n\n/**\n * Per-action rate limit overrides for authentication endpoints.\n *\n * Each bucket accepts an array of rules that are merged into the built-in\n * defaults by `(type, window)` tuple:\n * - A rule whose `(type, window)` matches a default replaces that default's\n * `limit`.\n * - A rule whose `(type, window)` does not match any default is added as\n * an extra rule for the bucket.\n * - Defaults whose `(type, window)` is not overridden are kept.\n *\n * This means you can tighten a single window without accidentally dropping\n * the other built-in protections for that bucket.\n *\n * @example Tighten the 15-minute signup cap; per-day default is preserved.\n * ```typescript\n * import { startApp } from 'modelence/server';\n * import { time } from 'modelence';\n *\n * startApp({\n * auth: {\n * rateLimits: {\n * signup: [\n * { type: 'ip', window: time.minutes(15), limit: 5 },\n * ],\n * },\n * },\n * });\n * ```\n *\n * @example Add an extra window alongside the defaults.\n * ```typescript\n * startApp({\n * auth: {\n * rateLimits: {\n * signup: [\n * { type: 'ip', window: time.minutes(1), limit: 2 },\n * ],\n * },\n * },\n * });\n * ```\n */\nexport type AuthRateLimitsConfig = {\n /** Per-IP limits for the signup endpoint (successful signups only). */\n signup?: AuthRateLimitOverride[];\n /** Per-IP limits for signup attempts (checked before duplicate detection). */\n signupAttempt?: AuthRateLimitOverride[];\n /** Per-IP limits for login attempts. */\n signin?: AuthRateLimitOverride[];\n /** Per-user limits for email verification requests. */\n verification?: AuthRateLimitOverride[];\n /** Rate limits for password reset requests. */\n passwordReset?: AuthRateLimitOverride[];\n /** Rate limits for magic link requests. */\n magicLink?: AuthRateLimitOverride[];\n /** Rate limits for one-time code sign-in attempts. */\n oneTimeCode?: AuthRateLimitOverride[];\n /** Rate limits for redeeming a mobile OAuth exchange code. */\n oauthExchange?: AuthRateLimitOverride[];\n /** Per-user rate limits for profile updates. */\n updateProfile?: AuthRateLimitOverride[];\n};\n\ntype GenerateHandleProps = {\n email: string;\n firstName?: string;\n lastName?: string;\n};\n\n/**\n * The password-setting flow that invoked {@link AuthConfig.validatePassword}.\n *\n * Every path that writes a password hash passes one of these, so a policy can\n * be uniform by default while still special-casing a flow if it needs to.\n */\nexport type PasswordContext = 'signup' | 'reset';\n\n/**\n * Props passed to {@link AuthConfig.validatePassword}.\n */\nexport type ValidatePasswordProps = {\n /** The candidate plaintext password, after the built-in length checks. */\n password: string;\n /**\n * Lowercased email address of the account the password is being set on.\n * Useful for rejecting passwords that contain the local-part.\n */\n email: string;\n /** Which flow is setting the password. */\n context: PasswordContext;\n};\n\n/**\n * Props passed to {@link AuthConfig.onBeforeSignup}.\n *\n * The hook fires after validation and the built-in disposable-email check,\n * but before the user document is inserted. Throwing aborts the signup —\n * the thrown error is re-thrown to the caller and `onSignupError` fires.\n */\nexport type BeforeSignupProps = {\n /** Lowercased, validated email address. */\n email: string;\n firstName?: string;\n lastName?: string;\n handle?: string;\n /** Provider that initiated the signup: `'email'` or `'magicLink'`. */\n provider: 'email' | 'magicLink';\n connectionInfo?: ConnectionInfo;\n};\n\n/**\n * Callback options for authentication operations\n */\nexport type AuthOption = {\n /** Callback executed when authentication succeeds */\n onSuccess?: (user: User) => void;\n /** Callback executed when authentication fails */\n onError?: (error: Error) => void;\n};\n\n/**\n * Authentication configuration for the application\n *\n * @example\n * ```typescript\n * import { startApp } from 'modelence/server';\n * import { time } from 'modelence';\n *\n * startApp({\n * auth: {\n * validateSignup: ({ email, firstName, lastName, password, handle, avatarUrl }) => {\n * // Validating the signup data\n * if (!email || !password) {\n * throw new Error('Email and password are required');\n * }\n * },\n * onAfterLogin: ({ user }) => {\n * console.log('User logged in:', user.name);\n * // Redirect to dashboard\n * },\n * onLoginError: ({ error }) => {\n * console.error('Login failed:', error.message);\n * // Show error toast\n * },\n * onAfterSignup: ({ user }) => {\n * console.log('User signed up:', user.email);\n * // Send welcome email\n * },\n * onSignupError: ({ error }) => {\n * console.error('Signup failed:', error.message);\n * },\n * generateHandle: ({ email }) => {\n * console.log('Generating handle for:', email);\n * // Generate handle\n * return 'user123';\n * },\n * }\n * });\n * ```\n */\nexport type AuthConfig = {\n /**\n * Pre-signup validation hook. Runs before a new user is created during\n * email/password signup, after format checks but before duplicate detection.\n * Throw to reject the signup — the thrown message is surfaced to the client.\n *\n * Receives the raw signup payload (`email`, `password`, and optional\n * `firstName`, `lastName`, `avatarUrl`, `handle`). May be async.\n */\n validateSignup?: (props: SignupProps) => void | Promise<void>;\n\n /**\n * Password policy hook. Runs on **every** path that sets a password — signup\n * and password reset today, plus any future change-password flow — after the\n * built-in length checks (`MIN_PASSWORD_LENGTH`..`MAX_PASSWORD_LENGTH`) and\n * before the password is hashed. Throw to reject the password; the thrown\n * message is surfaced to the client.\n *\n * Prefer this over {@link AuthConfig.validateSignup} for password rules.\n * `validateSignup` only runs at signup, so a policy enforced there alone can\n * be bypassed by signing up with a compliant password and immediately\n * resetting to a weaker one.\n *\n * Receives `{ password, email, context }`, where `context` is the flow that\n * triggered the check (`'signup'` or `'reset'`). May be async.\n *\n * @example Enforce a 12-character minimum everywhere.\n * ```typescript\n * startApp({\n * auth: {\n * validatePassword: ({ password }) => {\n * if (password.length < 12) {\n * throw new Error('Password must be at least 12 characters');\n * }\n * },\n * },\n * });\n * ```\n */\n validatePassword?: (props: ValidatePasswordProps) => void | Promise<void>;\n\n /**\n * Pre-update validation hook. Runs before a user's profile fields\n * (`firstName`, `lastName`, `avatarUrl`, `handle`) are written.\n * Throw to reject the update — the thrown message is surfaced to the client.\n * May be async.\n */\n validateProfileUpdate?: (props: UpdateProfileProps) => void | Promise<void>;\n\n /**\n * Fires after a successful login (email/password or OAuth) once the session\n * has been linked to the user. Receives `{ provider, user, session, connectionInfo }`.\n * Use for analytics, audit logging, or post-login side effects.\n */\n onAfterLogin?: (props: AuthSuccessProps) => void;\n\n /**\n * Fires when a login attempt fails. Receives `{ provider, error, session, connectionInfo }`.\n * Use for failure analytics or alerting — does NOT change the response sent to the client.\n */\n onLoginError?: (props: AuthErrorProps) => void;\n\n /**\n * Hook fired after validation and the built-in disposable-email check, but\n * before the new user document is inserted. Throwing aborts the signup —\n * the thrown error is re-thrown to the caller and `onSignupError` fires.\n *\n * Use this to plug in a custom domain-policy check (e.g. a tenant-specific\n * email-domain verification service) without having to disable the built-in\n * disposable-email check.\n *\n * Invoked for `'email'` and `'magicLink'` provider signups. OAuth signups\n * are not gated because OAuth providers (Google, GitHub, etc.) do not issue\n * disposable accounts.\n */\n onBeforeSignup?: (props: BeforeSignupProps) => void | Promise<void>;\n\n /**\n * Fires after a successful signup once the user record is created and the\n * session is linked. Receives `{ provider, user, session, connectionInfo }`.\n * Common uses: send welcome email, create default workspace, track activation.\n */\n onAfterSignup?: (props: AuthSuccessProps) => void;\n\n /**\n * Fires when a signup attempt fails (validation, duplicate email, etc.).\n * Receives `{ provider, error, session, connectionInfo }`.\n * Use for failure analytics — does NOT change the response sent to the client.\n */\n onSignupError?: (props: AuthErrorProps) => void;\n\n /**\n * Fires after a user's email is successfully verified (via the verification\n * link or implicitly via password reset). Receives `{ provider, user, session, connectionInfo }`.\n */\n onAfterEmailVerification?: (props: AuthSuccessProps) => void;\n\n /**\n * Fires when email verification fails (invalid or expired token).\n * Receives `{ provider, error, session, connectionInfo }`.\n */\n onEmailVerificationError?: (props: AuthErrorProps) => void;\n\n /**\n * Fires after an OAuth provider is linked to an existing account\n * (either automatically when `oauthAccountLinking: 'auto'` or via an\n * explicit link flow). Receives `{ provider, user, session, connectionInfo }`.\n */\n onAfterOAuthLink?: (props: AuthSuccessProps) => void;\n\n /**\n * Fires when OAuth account linking fails. Receives\n * `{ provider, error, session, connectionInfo }`.\n */\n onOAuthLinkError?: (props: AuthErrorProps) => void;\n\n /**\n * Custom handle generator. If provided, overrides the default behavior\n * (which derives the handle from the email local-part). Receives\n * `{ email, firstName?, lastName? }` and returns the desired handle\n * synchronously or as a `Promise<string>`. The returned handle must contain\n * only letters, numbers, underscores, and hyphens (matching `HANDLE_REGEX`).\n * If the returned handle collides with an existing one, Modelence appends\n * a numeric suffix automatically.\n */\n generateHandle?: (props: GenerateHandleProps) => Promise<string> | string;\n\n /** @deprecated Use {@link AuthConfig.onAfterLogin} and {@link AuthConfig.onLoginError} instead. */\n login?: AuthOption;\n /** @deprecated Use {@link AuthConfig.onAfterSignup} and {@link AuthConfig.onSignupError} instead. */\n signup?: AuthOption;\n\n /**\n * Enables passwordless magic link authentication. Disabled by default.\n *\n * Requires an email provider and delivery settings under the `email` option\n * (see `EmailConfig.magicLink`).\n *\n * @example\n * ```typescript\n * startApp({\n * auth: {\n * magicLink: { enabled: true, allowSignup: true },\n * },\n * });\n * ```\n */\n magicLink?: {\n enabled?: boolean;\n /**\n * Allows a magic link (or its one-time code) to create an account when\n * the email has no existing one — combined sign-in/sign-up, like OAuth.\n * Disabled by default: unknown emails get the same generic \"link sent\"\n * response but no email, and no account is ever auto-created.\n */\n allowSignup?: boolean;\n };\n\n /**\n * Controls how OAuth providers handle existing accounts with matching email.\n * - 'manual' (default): Returns an error when an OAuth login matches an existing email.\n * - 'auto': Automatically links the OAuth provider to the existing account\n * if the provider email is verified.\n */\n oauthAccountLinking?: 'auto' | 'manual';\n\n /**\n * Customizes how OAuth authentication errors are rendered. By default,\n * OAuth errors are returned as JSON; providing this returns a custom HTML\n * response instead, which is useful when the OAuth flow runs in a browser\n * context. Receives `{ error, statusCode }` and returns an HTML string\n * (or `null`/`undefined` to fall back to the default JSON response).\n *\n * Always escape interpolated values to prevent XSS.\n *\n * @deprecated Use {@link AuthConfig.oauthErrorRedirectUrl} instead. It sends\n * the browser back into the app, where the error can be shown on a real page\n * with the app's own UI; this only ever renders a standalone document at the\n * callback URL. Ignored when `oauthErrorRedirectUrl` is set.\n */\n errorComponent?: (props: OAuthErrorInfo) => string | null | undefined;\n\n /**\n * Where a failed web OAuth flow sends the browser. A path (`'/login'`),\n * resolved against `_system.site.url`, or an absolute URL. The failure is\n * appended as `?error=<message>&errorCode=<code>`,\n * the same contract a mobile flow delivers on its deep link, so the app can\n * show the message on a real page instead of the raw callback response.\n *\n * OAuth errors surface at the provider callback URL, outside any client\n * bundle, so nothing there can render app UI. Without this (or\n * {@link AuthConfig.errorComponent}) the user lands on a JSON body with no\n * way back. When both are set, the redirect wins and `errorComponent` is not\n * called.\n *\n * `error` is for display and its wording may change; branch on `errorCode`.\n *\n * Applies to the provider callback only. A request rejected at the sign-in\n * *initiation* endpoint (auth not configured, a disallowed mobile\n * `redirectUri`, a missing `codeChallenge`) still answers the calling client\n * with JSON, since nothing has left for the provider yet.\n *\n * @example\n * ```typescript\n * startApp({\n * auth: {\n * oauthErrorRedirectUrl: '/login',\n * },\n * });\n * ```\n */\n oauthErrorRedirectUrl?: string;\n\n /**\n * Overrides the built-in rate limits for authentication endpoints. Each rule\n * you provide is merged into the defaults by `(bucket, type, window)`:\n * matching tuples replace the default `limit`, new tuples are added, and\n * unspecified defaults are preserved. See {@link AuthRateLimitsConfig} for\n * full semantics and examples.\n */\n rateLimits?: AuthRateLimitsConfig;\n\n /**\n * When `true`, the built-in disposable-email check is skipped during signup.\n * Defaults to `false` (built-in check enforced).\n *\n * Set this to `true` when you want to enforce your own domain-policy logic\n * via {@link onBeforeSignup} — for example, a service that classifies\n * domains as public/disposable/custom with its own data sources and cache.\n *\n * Skipping the built-in check without registering an `onBeforeSignup` hook\n * means disposable emails will be allowed to sign up.\n */\n allowDisposableEmails?: boolean;\n\n /**\n * Settings for authenticating from a native (React Native / Expo) client.\n *\n * @example\n * ```typescript\n * startApp({\n * auth: {\n * mobile: { redirectUrls: ['myapp://auth'] },\n * },\n * });\n * ```\n */\n mobile?: {\n /**\n * Deep links the OAuth callback is allowed to redirect a native app back to,\n * e.g. `['myapp://auth']`. A sign-in request naming any other target is\n * rejected before the user ever reaches the provider's consent screen.\n *\n * This is an allowlist because the redirect target decides where an auth\n * flow ends up: without it, a crafted link could point the callback at an\n * attacker-controlled destination. There is no implicit default — mobile\n * OAuth stays disabled until at least one entry is configured here or via\n * the `auth.mobile.redirectUrls` config value (the two are merged).\n *\n * Entries are matched on scheme, host and path; a request may add query\n * parameters but may not change any of those three.\n */\n redirectUrls?: string[];\n };\n};\n\nlet authConfig: AuthConfig = Object.freeze({});\n\nexport function setAuthConfig(newAuthConfig: AuthConfig) {\n authConfig = Object.freeze(Object.assign({}, authConfig, newAuthConfig));\n}\n\nexport function getAuthConfig() {\n return authConfig;\n}\n","import { randomBytes } from 'crypto';\n\nimport bcrypt from 'bcrypt';\nimport { z } from 'zod';\n\nimport { Args, Context } from '../methods/types';\nimport { usersCollection } from './db';\nimport { serializeUserForClient } from './utils';\nimport {\n clearAuthTokenCookie,\n clearSessionUser,\n setAuthTokenCookie,\n setSessionUser,\n} from './session';\nimport { getEmailConfig } from '@/app/emailConfig';\nimport { consumeRateLimit } from '@/server';\nimport { validateEmail } from './validators';\nimport { getAuthConfig } from '@/app/authConfig';\nimport { getConfig } from '@/config/server';\nimport { AuthError, ValidationError } from '../error';\n\n/**\n * Whether unverified emails should be blocked from logging in.\n *\n * Verification can only be enforced when an email provider is configured (there\n * is otherwise no way to deliver a verification email). When a provider exists,\n * the `auth.email.verification` config flag acts as the actual switch — it\n * defaults to `true`, preserving the historical behavior where configuring a\n * provider implicitly turned verification on. Operators can now set the flag to\n * `false` to keep a provider while allowing unverified users to sign in.\n */\nfunction isEmailVerificationRequired(): boolean {\n if (!getEmailConfig()?.provider) {\n return false;\n }\n\n return Boolean(getConfig('_system.user.auth.email.verification'));\n}\n\nconst SALT_ROUNDS = 10;\nconst DUMMY_PASSWORD_HASH = bcrypt.hashSync(randomBytes(32).toString('hex'), SALT_ROUNDS);\n\nexport async function handleLoginWithPassword(\n args: Args,\n { user, session, connectionInfo, res }: Context\n) {\n try {\n if (!session) {\n throw new Error('Session is not initialized');\n }\n\n if (user) {\n throw new ValidationError('User is already authenticated', 'ALREADY_AUTHENTICATED');\n }\n\n const ip = connectionInfo?.ip;\n if (ip) {\n await consumeRateLimit({\n bucket: 'signin',\n type: 'ip',\n value: ip,\n });\n }\n\n const email = validateEmail(args.email as string);\n // password is accepted just as a string, so users can still sign in if the password validation rules are changed\n const password = z.string().parse(args.password);\n\n // TODO: add rate limiting by email (and perhaps IP address overall)\n\n const userDoc = await usersCollection.findOne(\n { 'emails.address': email, status: { $nin: ['deleted', 'disabled'] } },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n const passwordHash = userDoc?.authMethods?.password?.hash;\n\n // Always run bcrypt.compare to mitigate timing attacks that could reveal if an email exists.\n const hashToCompare = passwordHash || DUMMY_PASSWORD_HASH;\n const isValidPassword = await bcrypt.compare(password, hashToCompare);\n\n if (!passwordHash || !isValidPassword) {\n throw incorrectCredentialsError();\n }\n\n // Only check email verification if the user exists and the password is correct,\n // to prevent email enumeration by observing verification errors before password checks.\n const emailDoc = userDoc.emails?.find((e) => e.address.toLowerCase() === email);\n\n if (!emailDoc?.verified && isEmailVerificationRequired()) {\n throw new AuthError(\n \"Your email address hasn't been verified yet. Please check your inbox for the verification email.\",\n 'EMAIL_NOT_VERIFIED'\n );\n }\n\n await setSessionUser(session.authToken, userDoc._id);\n\n if (res) {\n setAuthTokenCookie(res, session.authToken);\n }\n\n getAuthConfig().onAfterLogin?.({\n provider: 'email',\n user: userDoc,\n session,\n connectionInfo,\n });\n getAuthConfig().login?.onSuccess?.(userDoc);\n\n return {\n user: serializeUserForClient(userDoc),\n session: { authToken: session.authToken },\n };\n } catch (error) {\n if (error instanceof Error) {\n getAuthConfig().onLoginError?.({\n provider: 'email',\n error,\n session,\n connectionInfo,\n });\n getAuthConfig().login?.onError?.(error);\n }\n throw error;\n }\n}\n\nexport async function handleLogout(args: Args, { session, res }: Context) {\n if (!session) {\n throw new Error('Session is not initialized');\n }\n\n await clearSessionUser(session.authToken);\n\n if (res) {\n clearAuthTokenCookie(res);\n }\n}\n\n/*\n It is important to return the same exact error both in case the email\n or password is incorrect so that the client cannot tell the difference,\n otherwise it would allow for an enumeration attack.\n*/\nfunction incorrectCredentialsError() {\n return new Error('Incorrect email/password combination');\n}\n","import { Store } from '../data/store';\nimport { schema } from '../data/types';\n\n/**\n * Two-bucket sliding window approximation to track rate limits.\n * This is a trade-off between storage and accuracy.\n * We're not keeping exact event logs, so we can't track the exact number of events in a window.\n * Instead, we're adding a weighted average from the previous window to the current window.\n */\nexport const dbRateLimits = new Store('_modelenceRateLimits', {\n schema: {\n bucket: schema.string(),\n type: schema.enum(['ip', 'user', 'email']),\n value: schema.string(),\n windowMs: schema.number(),\n\n windowStart: schema.date(),\n windowCount: schema.number(),\n prevWindowCount: schema.number(),\n\n expiresAt: schema.date(),\n },\n indexes: [\n { key: { bucket: 1, type: 1, value: 1, windowMs: 1 }, unique: true },\n { key: { expiresAt: 1 }, expireAfterSeconds: 0 },\n ],\n});\n","import { RateLimitRule, RateLimitType } from './types';\nimport { dbRateLimits } from './db';\nimport { RateLimitError } from '../error';\n\nlet allRules: Array<RateLimitRule> = [];\n\nexport function initRateLimits(rateLimits: RateLimitRule[]) {\n if (allRules.length > 0) {\n throw new Error('Duplicate call to initRateLimits - already initialized');\n }\n\n allRules = rateLimits;\n}\n\n/**\n * This function will check all rate limit rules on the specified bucket and type,\n * throw an error if any of them are exceeded and increase the count of the rate limit record.\n *\n * @category Rate Limits\n *\n * @example\n * ```ts\n * await consumeRateLimit({ bucket: 'api', type: 'ip', value: '127.0.0.1' });\n * ```\n * @param options.bucket - The bucket for the rate limit.\n * @param options.type - The type of the rate limit.\n * @param options.value - The value for the rate limit.\n * @param options.message - Optional custom error message when the rate limit is exceeded.\n */\nexport async function consumeRateLimit(options: {\n bucket: string;\n type: RateLimitType;\n value: string;\n message?: string;\n}) {\n const { bucket, type, value, message } = options;\n const rules = allRules.filter((rule) => rule.bucket === bucket && rule.type === type);\n const createError = message ? () => new RateLimitError(message) : undefined;\n\n for (const rule of rules) {\n await checkRateLimitRule(rule, value, createError);\n }\n}\n\n// Two-bucket sliding window approximation to track rate limits.\nasync function checkRateLimitRule(rule: RateLimitRule, value: string, createError?: () => Error) {\n const createRateLimitError = () => {\n return createError\n ? createError()\n : new RateLimitError(`Rate limit exceeded for ${rule.bucket}`);\n };\n\n const filter = {\n bucket: rule.bucket,\n type: rule.type,\n value,\n windowMs: rule.window,\n };\n\n const record = await dbRateLimits.findOne(filter);\n\n const now = Date.now();\n const currentWindowStart = Math.floor(now / rule.window) * rule.window;\n\n const { count, modifier } = record\n ? getCount(record, currentWindowStart, now)\n : {\n count: 0,\n modifier: {\n $setOnInsert: {\n windowStart: new Date(currentWindowStart),\n windowCount: 1,\n prevWindowCount: 0,\n expiresAt: new Date(currentWindowStart + rule.window + rule.window),\n },\n },\n };\n\n if (count >= rule.limit) {\n throw createRateLimitError();\n }\n\n /*\n Always use upsert, because there is a small chance the document might be auto-removed\n based on the expiration TTL index in between the check and the update\n */\n await dbRateLimits.upsertOne(filter, modifier);\n}\n\nfunction getCount(record: (typeof dbRateLimits)['Doc'], currentWindowStart: number, now: number) {\n const prevWindowStart = currentWindowStart - record.windowMs;\n\n if (record.windowStart.getTime() === currentWindowStart) {\n const currentWindowCount = record.windowCount;\n const prevWindowCount = record.prevWindowCount;\n const prevWindowWeight = 1 - (now - currentWindowStart) / record.windowMs;\n return {\n count: Math.round(currentWindowCount + prevWindowCount * prevWindowWeight),\n modifier: {\n $inc: { windowCount: 1 },\n $setOnInsert: {\n windowStart: new Date(currentWindowStart),\n prevWindowCount: 0,\n expiresAt: new Date(currentWindowStart + record.windowMs + record.windowMs),\n },\n },\n };\n }\n\n if (record.windowStart.getTime() === prevWindowStart) {\n const weight = 1 - (now - currentWindowStart) / record.windowMs;\n return {\n count: Math.round(record.windowCount * weight),\n modifier: {\n $set: {\n windowStart: new Date(currentWindowStart),\n windowCount: 1,\n prevWindowCount: record.windowCount,\n expiresAt: new Date(currentWindowStart + record.windowMs + record.windowMs),\n },\n },\n };\n }\n\n return {\n count: 0,\n modifier: {\n $set: {\n windowStart: new Date(currentWindowStart),\n windowCount: 1,\n prevWindowCount: 0,\n expiresAt: new Date(currentWindowStart + record.windowMs + record.windowMs),\n },\n },\n };\n}\n","import { usersCollection } from './db';\nimport { Args, Context, UpdateProfileProps } from '../methods/types';\nimport { serializeUserForClient } from './utils';\nimport { validateProfileFields } from './validators';\nimport { getAuthConfig } from '@/app/authConfig';\nimport { consumeRateLimit } from '../rate-limit/rules';\nimport { AuthError, ValidationError } from '../error';\n\nexport async function getOwnProfile(props: Args, { user }: Context) {\n if (!user) {\n throw new AuthError('Not authenticated');\n }\n\n const profile = await usersCollection.requireById(user.id);\n\n return {\n handle: profile.handle,\n emails: profile.emails,\n authMethods: Object.keys(profile.authMethods || {}),\n firstName: profile.firstName ?? undefined,\n lastName: profile.lastName ?? undefined,\n avatarUrl: profile.avatarUrl ?? undefined,\n };\n}\n\nexport async function handleUpdateProfile(props: Args, { user }: Context) {\n if (!user) {\n throw new AuthError('Not authenticated');\n }\n\n let profile = await usersCollection.requireById(user.id);\n\n const rawProps = props as UpdateProfileProps;\n const isHandleUnchanged =\n typeof rawProps.handle === 'string' && rawProps.handle.trim() === profile.handle;\n\n const { handle: _ignoredHandle, ...propsWithoutHandle } = rawProps;\n const update = validateProfileFields(\n isHandleUnchanged ? (propsWithoutHandle as UpdateProfileProps) : rawProps\n );\n\n await getAuthConfig().validateProfileUpdate?.(update);\n\n //Check if handle is already taken\n if ('handle' in update && update.handle !== undefined) {\n const existing = await usersCollection.findOne(\n {\n handle: update.handle,\n _id: { $ne: profile._id }, // excludes the current user\n },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (existing) {\n throw new ValidationError('Handle already taken.');\n }\n }\n\n //Update profile\n if (Object.keys(update).length > 0) {\n await consumeRateLimit({ bucket: 'updateProfile', type: 'user', value: user.id });\n\n const setFields: Record<string, unknown> = {}; //Used to set the fields\n const unsetFields: Record<string, ''> = {}; //Used to clear the fields the mongodb, because mongodb rejects undefined values when updating fields\n\n for (const [key, value] of Object.entries(update)) {\n if (value === undefined) {\n unsetFields[key] = '';\n } else {\n setFields[key] = value;\n }\n }\n\n const mongoUpdate: Record<string, unknown> = {};\n if (Object.keys(setFields).length > 0) mongoUpdate.$set = setFields;\n if (Object.keys(unsetFields).length > 0) mongoUpdate.$unset = unsetFields;\n\n try {\n await usersCollection.updateOne({ _id: profile._id }, mongoUpdate);\n\n //Also pass Cleared Fields as UNDEFINED in profile\n const clearedFields = Object.fromEntries(\n Object.keys(unsetFields).map((key) => [key, undefined])\n );\n profile = { ...profile, ...setFields, ...clearedFields } as typeof profile;\n } catch (error) {\n if (error instanceof Error && 'code' in error && (error as { code: number }).code === 11000) {\n throw new ValidationError('Handle already taken.');\n }\n throw error;\n }\n }\n\n return {\n user: serializeUserForClient(profile),\n };\n}\n","import { Document, ObjectId } from 'mongodb';\nimport { ConnectionInfo } from '@/methods/types';\n\nexport interface UserEmail {\n address: string;\n verified: boolean;\n}\n\nexport interface User extends Document {\n _id: ObjectId;\n\n handle: string;\n\n emails?: UserEmail[];\n\n status?: 'active' | 'disabled' | 'deleted';\n\n firstName?: string;\n lastName?: string;\n avatarUrl?: string;\n\n createdAt: Date;\n disabledAt?: Date;\n deletedAt?: Date;\n\n roles?: string[];\n\n authMethods: {\n password?: {\n hash: string;\n };\n google?: {\n id: string;\n };\n github?: {\n id: string;\n };\n };\n}\n\nexport type UserInfo = {\n /** The user's unique identifier. */\n id: string;\n /** The user's display handle. */\n handle: string;\n /** The role strings assigned to this user in the database. */\n roles: string[];\n /** Returns `true` if the user has the given role. */\n hasRole: (role: string) => boolean;\n /** Throws an error if the user does not have the given role. */\n requireRole: (role: string) => void;\n firstName?: string;\n lastName?: string;\n avatarUrl?: string;\n};\n\nexport type Role = string;\n\nexport type DefaultRoles = Record<'authenticated' | 'unauthenticated', Role | null>;\n\nexport type Session = {\n authToken: string;\n expiresAt: Date;\n userId: ObjectId | null;\n};\n\nexport type Permission = string;\n\n/**\n * Defines a role that can be assigned to users.\n *\n * Roles are registered via the `roles` option in {@link AppOptions} and\n * are synced to the Modelence Cloud dashboard for user management.\n *\n * @example\n * ```typescript\n * import { startApp } from 'modelence/server';\n *\n * startApp({\n * roles: {\n * admin: { description: 'Full access to all features' },\n * editor: { description: 'Can edit content' },\n * viewer: {},\n * },\n * });\n * ```\n */\nexport type RoleDefinition = {\n /** Human-readable description of the role, shown in the Modelence Cloud dashboard. */\n description?: string;\n /** @internal */\n permissions?: Permission[];\n};\n\nexport const SUPPORTED_OAUTH_PROVIDERS = ['google', 'github'] as const;\nexport type OAuthProvider = (typeof SUPPORTED_OAUTH_PROVIDERS)[number];\n\nexport type AuthProvider = OAuthProvider | 'email' | 'magicLink';\n\nexport type AuthSuccessProps = {\n provider: AuthProvider;\n user: User;\n session: Session | null;\n connectionInfo: ConnectionInfo;\n};\n\nexport type AuthErrorProps = {\n provider: AuthProvider;\n error: Error;\n session: Session | null;\n connectionInfo: ConnectionInfo;\n};\n\nexport type OAuthErrorInfo = {\n error: string;\n statusCode: number;\n};\n","import { usersCollection } from './db';\nimport { Args, Context } from '@/methods/types';\nimport { OAuthProvider, SUPPORTED_OAUTH_PROVIDERS } from './types';\n\nexport async function handleUnlinkOAuthProvider({ provider }: Args, { user }: Context) {\n if (!user) {\n throw new Error('You must be signed in to unlink a provider.');\n }\n\n if (\n typeof provider !== 'string' ||\n !SUPPORTED_OAUTH_PROVIDERS.includes(provider as OAuthProvider)\n ) {\n throw new Error(\n `Invalid provider. Supported providers are: ${SUPPORTED_OAUTH_PROVIDERS.join(', ')}.`\n );\n }\n\n // requireById throws if user is not found, so no null check needed\n const userDoc = await usersCollection.requireById(user.id);\n\n const methods = userDoc.authMethods ?? {};\n const providerKey = provider as keyof typeof methods;\n\n if (!methods[providerKey]) {\n throw new Error(`${provider} is not linked to your account.`);\n }\n\n const activeMethodsCount = Object.values(methods).filter(Boolean).length;\n if (activeMethodsCount <= 1) {\n throw new Error(\n 'Cannot unlink your only authentication method. Please add another method first.'\n );\n }\n\n // Determine the OTHER auth method fields that must still exist for lockout prevention.\n // This atomic filter ensures that between our read and write, at least one other method\n // hasn't been concurrently removed (prevents two simultaneous unlinks from both succeeding).\n const otherMethods = Object.keys(methods).filter(\n (key) => key !== provider && methods[key as keyof typeof methods]\n );\n const otherMethodGuard =\n otherMethods.length > 0\n ? { $or: otherMethods.map((key) => ({ [`authMethods.${key}`]: { $exists: true } })) }\n : {};\n\n const result = await usersCollection.updateOne(\n { _id: userDoc._id, ...otherMethodGuard },\n { $unset: { [`authMethods.${provider}`]: '' } }\n );\n\n if (result.matchedCount === 0) {\n throw new Error(\n 'Cannot unlink your only authentication method. Please add another method first.'\n );\n }\n}\n","export function emailVerificationTemplate({\n name,\n email,\n verificationUrl,\n}: {\n name?: string;\n email: string;\n verificationUrl: string;\n}) {\n return `\n <p>Hi${name ? ` ${name}` : ''},</p>\n <p>Please verify your email address ${email} by clicking the link below:</p>\n <p><a href=\"${verificationUrl}\">${verificationUrl}</a></p>\n <p>If you did not request this, please ignore this email.</p>\n `;\n}\n","import { z } from 'zod';\n\nimport { usersCollection, emailVerificationTokensCollection } from './db';\nimport { ObjectId, RouteParams, RouteResponse } from '@/server';\nimport { getEmailConfig } from '@/app/emailConfig';\nimport { randomBytes } from 'crypto';\nimport { time } from '@/time';\nimport { htmlToText } from '@/utils';\nimport { emailVerificationTemplate } from './templates/emailVerficationTemplate';\nimport { getAuthConfig } from '@/app/authConfig';\nimport { User } from './types';\nimport { Args, Context } from '@/methods/types';\nimport { validateEmail } from './validators';\nimport { consumeRateLimit } from '@/rate-limit/rules';\nimport { getConfig } from '@/config/server';\nimport { createSession, setAuthTokenCookie } from './session';\n\nconst USER_COLLATION = { locale: 'en', strength: 2 } as const;\n\nasync function verifyEmailToken(token: string) {\n const tokenDoc = await emailVerificationTokensCollection.findOne({\n token,\n expiresAt: { $gt: new Date() },\n });\n\n if (!tokenDoc) {\n throw new Error('Invalid or expired verification token');\n }\n\n const userDoc = await usersCollection.findOne({\n _id: tokenDoc.userId,\n status: { $nin: ['deleted', 'disabled'] },\n });\n\n if (!userDoc) {\n throw new Error('User not found');\n }\n\n const email = tokenDoc.email;\n\n if (!email) {\n throw new Error('Email not found in token');\n }\n\n // Mark the specific email as verified atomically, returning the updated doc.\n // `$elemMatch` keeps both conditions bound to a single array element: with two\n // independent dotted predicates, `'emails.verified': { $ne: true }` means \"no\n // element is verified\", so any account already holding one verified address\n // could never verify another, and the positional `$` had no guarantee of\n // binding to the element carrying `address`.\n // Same strength-2 collation as the lookups: the token always carries a\n // lowercased address (validateEmail) while the stored address may keep its\n // original casing (e.g. OAuth-created accounts), or the match silently fails.\n const updatedUserDoc = await usersCollection.findOneAndUpdate(\n {\n _id: tokenDoc.userId,\n status: { $nin: ['deleted', 'disabled'] },\n emails: { $elemMatch: { address: email, verified: { $ne: true } } },\n },\n { $set: { 'emails.$.verified': true } },\n { collation: USER_COLLATION, returnDocument: 'after' }\n );\n\n // Consume the token regardless of the update outcome: it has now been\n // presented, and leaving it spendable until its 24h expiry lets a token that\n // always fails be replayed.\n await emailVerificationTokensCollection.deleteOne({ _id: tokenDoc._id });\n\n if (!updatedUserDoc) {\n // Distinguish \"already verified\" from \"not this user's address\" by checking\n // the specific element, mirroring handleResendEmailVerification. The prior\n // check only matched on address, so an address that failed to verify for any\n // other reason was still reported as already verified.\n const existingEmailDoc = userDoc.emails?.find((e) => e.address.toLowerCase() === email);\n\n if (!existingEmailDoc) {\n throw new Error('Email address not found for this user');\n }\n\n if (existingEmailDoc.verified) {\n throw new Error('Email is already verified');\n }\n\n throw new Error('Unable to verify email address');\n }\n\n return { userDoc: updatedUserDoc, email };\n}\n\nexport async function handleVerifyEmail(params: RouteParams): Promise<RouteResponse> {\n const baseUrl = getConfig('_system.site.url') as string | undefined;\n const emailVerifiedRedirectUrl =\n getEmailConfig().verification?.redirectUrl ||\n getEmailConfig().emailVerifiedRedirectUrl ||\n baseUrl ||\n '/';\n try {\n const token = z.string().parse(params.query.token);\n const { userDoc } = await verifyEmailToken(token);\n\n const authConfig = getAuthConfig();\n authConfig.onAfterEmailVerification?.({\n provider: 'email',\n user: userDoc as User,\n session: null,\n connectionInfo: {\n baseUrl,\n ip: params.req.ip || params.req.socket.remoteAddress,\n userAgent: params.headers['user-agent'],\n acceptLanguage: params.headers['accept-language'],\n referrer: params.headers['referer'],\n },\n });\n\n const { authToken } = await createSession(userDoc._id);\n\n setAuthTokenCookie(params.res, authToken);\n\n return {\n status: 301,\n // Suppress the Referer so the token-bearing verification URL never leaks.\n headers: { 'Referrer-Policy': 'no-referrer' },\n redirect: `${emailVerifiedRedirectUrl}?status=verified`,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'An unexpected error occurred';\n if (error instanceof Error) {\n const authConfig = getAuthConfig();\n authConfig.onEmailVerificationError?.({\n provider: 'email',\n error,\n session: null,\n connectionInfo: {\n baseUrl,\n ip: params.req.ip || params.req.socket.remoteAddress,\n userAgent: params.headers['user-agent'],\n acceptLanguage: params.headers['accept-language'],\n referrer: params.headers['referer'],\n },\n });\n console.error('Error verifying email:', error);\n }\n\n return {\n status: 301,\n headers: { 'Referrer-Policy': 'no-referrer' },\n redirect: `${emailVerifiedRedirectUrl}?status=error&message=${encodeURIComponent(message)}`,\n };\n }\n}\n\nexport async function sendVerificationEmail({\n userId,\n email,\n baseUrl: requestBaseUrl,\n}: {\n userId: ObjectId;\n email: string;\n baseUrl?: string;\n}) {\n const baseUrl = (getConfig('_system.site.url') as string | undefined) || requestBaseUrl;\n\n if (getEmailConfig().provider) {\n const emailProvider = getEmailConfig().provider;\n\n // Generate verification token\n const verificationToken = randomBytes(32).toString('hex');\n const expiresAt = new Date(Date.now() + time.hours(24));\n\n // Store token in database\n await emailVerificationTokensCollection.insertOne({\n userId,\n email,\n token: verificationToken,\n createdAt: new Date(),\n expiresAt,\n });\n\n const verificationUrl = `${baseUrl}/api/_internal/auth/verify-email?token=${verificationToken}`;\n\n const template = getEmailConfig()?.verification?.template || emailVerificationTemplate;\n // TODO: we should have also the name on this step\n const htmlTemplate = template({ name: '', email, verificationUrl });\n const textContent = htmlToText(htmlTemplate);\n\n await emailProvider?.sendEmail({\n to: email,\n from: getEmailConfig()?.from || 'noreply@modelence.com',\n subject: getEmailConfig()?.verification?.subject || 'Verify your email address',\n text: textContent,\n html: htmlTemplate,\n });\n }\n}\n\nconst resendVerificationResponse = {\n success: true,\n message: 'If that email is registered and not yet verified, a verification email has been sent',\n};\n\nexport async function handleResendEmailVerification(args: Args, { connectionInfo }: Context) {\n const email = validateEmail(args.email as string);\n\n // Find user by email, excluding deleted/disabled accounts\n const userDoc = await usersCollection.findOne(\n { 'emails.address': email, status: { $nin: ['deleted', 'disabled'] } },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n // Return the same generic response whether the email is unknown,\n // already verified, or successfully sent — to prevent user enumeration.\n if (!userDoc) {\n return resendVerificationResponse;\n }\n\n const emailDoc = userDoc.emails?.find((e) => e.address.toLowerCase() === email);\n\n if (!emailDoc || emailDoc.verified) {\n return resendVerificationResponse;\n }\n\n if (!getEmailConfig().provider) {\n throw new Error('Email provider is not configured');\n }\n\n await consumeRateLimit({\n bucket: 'verification',\n type: 'user',\n value: userDoc._id.toString(),\n message: 'Please wait at least 60 seconds before requesting another verification email',\n });\n\n await sendVerificationEmail({\n userId: userDoc._id,\n email,\n baseUrl: connectionInfo?.baseUrl,\n });\n\n return resendVerificationResponse;\n}\n","import bcrypt from 'bcrypt';\nimport { SignupProps, Context, Args } from '../methods/types';\nimport { usersCollection } from './db';\nimport { isDisposableEmail } from './disposableEmails';\nimport { consumeRateLimit } from '../rate-limit/rules';\nimport { sendVerificationEmail } from './verification';\nimport { validateEmail, validatePassword, validateProfileFields } from './validators';\nimport { getAuthConfig } from '@/app/authConfig';\nimport { resolveUniqueHandle, isDuplicateEmailError } from './utils';\nimport { ValidationError } from '../error';\n\nexport async function handleSignupWithPassword(\n props: Args,\n { user, session, connectionInfo }: Context\n) {\n const authConfig = getAuthConfig();\n try {\n if (user) {\n throw new ValidationError('User is already authenticated', 'ALREADY_AUTHENTICATED');\n }\n\n // Narrow once at the boundary\n const signupProps = props as SignupProps;\n const { firstName, lastName, avatarUrl, handle } = signupProps;\n\n const email = validateEmail(signupProps.email);\n const password = validatePassword(signupProps.password);\n\n const ip = connectionInfo?.ip;\n if (ip) {\n await consumeRateLimit({\n bucket: 'signupAttempt',\n type: 'ip',\n value: ip,\n });\n }\n\n if (!authConfig.allowDisposableEmails && (await isDisposableEmail(email))) {\n throw new Error('Please use a permanent email address');\n }\n\n // TODO: captcha check\n\n const existingUser = await usersCollection.findOne(\n { 'emails.address': email },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (existingUser) {\n throw new Error('Unable to create account');\n }\n\n await authConfig.onBeforeSignup?.({\n email,\n firstName,\n lastName,\n handle,\n provider: 'email',\n connectionInfo,\n });\n\n if (ip) {\n await consumeRateLimit({\n bucket: 'signup',\n type: 'ip',\n value: ip,\n });\n }\n\n // Validate optional profile fields (firstName, lastName, avatarUrl, handle)\n const profileFields = validateProfileFields({\n firstName,\n lastName,\n avatarUrl,\n handle,\n });\n\n await authConfig.validateSignup?.({\n email,\n password,\n ...profileFields,\n });\n\n // Runs on every password-setting path (see `resetPassword`), so a policy\n // configured here can't be bypassed by resetting to a weaker password.\n await authConfig.validatePassword?.({\n password,\n email,\n context: 'signup',\n });\n\n // Resolve unique handle\n let resolvedHandle: string;\n\n if (profileFields.handle) {\n resolvedHandle = await resolveUniqueHandle(profileFields.handle, email);\n } else if (authConfig.generateHandle) {\n const generated = await authConfig.generateHandle({\n email,\n firstName: profileFields.firstName,\n lastName: profileFields.lastName,\n });\n\n resolvedHandle = await resolveUniqueHandle(generated, email, {\n throwOnConflict: false,\n });\n } else {\n resolvedHandle = await resolveUniqueHandle(undefined, email);\n }\n\n // Hash password with bcrypt (salt is automatically generated)\n const hash = await bcrypt.hash(password, 10);\n\n let result;\n try {\n result = await usersCollection.insertOne({\n handle: resolvedHandle,\n status: 'active',\n emails: [\n {\n address: email,\n verified: false,\n },\n ],\n createdAt: new Date(),\n authMethods: {\n password: {\n hash,\n },\n },\n ...(profileFields.firstName !== undefined && { firstName: profileFields.firstName }),\n ...(profileFields.lastName !== undefined && { lastName: profileFields.lastName }),\n ...(profileFields.avatarUrl !== undefined && { avatarUrl: profileFields.avatarUrl }),\n });\n } catch (error) {\n if (isDuplicateEmailError(error)) {\n // A concurrent signup won the race between the findOne check above and\n // this insert; the unique emails.address index rejected the duplicate.\n // Surface the same message the pre-check would have.\n throw new Error('Unable to create account');\n }\n throw error;\n }\n\n const userDocument = await usersCollection.findOne(\n { _id: result.insertedId },\n { readPreference: 'primary' }\n );\n\n if (!userDocument) {\n throw new Error('User not found');\n }\n\n await sendVerificationEmail({\n userId: result?.insertedId,\n email,\n baseUrl: connectionInfo?.baseUrl,\n });\n\n authConfig.onAfterSignup?.({\n provider: 'email',\n user: userDocument,\n session,\n connectionInfo,\n });\n\n authConfig.signup?.onSuccess?.(userDocument);\n\n return result.insertedId;\n } catch (error) {\n if (error instanceof Error) {\n authConfig.onSignupError?.({\n provider: 'email',\n error,\n session,\n connectionInfo,\n });\n\n authConfig.signup?.onError?.(error);\n }\n throw error;\n }\n}\n","import { z } from 'zod';\nimport bcrypt from 'bcrypt';\nimport { randomBytes } from 'crypto';\n\nimport { Args, Context } from '@/methods/types';\nimport { ObjectId, RouteParams, RouteResponse } from '@/server';\nimport { usersCollection, resetPasswordTokensCollection, magicLinkTokensCollection } from './db';\nimport { getEmailConfig } from '@/app/emailConfig';\nimport { getAuthConfig } from '@/app/authConfig';\nimport { time } from '@/time';\nimport { htmlToText } from '@/utils';\nimport { validateEmail, validatePassword } from './validators';\nimport { consumeRateLimit } from '@/server';\nimport { getConfig } from '@/config/server';\nimport { invalidateAllUserSessions } from './session';\nimport { hashToken } from './tokenHash';\nimport { resolveUrl } from './utils';\n\n// Short-lived httpOnly cookie carrying the reset token from the landing route to\n// the `resetPassword` mutation, so it never appears on a client-rendered URL.\nconst RESET_PASSWORD_COOKIE = 'resetPasswordToken';\n\n// Scope the cookie to the API surface so it's only sent on the resetPassword request.\nconst RESET_PASSWORD_COOKIE_PATH = '/api/_internal/';\n\n// Attributes shared by the set and clear calls. Browsers only remove a cookie\n// when the clearing Set-Cookie matches the same path/secure/sameSite/httpOnly,\n// so both sites must use these identical flags or the httpOnly cookie can linger.\nconst RESET_PASSWORD_COOKIE_OPTIONS = {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n // `lax` so the cookie survives the email-link navigation; `strict` can drop\n // it when the SPA is reached cross-site (some webviews).\n sameSite: 'lax',\n path: RESET_PASSWORD_COOKIE_PATH,\n} as const;\n\n/**\n * Looks up a reset token without consuming it. Only matches the hashed form:\n * tokens are stored as `hashToken(rawToken)`, and the caller must present the\n * raw token. There is deliberately no plaintext fallback — matching the raw\n * value against the stored column would let a leaked SHA-256 digest be replayed\n * as a bearer token (submit the digest, it equals the stored value), defeating\n * the point of hashing at rest. Tokens expire within 1 hour (TTL index), so no\n * migration fallback is needed for tokens issued before hashing was deployed.\n */\nasync function findResetTokenDoc(rawToken: string) {\n return resetPasswordTokensCollection.findOne({ token: hashToken(rawToken) });\n}\n\n/**\n * Atomically claims a reset token by `_id` — the single-use enforcement point.\n * Of concurrent requests holding the same token, exactly one gets the doc back;\n * the rest get null. Returns the deleted doc, or null if already claimed.\n */\nasync function claimResetTokenById(id: ObjectId) {\n return resetPasswordTokensCollection.findOneAndDelete({ _id: id });\n}\n\nfunction defaultPasswordResetTemplate({ email, resetUrl }: { email: string; resetUrl: string }) {\n return `\n <p>Hi,</p>\n <p>We received a request to reset your password for ${email}.</p>\n <p>Click the link below to reset your password:</p>\n <p><a href=\"${resetUrl}\">${resetUrl}</a></p>\n <p>This link will expire in 1 hour.</p>\n <p>If you did not request this password reset, please ignore this email.</p>\n `;\n}\n\nconst passwordResetSent = {\n success: true,\n message: 'If an account with that email exists, a password reset link has been sent',\n};\n\nexport async function handleSendResetPasswordToken(args: Args, { connectionInfo }: Context) {\n const email = validateEmail(args.email as string);\n const ip = connectionInfo?.ip;\n\n if (ip) {\n await consumeRateLimit({\n bucket: 'passwordReset',\n type: 'ip',\n value: ip,\n });\n }\n\n await consumeRateLimit({\n bucket: 'passwordReset',\n type: 'email',\n value: email,\n });\n\n // Find user by email\n const userDoc = await usersCollection.findOne(\n { 'emails.address': email, status: { $nin: ['deleted', 'disabled'] } },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (!userDoc) {\n // For security, don't reveal if email exists or not\n return passwordResetSent;\n }\n\n // Intentionally NOT gated on an existing password: this is a set-OR-reset\n // flow. Passwordless accounts (magic link, OAuth-only) are the documented\n // way to *add* a password — `handleResetPassword` sets the hash whether or\n // not one existed. Requiring `authMethods.password` here would silently dead-\n // end those users (generic success, no email) despite the promised flow.\n // Ownership is still proven by receiving the emailed token, and the response\n // stays generic either way, so this doesn't enable account enumeration.\n\n const emailProvider = getEmailConfig().provider;\n if (!emailProvider) {\n throw new Error('Email provider is not configured');\n }\n\n // Generate reset token\n const resetToken = randomBytes(32).toString('hex');\n const now = Date.now();\n const createdAt = new Date(now);\n const expiresAt = new Date(now + time.hours(1)); // 1 hour expiry\n\n // Store only the hash, never the raw token (defense against db leaks).\n await resetPasswordTokensCollection.insertOne({\n userId: userDoc._id,\n email,\n token: hashToken(resetToken),\n createdAt,\n expiresAt,\n });\n\n // Point the email at the server landing route, not the SPA page, so the raw\n // token never reaches a client-rendered URL.\n const baseUrl = (getConfig('_system.site.url') as string | undefined) || connectionInfo?.baseUrl;\n if (!baseUrl) {\n // Without a base URL we'd email a broken `undefined/...` link — fail loudly.\n throw new Error(\n 'Unable to build password reset link: set _system.site.url (MODELENCE_SITE_URL)'\n );\n }\n const resetUrl = `${baseUrl}/api/_internal/auth/reset-password?token=${resetToken}`;\n\n // Send email\n const template = getEmailConfig()?.passwordReset?.template || defaultPasswordResetTemplate;\n const htmlTemplate = template({ email, resetUrl, name: '' });\n const textContent = htmlToText(htmlTemplate);\n\n await emailProvider.sendEmail({\n to: email,\n from: getEmailConfig()?.from || 'noreply@modelence.com',\n subject: getEmailConfig()?.passwordReset?.subject || 'Reset your password',\n text: textContent,\n html: htmlTemplate,\n });\n\n return passwordResetSent;\n}\n\n/**\n * Server landing route for password reset. The email links here instead of the\n * SPA page: we validate the token, stash it in a short-lived httpOnly cookie, and\n * 302-redirect to the tokenless SPA page — so the token never reaches client JS.\n */\nexport async function handleResetPasswordLanding(params: RouteParams): Promise<RouteResponse> {\n const baseUrl =\n (getConfig('_system.site.url') as string | undefined) ||\n `${params.req.protocol}://${params.req.get('host')}`;\n const resetPasswordUrl = resolveUrl(baseUrl, getEmailConfig().passwordReset?.redirectUrl);\n\n try {\n const token = z.string().parse(params.query.token);\n\n const resetTokenDoc = await findResetTokenDoc(token);\n if (!resetTokenDoc || resetTokenDoc.expiresAt < new Date()) {\n throw new Error('This password reset link is invalid or has expired.');\n }\n\n params.res.cookie(RESET_PASSWORD_COOKIE, token, {\n ...RESET_PASSWORD_COOKIE_OPTIONS,\n maxAge: time.hours(1),\n });\n\n return {\n status: 302,\n // Suppress the Referer so the token-bearing landing URL never leaks.\n headers: { 'Referrer-Policy': 'no-referrer' },\n redirect: resetPasswordUrl,\n };\n } catch (error) {\n // Surface a fixed, friendly message; never forward the raw error (ZodError or\n // DB error) into the redirect URL. Log the real cause server-side instead.\n console.error('Error handling password reset landing:', error);\n const message = 'This password reset link is invalid or has expired.';\n return {\n status: 302,\n headers: { 'Referrer-Policy': 'no-referrer' },\n redirect: `${resetPasswordUrl}?status=error&message=${encodeURIComponent(message)}`,\n };\n }\n}\n\nexport async function handleResetPassword(args: Args, context: Context) {\n // Prefer the token from the httpOnly cookie set by the landing route.\n // `req` is null for in-process invocations (no cookie to read).\n const cookieToken = context.req?.cookies?.[RESET_PASSWORD_COOKIE];\n\n // DEPRECATED fallback: token from request args, for older clients that submit it\n // directly. Reintroduces the leak risk the cookie exchange closes — rollout only.\n // TODO(reset-token-arg-fallback): remove the `args.token` path.\n if (!cookieToken && args.token) {\n console.warn(\n '[modelence] resetPassword received a token via request args instead of the httpOnly ' +\n 'cookie. This path is deprecated and will be removed; ensure password reset emails link ' +\n 'to /api/_internal/auth/reset-password so the token is exchanged server-side.'\n );\n }\n\n const token = z.string().parse(cookieToken ?? args.token);\n const password = validatePassword(args.password as string);\n\n const clearCookie = () => {\n // `res` is null for in-process invocations (no response to mutate).\n // Clear with the same flags used to set it; browsers ignore a clear whose\n // attributes don't match, which would leave the httpOnly cookie behind.\n context.res?.clearCookie(RESET_PASSWORD_COOKIE, RESET_PASSWORD_COOKIE_OPTIONS);\n };\n\n // Look up WITHOUT consuming: if validation/hashing fails (or the user is\n // missing), the link stays valid so the user can retry the same link.\n const resetTokenDoc = await findResetTokenDoc(token);\n if (!resetTokenDoc) {\n clearCookie();\n throw new Error('Invalid or expired reset token');\n }\n\n if (resetTokenDoc.expiresAt < new Date()) {\n // Expired: remove it and reject.\n await claimResetTokenById(resetTokenDoc._id as ObjectId);\n clearCookie();\n throw new Error('Reset token has expired');\n }\n\n // Find the user\n const userDoc = await usersCollection.findOne({ _id: resetTokenDoc.userId });\n if (!userDoc) {\n throw new Error('User not found');\n }\n\n // App-level password policy. Runs before the token is claimed below, so a\n // rejected password leaves the link valid and the user can retry with a\n // compliant one — same rationale as the deferred token lookup above.\n //\n // Legacy tokens predate the `email` field (see the guard on the verify write\n // below), so fall back to the user's first stored address. The hook still\n // runs either way: skipping the policy for those tokens would reopen the very\n // bypass it exists to close.\n await getAuthConfig().validatePassword?.({\n password,\n email: resetTokenDoc.email ?? userDoc.emails?.[0]?.address?.toLowerCase() ?? '',\n context: 'reset',\n });\n\n // Hash the new password\n const hash = await bcrypt.hash(password, 10);\n\n // Commit point: atomically claim the token. If a concurrent request already\n // consumed it, abort WITHOUT touching the password (single-use, no double-spend).\n const claimedToken = await claimResetTokenById(resetTokenDoc._id as ObjectId);\n if (!claimedToken) {\n clearCookie();\n throw new Error('Invalid or expired reset token');\n }\n\n // Update user's password\n await usersCollection.updateOne(\n { _id: userDoc._id },\n { $set: { 'authMethods.password.hash': hash } }\n );\n\n // Mark the email as verified since the user proved ownership via the reset token.\n // Match with strength-2 collation: `resetTokenDoc.email` is lowercased, but the\n // stored address may keep its original casing (e.g. OAuth-created accounts), and\n // without the collation the positional `$` update would match nothing — leaving\n // the email unverified after a successful reset and blocking password login when\n // verification is required.\n if (resetTokenDoc.email) {\n await usersCollection.updateOne(\n { _id: userDoc._id, 'emails.address': resetTokenDoc.email },\n { $set: { 'emails.$.verified': true } },\n { collation: { locale: 'en', strength: 2 } }\n );\n }\n\n // Invalidate all existing sessions for this user so that other browsers/devices\n // are forced to re-authenticate with the new password\n await invalidateAllUserSessions(userDoc._id);\n\n // Revoke every outstanding magic link token and one-time code for this user's\n // email addresses. A reset is meant to lock out anyone who had access; without\n // this, a magic link or code issued before the reset (valid for up to 15\n // minutes) would still log an attacker back in — bypassing the lockout.\n // Magic link tokens are always stored with a `validateEmail`-lowercased email,\n // while a user's stored addresses may keep their original casing (e.g. OAuth-\n // created accounts), so match on the lowercased form to line up with the\n // stored keys — covering every address, not just the one that was reset.\n const userEmails = Array.from(\n new Set(\n (userDoc.emails ?? [])\n .map((e) => e.address?.toLowerCase())\n .filter((address): address is string => Boolean(address))\n )\n );\n if (userEmails.length > 0) {\n await magicLinkTokensCollection.deleteMany({ email: { $in: userEmails } });\n }\n\n // Token already consumed at the commit point; just drop the cookie.\n clearCookie();\n\n return { success: true, message: 'Password has been reset successfully' };\n}\n","export function magicLinkTemplate({\n name,\n email,\n magicLinkUrl,\n code,\n}: {\n name?: string;\n email: string;\n magicLinkUrl: string;\n code: string;\n}) {\n return `\n <p>Hi${name ? ` ${name}` : ''},</p>\n <p>Click the link below to sign in as ${email}:</p>\n <p><a href=\"${magicLinkUrl}\">${magicLinkUrl}</a></p>\n <p>Or enter this one-time code in the app:</p>\n <p><strong style=\"font-size: 24px; letter-spacing: 4px;\">${code}</strong></p>\n <p>The link and code can only be used once and will expire in 15 minutes.</p>\n <p>If you did not request this, please ignore this email.</p>\n `;\n}\n","import { z } from 'zod';\nimport { randomBytes, randomInt } from 'crypto';\n\nimport { Args, ConnectionInfo, Context } from '@/methods/types';\nimport { ObjectId, RouteParams, RouteResponse } from '@/server';\nimport { usersCollection, magicLinkTokensCollection } from './db';\nimport { getEmailConfig } from '@/app/emailConfig';\nimport { getAuthConfig } from '@/app/authConfig';\nimport { time } from '@/time';\nimport { htmlToText } from '@/utils';\nimport { validateEmail } from './validators';\nimport { consumeRateLimit } from '@/server';\nimport { getConfig } from '@/config/server';\nimport { isDisposableEmail } from './disposableEmails';\nimport { setAuthTokenCookie, setSessionUser } from './session';\nimport { hashToken } from './tokenHash';\nimport { magicLinkTemplate } from './templates/magicLinkTemplate';\nimport {\n resolveUrl,\n resolveUniqueHandle,\n serializeUserForClient,\n isDuplicateEmailError,\n} from './utils';\nimport { Session, User } from './types';\n\n// Short-lived httpOnly cookie carrying the magic link token from the landing\n// route to the `loginWithMagicLink` mutation, so it never appears on a\n// client-rendered URL.\nconst MAGIC_LINK_COOKIE = 'magicLinkToken';\n\n// Scope the cookie to the API surface so it's only sent on the loginWithMagicLink request.\nconst MAGIC_LINK_COOKIE_PATH = '/api/_internal/';\n\n// Attributes shared by the set and clear calls. Browsers only remove a cookie\n// when the clearing Set-Cookie matches the same path/secure/sameSite/httpOnly,\n// so both sites must use these identical flags or the httpOnly cookie can linger.\nconst MAGIC_LINK_COOKIE_OPTIONS = {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n // `lax` so the cookie survives the email-link navigation; `strict` can drop\n // it when the SPA is reached cross-site (some webviews).\n sameSite: 'lax',\n path: MAGIC_LINK_COOKIE_PATH,\n} as const;\n\nconst MAGIC_LINK_EXPIRY_MINUTES = 15;\n\nconst ONE_TIME_CODE_LENGTH = 6;\n\n// A 6-digit code is guessable in a way a 32-byte token is not, so each doc\n// tolerates only a few wrong guesses before it can no longer be used.\nconst MAX_CODE_ATTEMPTS = 5;\n\n/**\n * Invokes a notify-only auth hook (onAfterLogin, onLoginError, ...) without\n * letting it affect the auth flow. These hooks are typed `() => void`, but a\n * user can still pass an async function — a rejection from it, never awaited,\n * would be an unhandled promise rejection (which can crash the process). A sync\n * throw is contained the same way. Deliberately NOT awaited: a failing\n * analytics hook must not fail an otherwise-successful login, and in error\n * paths it must not mask the real error being propagated.\n */\nfunction fireAuthHook(name: string, invoke: (() => void | Promise<void>) | undefined) {\n try {\n Promise.resolve(invoke?.()).catch((hookError: unknown) => {\n console.error(`Error in ${name} hook:`, hookError);\n });\n } catch (hookError) {\n console.error(`Error in ${name} hook:`, hookError);\n }\n}\n\nfunction isMagicLinkEnabled(): boolean {\n return Boolean(getAuthConfig().magicLink?.enabled);\n}\n\nfunction isMagicLinkSignupEnabled(): boolean {\n return Boolean(getAuthConfig().magicLink?.allowSignup);\n}\n\n/**\n * Looks up a magic link token without consuming it. Only matches the hashed\n * form: tokens are stored as `hashToken(rawToken)`, and the caller must present\n * the raw token. There is deliberately no plaintext fallback — matching the raw\n * value against the stored column would let a leaked SHA-256 digest be replayed\n * as a bearer token, defeating the point of hashing at rest.\n */\nasync function findMagicLinkTokenDoc(rawToken: string) {\n return magicLinkTokensCollection.findOne({ token: hashToken(rawToken) });\n}\n\n/**\n * Atomically claims a magic link token by `_id` — the single-use enforcement\n * point. Of concurrent requests holding the same token, exactly one gets the\n * doc back; the rest get null. Returns the deleted doc, or null if already claimed.\n */\nasync function claimMagicLinkTokenById(id: ObjectId) {\n return magicLinkTokensCollection.findOneAndDelete({ _id: id });\n}\n\n/**\n * Best-effort undo of a token claim: re-inserts the exact claimed doc (same\n * `_id`, hashes, and expiry) so the single-use link/code works again on retry.\n * Used when the work the claim was committing to (the signup pre-gate or the\n * account insert) failed without producing an account — otherwise the failure would permanently burn\n * the user's link. Never throws: the caller is already propagating the real\n * error, and a failed restore must not mask it.\n */\nasync function restoreClaimedMagicLinkToken(\n claimedToken: NonNullable<Awaited<ReturnType<typeof claimMagicLinkTokenById>>>\n) {\n try {\n await magicLinkTokensCollection.insertOne(claimedToken);\n } catch (restoreError) {\n console.error('Failed to restore a magic link token after a failed signup:', restoreError);\n }\n}\n\n// The same generic response is returned whether or not an account exists for\n// the email, to prevent user enumeration.\nconst magicLinkSent = {\n success: true,\n message: 'If this email can be used to sign in, a link has been sent',\n};\n\nexport async function handleSendMagicLink(args: Args, { connectionInfo }: Context) {\n if (!isMagicLinkEnabled()) {\n throw new Error('Magic link authentication is not enabled');\n }\n\n const email = validateEmail(args.email as string);\n const ip = connectionInfo?.ip;\n\n if (ip) {\n await consumeRateLimit({\n bucket: 'magicLink',\n type: 'ip',\n value: ip,\n });\n }\n\n await consumeRateLimit({\n bucket: 'magicLink',\n type: 'email',\n value: email,\n });\n\n // Checked up front (unlike password reset): with `allowSignup` on, unknown\n // emails also get a real email, so there is no guaranteed early-out path\n // that could otherwise skip this.\n const emailProvider = getEmailConfig().provider;\n if (!emailProvider) {\n throw new Error('Email provider is not configured');\n }\n\n // Same preflight reasoning: no fallback sender. A default like\n // `noreply@modelence.com` would send customer auth email from our domain —\n // and fail SPF/DKIM on their provider anyway — so fail loudly instead.\n const fromAddress = getEmailConfig().from;\n if (!fromAddress) {\n throw new Error('Email `from` address is not configured');\n }\n\n // Also a preflight: resolved before the token is stored, so a misconfigured\n // server fails loudly without leaving behind an orphaned (unsendable) token.\n // Config ONLY — no fallback to the request's Host-derived base URL: the\n // emailed link IS the credential, and a request-derived base would let a\n // spoofed Host header poison the link's destination (login-link poisoning).\n const baseUrl = getConfig('_system.site.url') as string | undefined;\n if (!baseUrl) {\n throw new Error('Unable to build magic link: set _system.site.url (MODELENCE_SITE_URL)');\n }\n\n // Runs identically for known and unknown emails, so the thrown error reveals\n // only a property of the email domain, never account existence.\n if (!getAuthConfig().allowDisposableEmails && (await isDisposableEmail(email))) {\n throw new Error('Please use a permanent email address');\n }\n\n const userDoc = await usersCollection.findOne(\n { 'emails.address': email },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (userDoc && (userDoc.status === 'disabled' || userDoc.status === 'deleted')) {\n // For security, don't reveal the account state — skip the email silently.\n return magicLinkSent;\n }\n\n if (!userDoc && !isMagicLinkSignupEnabled()) {\n // Signup is not allowed, so a link for an unknown email could never be\n // used — skip it silently (same generic response) so account existence\n // is not revealed.\n return magicLinkSent;\n }\n\n // Generate the two credentials backed by the same doc: a long token for the\n // clickable link and a short code the user can type (e.g. on mobile, or when\n // reading the email on a different device).\n const magicLinkToken = randomBytes(32).toString('hex');\n const oneTimeCode = randomInt(0, 10 ** ONE_TIME_CODE_LENGTH)\n .toString()\n .padStart(ONE_TIME_CODE_LENGTH, '0');\n const now = Date.now();\n const createdAt = new Date(now);\n const expiresAt = new Date(now + time.minutes(MAGIC_LINK_EXPIRY_MINUTES));\n\n // Store only the hashes, never the raw values (defense against db leaks;\n // for the short code the attempt cap and expiry are the primary defense,\n // since its keyspace is small enough to brute-force a leaked hash offline).\n await magicLinkTokensCollection.insertOne({\n email,\n token: hashToken(magicLinkToken),\n code: hashToken(oneTimeCode),\n attempts: 0,\n createdAt,\n expiresAt,\n });\n\n // Point the email at the server landing route, not the SPA page, so the raw\n // token never reaches a client-rendered URL.\n const magicLinkUrl = `${baseUrl}/api/_internal/auth/magic-link?token=${magicLinkToken}`;\n\n // Send email\n const template = getEmailConfig()?.magicLink?.template || magicLinkTemplate;\n const htmlTemplate = template({ email, magicLinkUrl, code: oneTimeCode, name: '' });\n const textContent = htmlToText(htmlTemplate);\n\n await emailProvider.sendEmail({\n to: email,\n from: fromAddress,\n subject: getEmailConfig()?.magicLink?.subject || 'Your sign-in link',\n text: textContent,\n html: htmlTemplate,\n });\n\n return magicLinkSent;\n}\n\n/**\n * Server landing route for magic link sign-in. The email links here instead of\n * the SPA page: we validate the token, stash it in a short-lived httpOnly\n * cookie, and 302-redirect to the tokenless SPA page — so the token never\n * reaches client JS. The token is deliberately NOT consumed here: email\n * security scanners prefetch GET links, and consuming on GET would burn the\n * single-use token before the user ever clicks it. Consumption happens in the\n * `loginWithMagicLink` mutation triggered by the user's browser.\n */\nexport async function handleMagicLinkLanding(params: RouteParams): Promise<RouteResponse> {\n // Config ONLY — no fallback to the request's Host header, which is\n // attacker-controlled and must never decide where an auth flow redirects.\n // A link can only have been emailed with `_system.site.url` set, so this is\n // normally present; if it was unset since, fall back to a same-origin\n // relative redirect rather than trusting the request.\n const baseUrl = (getConfig('_system.site.url') as string | undefined) || '';\n const magicLinkPageUrl = resolveUrl(baseUrl, getEmailConfig().magicLink?.redirectUrl) || '/';\n\n try {\n const token = z.string().parse(params.query.token);\n\n const tokenDoc = await findMagicLinkTokenDoc(token);\n if (!tokenDoc || tokenDoc.expiresAt < new Date()) {\n throw new Error('This sign-in link is invalid or has expired.');\n }\n\n params.res.cookie(MAGIC_LINK_COOKIE, token, {\n ...MAGIC_LINK_COOKIE_OPTIONS,\n maxAge: time.minutes(MAGIC_LINK_EXPIRY_MINUTES),\n });\n\n return {\n status: 302,\n // Suppress the Referer so the token-bearing landing URL never leaks.\n headers: { 'Referrer-Policy': 'no-referrer' },\n redirect: magicLinkPageUrl,\n };\n } catch (error) {\n // Surface a fixed, friendly message; never forward the raw error (ZodError or\n // DB error) into the redirect URL. Log the real cause server-side instead.\n console.error('Error handling magic link landing:', error);\n const message = 'This sign-in link is invalid or has expired.';\n return {\n status: 302,\n headers: { 'Referrer-Policy': 'no-referrer' },\n redirect: `${magicLinkPageUrl}?status=error&message=${encodeURIComponent(message)}`,\n };\n }\n}\n\ntype MagicLinkAuthParams = {\n tokenDoc: { _id: ObjectId; email: string };\n session: Session;\n connectionInfo: ConnectionInfo;\n res: Context['res'];\n clearCookie: () => void;\n};\n\n// A magic link / one-time code proves ownership of the email, so authentication\n// is a find-or-create: an existing account logs in, an unknown email signs up\n// (when enabled). Mongo does both in one atomic upsert, so there is no\n// claim→insert→catch-duplicate→recover saga to compensate — see the retry note\n// on the E11000 branch for the one race the atomic op can't rule out by itself.\n\nconst USER_COLLATION = { locale: 'en', strength: 2 } as const;\n\n/**\n * The account-touching core of both entrypoints, given an already-validated\n * token doc. Claims the token (single-use commit point), then find-or-creates\n * the user atomically, verifies the clicked email if it wasn't already, binds\n * the session, and fires the create-path or login-path hooks accordingly.\n */\nasync function authenticateMagicLinkUser(params: MagicLinkAuthParams) {\n const { tokenDoc, session, connectionInfo, res, clearCookie } = params;\n const email = tokenDoc.email;\n const authConfig = getAuthConfig();\n const signupEnabled = isMagicLinkSignupEnabled();\n\n // Tracks whether this request is creating a new account, so a failure routes\n // to onSignupError vs onLoginError. Set in the pre-gate below the moment we\n // know the email is unknown (a signup), and kept in sync if the atomic\n // find-or-create later reveals a concurrent account (demoting to a login).\n let isSignupAttempt = false;\n\n try {\n // Commit point: atomically claim the single-use token. If a concurrent\n // request already consumed it, abort WITHOUT authenticating (no double-spend).\n // Claimed BEFORE the signup pre-gate below so that of concurrent requests\n // holding the same token doc (link + code used together, or a double-submitted\n // login) only the winner runs the signup side effects — a loser aborting here\n // has not re-run onBeforeSignup, burned a signup rate-limit slot, or promoted\n // itself to a signup attempt, so it routes to onLoginError instead of firing\n // onSignupError alongside the winner's onAfterSignup.\n const claimedToken = await claimMagicLinkTokenById(tokenDoc._id);\n if (!claimedToken) {\n clearCookie();\n throw new Error('Invalid or expired code or magic link');\n }\n\n // Signup-only side effects run BEFORE the account can exist, preserving\n // onBeforeSignup's veto contract and the pre-insert signup rate limit. The\n // upsert can't tell us \"is this a signup?\" until after it inserts, so we\n // detect the unknown-email case with a lookup here. A concurrent request\n // may still create the account between this read and the upsert — that race\n // resolves as a login (isNew=false) below, which is the correct outcome.\n let resolvedHandle: string | undefined;\n if (signupEnabled) {\n try {\n const existing = await usersCollection.findOne(\n { 'emails.address': email },\n { collation: USER_COLLATION }\n );\n if (!existing) {\n isSignupAttempt = true;\n await authConfig.onBeforeSignup?.({ email, provider: 'magicLink', connectionInfo });\n\n const ip = connectionInfo?.ip;\n if (ip) {\n await consumeRateLimit({ bucket: 'signup', type: 'ip', value: ip });\n }\n\n resolvedHandle = authConfig.generateHandle\n ? await resolveUniqueHandle(await authConfig.generateHandle({ email }), email, {\n throwOnConflict: false,\n })\n : await resolveUniqueHandle(undefined, email);\n }\n } catch (error) {\n // The pre-gate rejected (onBeforeSignup veto, rate limit) or failed\n // transiently — either way no account was created. The token was claimed\n // above; put it back so the failure doesn't permanently burn the link.\n await restoreClaimedMagicLinkToken(claimedToken);\n throw error;\n }\n }\n\n let upsertResult;\n try {\n upsertResult = await upsertMagicLinkUser(email, resolvedHandle, signupEnabled);\n } catch (error) {\n // The find-or-create failed transiently (e.g. a DB error) without\n // authenticating anyone. The token was claimed above — put it back so a\n // failure that wasn't the link's use doesn't permanently burn it.\n await restoreClaimedMagicLinkToken(claimedToken);\n throw error;\n }\n const { userDoc, isNew } = upsertResult;\n\n if (!userDoc) {\n // Reachable only when the op ran as a pure find (no insert) yet matched\n // nothing. Two cases, distinguished by whether the pre-gate resolved a\n // handle (i.e. whether this request was a signup):\n await restoreClaimedMagicLinkToken(claimedToken);\n clearCookie();\n if (signupEnabled && !isSignupAttempt) {\n // Signup is on and the pre-gate saw an existing account, so no handle\n // was resolved — but that account's emails were cleared (hard-delete)\n // before the find, so the match missed. Refusing to insert a handle-less\n // doc left userDoc null. This was a login whose account vanished\n // mid-flight; nothing was created, so let the user retry.\n throw new Error('User account is not active');\n }\n // Signup disabled and no account exists (with a permitted insert the op\n // always returns a doc). Defense in depth: the send path already refuses\n // unknown emails when signup is off, but a token can outlive its account\n // or the flag can flip between send and click. Nothing was created, so\n // restore the token rather than burning a link on a state the user can't\n // act on. This is a signup attempt the config forbids, so it routes to\n // onSignupError (as the old signup path).\n isSignupAttempt = true;\n throw new Error('Sign up with magic link is not enabled');\n }\n\n // The upsert is authoritative: a concurrent account means this was a login,\n // not the signup the pre-gate anticipated (demotes onSignupError→onLoginError).\n isSignupAttempt = isNew;\n\n if (userDoc.status === 'disabled' || userDoc.status === 'deleted') {\n // The link proved email possession but the account cannot be used. Leave\n // the token burned so it can't be retried against a re-enabled account.\n clearCookie();\n throw new Error('User account is not active');\n }\n\n let currentUserDoc = userDoc;\n // Clicking the emailed link proves ownership. On the insert path the email\n // is already stored verified; on the login path a concurrent password\n // signup may have left it unverified — this link is the proof that verifies\n // it. Skip the redundant write when already verified.\n const emailDoc = userDoc.emails?.find((e) => e.address.toLowerCase() === email);\n const wasUnverified = !isNew && !emailDoc?.verified;\n if (wasUnverified) {\n // Same strength-2 collation as the lookups: the stored address may keep\n // its original casing (e.g. OAuth-created accounts) while `email` is\n // lowercased, or the positional update silently matches nothing. Return\n // the updated doc so hooks and the response see `verified: true`.\n const verifiedDoc = await usersCollection.findOneAndUpdate(\n { _id: userDoc._id, 'emails.address': email },\n { $set: { 'emails.$.verified': true } },\n { collation: USER_COLLATION, returnDocument: 'after' }\n );\n currentUserDoc = verifiedDoc ?? userDoc;\n }\n\n await setSessionUser(session.authToken, currentUserDoc._id);\n\n if (res) {\n setAuthTokenCookie(res, session.authToken);\n }\n\n if (isNew) {\n fireAuthHook('onAfterSignup', () =>\n authConfig.onAfterSignup?.({\n provider: 'magicLink',\n user: currentUserDoc,\n session,\n connectionInfo,\n })\n );\n } else {\n if (wasUnverified) {\n fireAuthHook('onAfterEmailVerification', () =>\n authConfig.onAfterEmailVerification?.({\n provider: 'magicLink',\n user: currentUserDoc,\n session,\n connectionInfo,\n })\n );\n }\n\n fireAuthHook('onAfterLogin', () =>\n authConfig.onAfterLogin?.({\n provider: 'magicLink',\n user: currentUserDoc,\n session,\n connectionInfo,\n })\n );\n }\n\n clearCookie();\n\n return {\n user: serializeUserForClient(currentUserDoc),\n session: { authToken: session.authToken },\n };\n } catch (error) {\n // Route the failure to the hook matching what this request was attempting.\n // `isSignupAttempt` is true once the pre-gate sees an unknown email (so a\n // vetoing onBeforeSignup or a signup rate-limit rejection fires\n // onSignupError, matching the old signup path), and stays true only if the\n // account was actually created — the unknown→login race demotes it.\n if (error instanceof Error) {\n const hookName = isSignupAttempt ? 'onSignupError' : 'onLoginError';\n const hook = isSignupAttempt ? authConfig.onSignupError : authConfig.onLoginError;\n fireAuthHook(hookName, () =>\n hook?.({ provider: 'magicLink', error, session, connectionInfo })\n );\n }\n throw error;\n }\n}\n\n/**\n * Atomic find-or-create for the magic-link user. Returns the user (post-op)\n * and whether this call created it. `upsert` is gated on `signupEnabled`: when\n * signup is off this is a pure find (unknown email → `{ userDoc: null }`), so\n * no account is ever created for an email that could never sign up — no\n * insert-then-delete compensation.\n *\n * The `$elemMatch` selector (rather than `'emails.address'`) avoids the upsert\n * field-path conflict with `$setOnInsert: { emails: [...] }`. Concurrent upserts\n * can still race to insert the same email (a known Mongo caveat the unique\n * index turns into an E11000); a single retry re-runs the op, which now finds\n * the winner's doc and returns it as a login (`isNew: false`).\n *\n * The insert branch is additionally gated on a resolved handle: the pre-gate\n * only computes `resolvedHandle` when it sees an unknown email, so if a\n * concurrent hard-delete clears the existing account's emails between that read\n * and this op, the `$elemMatch` would miss and insert a doc with no handle\n * (violating the non-optional `handle` schema and its unique index). Refusing\n * the insert without a handle turns that race into a pure find returning\n * `null`, which the caller handles like signup-disabled — restore the token,\n * don't burn a link on a handle-less account.\n */\nasync function upsertMagicLinkUser(\n email: string,\n resolvedHandle: string | undefined,\n signupEnabled: boolean\n): Promise<{ userDoc: User | null; isNew: boolean }> {\n const canInsert = signupEnabled && resolvedHandle !== undefined;\n const runUpsert = () =>\n usersCollection.findOneAndUpsert(\n { emails: { $elemMatch: { address: email } } },\n {\n $setOnInsert: {\n handle: resolvedHandle as string,\n status: 'active',\n // Clicking the emailed link proves ownership of the address.\n emails: [{ address: email, verified: true }],\n createdAt: new Date(),\n // Magic link is proof of email possession — there is no per-user\n // credential to store, so no authMethods entry is added.\n authMethods: {},\n },\n },\n { upsert: canInsert, collation: USER_COLLATION }\n );\n\n try {\n const { doc, isNew } = await runUpsert();\n return { userDoc: doc as User | null, isNew };\n } catch (error) {\n if (!isDuplicateEmailError(error)) {\n throw error;\n }\n // Concurrent insert won the race; re-run to read the winner's doc. This can\n // only resolve as a match now (the account exists), so isNew is false.\n const { doc } = await runUpsert();\n return { userDoc: doc as User | null, isNew: false };\n }\n}\n\n/**\n * Consumes the magic link token stashed in the httpOnly cookie by the landing\n * route, logging in the matching user — or, when `magicLink.allowSignup` is\n * enabled, creating the account first when the email is unknown (combined\n * sign-in/sign-up, like OAuth).\n */\nexport async function handleLoginWithMagicLink(args: Args, context: Context) {\n const { session, connectionInfo, res, req } = context;\n\n if (!session) {\n throw new Error('Session is not initialized');\n }\n\n if (!isMagicLinkEnabled()) {\n throw new Error('Magic link authentication is not enabled');\n }\n\n // The token travels only in the httpOnly cookie set by the landing route —\n // there is deliberately no args fallback (new API, no legacy clients).\n const token = z.string().parse(req?.cookies?.[MAGIC_LINK_COOKIE]);\n\n const clearCookie = () => {\n // `res` is null for in-process invocations (no response to mutate).\n // Clear with the same flags used to set it; browsers ignore a clear whose\n // attributes don't match, which would leave the httpOnly cookie behind.\n res?.clearCookie(MAGIC_LINK_COOKIE, MAGIC_LINK_COOKIE_OPTIONS);\n };\n\n // Look up WITHOUT consuming: if a later step fails transiently, the link\n // stays valid so the user can retry it.\n const tokenDoc = await findMagicLinkTokenDoc(token);\n if (!tokenDoc) {\n clearCookie();\n throw new Error('Invalid or expired code or magic link');\n }\n\n if (tokenDoc.expiresAt < new Date()) {\n // Expired: remove it and reject.\n await claimMagicLinkTokenById(tokenDoc._id as ObjectId);\n clearCookie();\n throw new Error('Code or magic link has expired');\n }\n\n return authenticateMagicLinkUser({\n tokenDoc: { _id: tokenDoc._id as ObjectId, email: tokenDoc.email },\n session,\n connectionInfo,\n res,\n clearCookie,\n });\n}\n\n/**\n * Consumes the one-time code from the magic link email, logging in the\n * matching user — or, when `magicLink.allowSignup` is enabled, creating the\n * account first when the email is unknown. Same semantics as the link: the\n * code proves possession of the address.\n *\n * The typed-code path exists for contexts where the link chain breaks: native\n * apps without deep links set up, or reading the email on a different device\n * than the one signing in.\n */\nexport async function handleLoginWithOneTimeCode(args: Args, context: Context) {\n const { session, connectionInfo, res } = context;\n\n if (!session) {\n throw new Error('Session is not initialized');\n }\n\n if (!isMagicLinkEnabled()) {\n throw new Error('Magic link authentication is not enabled');\n }\n\n const email = validateEmail(args.email as string);\n // Tolerate the formatting users copy from the email (\"482 193\", \"482-193\").\n const code = z.string().parse(args.code).replace(/[\\s-]/g, '');\n\n const ip = connectionInfo?.ip;\n if (ip) {\n await consumeRateLimit({\n bucket: 'oneTimeCode',\n type: 'ip',\n value: ip,\n });\n }\n\n await consumeRateLimit({\n bucket: 'oneTimeCode',\n type: 'email',\n value: email,\n });\n\n // Look up WITHOUT consuming, keyed by email + hashed code. Docs that already\n // burned their guess allowance are excluded, so a capped code can never be\n // used even if eventually guessed right.\n const tokenDoc = await magicLinkTokensCollection.findOne({\n email,\n code: hashToken(code),\n attempts: { $lt: MAX_CODE_ATTEMPTS },\n });\n\n if (!tokenDoc) {\n // Count the failed guess against every outstanding code for this email —\n // attempts are tracked on the docs themselves so concurrent guesses are\n // counted atomically.\n await magicLinkTokensCollection.updateMany({ email }, { $inc: { attempts: 1 } });\n // Same message whether the code is wrong, capped, or the email unknown —\n // never reveal account existence.\n throw new Error('Invalid or expired code or magic link');\n }\n\n if (tokenDoc.expiresAt < new Date()) {\n // Expired: remove it and reject.\n await claimMagicLinkTokenById(tokenDoc._id as ObjectId);\n throw new Error('Code or magic link has expired');\n }\n\n return authenticateMagicLinkUser({\n tokenDoc: { _id: tokenDoc._id as ObjectId, email: tokenDoc.email },\n session,\n connectionInfo,\n res,\n // The code travels as a mutation argument — there is no cookie to clear.\n clearCookie: () => {},\n });\n}\n","import { ObjectId } from 'mongodb';\nimport { z } from 'zod';\n\nimport { getAuthConfig } from '@/app/authConfig';\nimport { consumeRateLimit } from '@/server';\nimport { AuthError, ValidationError } from '../error';\nimport { Args, Context } from '../methods/types';\nimport { usersCollection } from './db';\nimport { consumeOAuthExchangeCode, setSessionUser } from './session';\nimport type { OAuthProvider } from './types';\nimport { serializeUserForClient } from './utils';\n\n/**\n * Same message whether the code is unknown, already redeemed, expired, or bound\n * to an account that is no longer active — the client has no legitimate use for\n * the distinction, and telling them apart would let a caller probe for valid codes.\n */\nfunction invalidCodeError() {\n return new AuthError('Invalid or expired sign-in code', 'INVALID_OAUTH_CODE');\n}\n\n/**\n * Completes a native OAuth sign-in by exchanging the single-use code the\n * callback deep-linked back to the app for a real session.\n *\n * This is the second half of the mobile flow: the OAuth callback authenticates\n * the user through the ordinary find-or-create path and mints a code, and the\n * app redeems it here over TLS. The session is created at this point rather\n * than in the callback, so a code that is intercepted but never redeemed never\n * corresponds to a live session.\n */\nexport async function handleLoginWithOAuth(args: Args, { user, session, connectionInfo }: Context) {\n // Known only after the code is redeemed; until then a failure can't be\n // attributed to a specific provider.\n let provider: OAuthProvider | undefined;\n\n try {\n if (!session) {\n throw new Error('Session is not initialized');\n }\n\n // Matches handleLoginWithPassword: an authenticated session must not be\n // silently rebound to another account by a code arriving from anywhere.\n if (user) {\n throw new ValidationError('User is already authenticated', 'ALREADY_AUTHENTICATED');\n }\n\n const ip = connectionInfo?.ip;\n if (ip) {\n await consumeRateLimit({\n bucket: 'oauthExchange',\n type: 'ip',\n value: ip,\n });\n }\n\n const code = z.string().min(1).parse(args.code);\n\n // Optional at the schema level so a code minted by a previous-version\n // instance mid-deploy is still redeemable; whether it is actually required\n // is decided by the challenge stored with the code.\n const codeVerifier = z\n .string()\n .min(1)\n .optional()\n .parse(args.codeVerifier ?? undefined);\n\n // Commit point: single-use, so concurrent redemptions resolve to one winner.\n // Also where the device binding is enforced — a code minted against another\n // device's challenge does not survive this call.\n const claimed = await consumeOAuthExchangeCode(code, codeVerifier);\n if (!claimed || !ObjectId.isValid(claimed.userId)) {\n throw invalidCodeError();\n }\n provider = claimed.provider;\n\n // Re-check status here: the account may have been disabled between the\n // callback minting this code and the app redeeming it.\n const userDoc = await usersCollection.findOne({\n _id: new ObjectId(claimed.userId),\n status: { $nin: ['deleted', 'disabled'] },\n });\n\n if (!userDoc) {\n throw invalidCodeError();\n }\n\n await setSessionUser(session.authToken, userDoc._id);\n\n getAuthConfig().onAfterLogin?.({\n provider,\n user: userDoc,\n session,\n connectionInfo,\n });\n getAuthConfig().login?.onSuccess?.(userDoc);\n\n return {\n user: serializeUserForClient(userDoc),\n session: { authToken: session.authToken },\n };\n } catch (error) {\n if (error instanceof Error && provider) {\n getAuthConfig().onLoginError?.({\n provider,\n error,\n session,\n connectionInfo,\n });\n getAuthConfig().login?.onError?.(error);\n }\n throw error;\n }\n}\n","import { randomBytes } from 'crypto';\n\nimport type { AuthRateLimitsConfig } from '../app/authConfig';\nimport { Module } from '../app/module';\nimport { RateLimitRule } from '../rate-limit/types';\nimport { time } from '../time';\nimport {\n dbDisposableEmailDomains,\n emailVerificationTokensCollection,\n magicLinkTokensCollection,\n resetPasswordTokensCollection,\n usersCollection,\n} from './db';\nimport { updateDisposableEmailListCron } from './disposableEmails';\nimport { handleLoginWithPassword, handleLogout } from './login';\nimport { getOwnProfile, handleUpdateProfile } from './profile';\nimport { handleUnlinkOAuthProvider } from './unlinkOAuthProvider';\nimport { handleSignupWithPassword } from './signup';\nimport { handleVerifyEmail, handleResendEmailVerification } from './verification';\nimport {\n handleResetPassword,\n handleResetPasswordLanding,\n handleSendResetPasswordToken,\n} from './resetPassword';\nimport {\n handleLoginWithMagicLink,\n handleLoginWithOneTimeCode,\n handleMagicLinkLanding,\n handleSendMagicLink,\n} from './magicLink';\nimport { handleLoginWithOAuth } from './loginWithOAuth';\n\nfunction ruleKey(rule: Pick<RateLimitRule, 'bucket' | 'type' | 'window'>): string {\n return `${rule.bucket}\\n${rule.type}\\n${rule.window}`;\n}\n\nfunction defaultAuthRateLimits(): RateLimitRule[] {\n return [\n { bucket: 'signup', type: 'ip', window: time.minutes(15), limit: 20 },\n { bucket: 'signup', type: 'ip', window: time.days(1), limit: 200 },\n { bucket: 'signupAttempt', type: 'ip', window: time.minutes(15), limit: 50 },\n { bucket: 'signupAttempt', type: 'ip', window: time.days(1), limit: 500 },\n { bucket: 'signin', type: 'ip', window: time.minutes(15), limit: 50 },\n { bucket: 'signin', type: 'ip', window: time.days(1), limit: 500 },\n { bucket: 'verification', type: 'user', window: time.seconds(60), limit: 1 },\n { bucket: 'verification', type: 'user', window: time.days(1), limit: 10 },\n { bucket: 'passwordReset', type: 'ip', window: time.minutes(15), limit: 10 },\n { bucket: 'passwordReset', type: 'ip', window: time.days(1), limit: 100 },\n { bucket: 'passwordReset', type: 'email', window: time.hours(1), limit: 5 },\n { bucket: 'passwordReset', type: 'email', window: time.days(1), limit: 10 },\n { bucket: 'magicLink', type: 'ip', window: time.minutes(15), limit: 10 },\n { bucket: 'magicLink', type: 'ip', window: time.days(1), limit: 100 },\n { bucket: 'magicLink', type: 'email', window: time.hours(1), limit: 5 },\n { bucket: 'magicLink', type: 'email', window: time.days(1), limit: 10 },\n { bucket: 'oneTimeCode', type: 'ip', window: time.minutes(15), limit: 20 },\n { bucket: 'oneTimeCode', type: 'ip', window: time.days(1), limit: 100 },\n { bucket: 'oneTimeCode', type: 'email', window: time.hours(1), limit: 10 },\n { bucket: 'oneTimeCode', type: 'email', window: time.days(1), limit: 20 },\n // Redeeming a mobile OAuth exchange code. Codes are 256-bit and live for a\n // minute, so this guards against bulk guessing rather than a targeted one.\n { bucket: 'oauthExchange', type: 'ip', window: time.minutes(15), limit: 30 },\n { bucket: 'oauthExchange', type: 'ip', window: time.days(1), limit: 300 },\n { bucket: 'updateProfile', type: 'user', window: time.minutes(15), limit: 30 },\n { bucket: 'updateProfile', type: 'user', window: time.days(1), limit: 200 },\n ];\n}\n\nfunction collectOverrides(config: AuthRateLimitsConfig): RateLimitRule[] {\n const overrides: RateLimitRule[] = [];\n\n const buckets: Array<keyof AuthRateLimitsConfig> = [\n 'signup',\n 'signupAttempt',\n 'signin',\n 'verification',\n 'passwordReset',\n 'magicLink',\n 'oneTimeCode',\n 'oauthExchange',\n 'updateProfile',\n ];\n\n for (const bucket of buckets) {\n const rules = config[bucket];\n if (rules === undefined) continue;\n for (const rule of rules) {\n overrides.push({ bucket, ...rule });\n }\n }\n\n return overrides;\n}\n\n/**\n * Builds the rate limit rules for all authentication endpoints by merging\n * caller-supplied overrides into the built-in defaults.\n *\n * Merge semantics:\n * - Each override rule replaces the default rule with the same\n * (bucket, type, window) tuple.\n * - Defaults whose tuple is not overridden are preserved.\n * - Override rules with no matching default are added.\n *\n * This means `signup: [{ type: 'ip', window: time.minutes(15), limit: 10 }]`\n * tightens the 15-minute signup cap without dropping the per-day cap.\n */\nexport function buildAuthRateLimits(config: AuthRateLimitsConfig = {}): RateLimitRule[] {\n const defaults = defaultAuthRateLimits();\n const overrides = collectOverrides(config);\n\n const overrideByKey = new Map<string, RateLimitRule>();\n for (const rule of overrides) {\n overrideByKey.set(ruleKey(rule), rule);\n }\n\n const merged: RateLimitRule[] = [];\n const consumed = new Set<string>();\n for (const def of defaults) {\n const key = ruleKey(def);\n const override = overrideByKey.get(key);\n if (override !== undefined) {\n merged.push(override);\n consumed.add(key);\n } else {\n merged.push(def);\n }\n }\n for (const [key, rule] of overrideByKey) {\n if (!consumed.has(key)) {\n merged.push(rule);\n }\n }\n return merged;\n}\n\nexport async function createGuestUser() {\n // TODO: add rate-limiting and captcha handling\n\n const guestId = randomBytes(9)\n .toString('base64')\n .replace(/[+/]/g, (c) => (c === '+' ? 'a' : 'b'));\n\n const handle = `guest_${guestId}`;\n // TODO: re-try on handle collision\n\n const result = await usersCollection.insertOne({\n handle,\n status: 'active',\n createdAt: new Date(),\n authMethods: {},\n });\n\n return result.insertedId;\n}\n\nexport default new Module('_system.user', {\n stores: [\n usersCollection,\n dbDisposableEmailDomains,\n emailVerificationTokensCollection,\n resetPasswordTokensCollection,\n magicLinkTokensCollection,\n ],\n queries: {\n getOwnProfile,\n },\n mutations: {\n signupWithPassword: handleSignupWithPassword,\n loginWithPassword: handleLoginWithPassword,\n logout: handleLogout,\n resendEmailVerification: handleResendEmailVerification,\n sendResetPasswordToken: handleSendResetPasswordToken,\n resetPassword: handleResetPassword,\n sendMagicLink: handleSendMagicLink,\n loginWithMagicLink: handleLoginWithMagicLink,\n loginWithOneTimeCode: handleLoginWithOneTimeCode,\n loginWithOAuth: handleLoginWithOAuth,\n updateProfile: handleUpdateProfile,\n unlinkOAuthProvider: handleUnlinkOAuthProvider,\n },\n cronJobs: {\n updateDisposableEmailList: updateDisposableEmailListCron,\n },\n rateLimits: buildAuthRateLimits(),\n configSchema: {\n 'auth.email.enabled': {\n type: 'boolean',\n isPublic: true,\n default: true,\n },\n 'auth.email.from': {\n type: 'string',\n isPublic: false,\n default: '',\n },\n 'auth.email.verification': {\n type: 'boolean',\n isPublic: true,\n default: true,\n },\n 'auth.google.enabled': {\n type: 'boolean',\n isPublic: true,\n default: false,\n },\n 'auth.google.clientId': {\n type: 'string',\n isPublic: false,\n default: '',\n },\n 'auth.google.clientSecret': {\n type: 'secret',\n isPublic: false,\n default: '',\n },\n 'auth.github.enabled': {\n type: 'boolean',\n isPublic: true,\n default: false,\n },\n 'auth.github.clientId': {\n type: 'string',\n isPublic: false,\n default: '',\n },\n 'auth.github.clientSecret': {\n type: 'secret',\n isPublic: false,\n default: '',\n },\n // Deep links the mobile OAuth callback may redirect to, e.g. \"myapp://auth\",\n // separated by commas or newlines. Merged with `AuthConfig.mobile.redirectUrls`.\n // Not public: the client names its own redirect target, and publishing the\n // allowlist would only advertise what an attacker needs to guess.\n 'auth.mobile.redirectUrls': {\n type: 'text',\n isPublic: false,\n default: '',\n },\n },\n routes: [\n {\n path: '/api/_internal/auth/verify-email',\n handlers: {\n get: handleVerifyEmail,\n },\n },\n {\n // Server landing for password reset (see handleResetPasswordLanding).\n // Runs before the SPA catch-all.\n path: '/api/_internal/auth/reset-password',\n handlers: {\n get: handleResetPasswordLanding,\n },\n },\n {\n // Server landing for magic link sign-in (see handleMagicLinkLanding).\n // Runs before the SPA catch-all.\n path: '/api/_internal/auth/magic-link',\n handlers: {\n get: handleMagicLinkLanding,\n },\n },\n ],\n});\n","import { AppConfig, ConfigSchema, ConfigType } from './types';\n\ntype LocalConfigVariant = 'withRemoteServer' | 'withoutRemoteServer';\n\nconst localConfigMap = {\n withoutRemoteServer: {\n MONGODB_URI: '_system.mongodbUri',\n MONGODB_POOL_SIZE: '_system.mongodbPoolSize',\n MODELENCE_AUTH_GOOGLE_ENABLED: '_system.user.auth.google.enabled',\n MODELENCE_AUTH_GOOGLE_CLIENT_ID: '_system.user.auth.google.clientId',\n MODELENCE_AUTH_GOOGLE_CLIENT_SECRET: '_system.user.auth.google.clientSecret',\n MODELENCE_AUTH_GITHUB_ENABLED: '_system.user.auth.github.enabled',\n MODELENCE_AUTH_GITHUB_CLIENT_ID: '_system.user.auth.github.clientId',\n MODELENCE_AUTH_GITHUB_CLIENT_SECRET: '_system.user.auth.github.clientSecret',\n MODELENCE_AUTH_GITHUB_CLIENT_SCOPES: '_system.user.auth.github.scopes',\n MODELENCE_AUTH_MOBILE_REDIRECT_URLS: '_system.user.auth.mobile.redirectUrls',\n MODELENCE_EMAIL_RESEND_API_KEY: '_system.email.resend.apiKey',\n MODELENCE_EMAIL_AWS_SES_REGION: '_system.email.awsSes.region',\n MODELENCE_EMAIL_AWS_SES_ACCESS_KEY_ID: '_system.email.awsSes.accessKeyId',\n MODELENCE_EMAIL_AWS_SES_SECRET_ACCESS_KEY: '_system.email.awsSes.secretAccessKey',\n MODELENCE_EMAIL_SMTP_HOST: '_system.email.smtp.host',\n MODELENCE_EMAIL_SMTP_PORT: '_system.email.smtp.port',\n MODELENCE_EMAIL_SMTP_USER: '_system.email.smtp.user',\n MODELENCE_EMAIL_SMTP_PASS: '_system.email.smtp.pass',\n MODELENCE_SITE_URL: '_system.site.url',\n MODELENCE_ENV_TYPE: '_system.env.type',\n MODELENCE_MULTI_INSTANCE: '_system.multiInstance',\n // deprecated\n MODELENCE_ENV: '_system.env',\n GOOGLE_AUTH_ENABLED: '_system.user.auth.google.enabled',\n GOOGLE_AUTH_CLIENT_ID: '_system.user.auth.google.clientId',\n GOOGLE_AUTH_CLIENT_SECRET: '_system.user.auth.google.clientSecret',\n },\n withRemoteServer: {\n MODELENCE_SITE_URL: '_system.site.url',\n },\n} as const;\n\nfunction formatLocalConfigValue(value: string, type: ConfigType): string | number | boolean {\n if (type === 'number') {\n const numValue = Number(value);\n if (isNaN(numValue)) {\n throw new Error(`Invalid number value for config: ${value}`);\n }\n return numValue;\n }\n if (type === 'boolean') {\n if (value.toLowerCase() === 'true') {\n return true;\n }\n if (value.toLowerCase() === 'false') {\n return false;\n }\n throw new Error(`Invalid boolean value for config: ${value}`);\n }\n return value;\n}\n\nfunction getConfigsFromEnvMap(\n configMap: Record<string, string>,\n configSchema: ConfigSchema\n): AppConfig[] {\n const configs: AppConfig[] = [];\n\n for (const [envVar, configKey] of Object.entries(configMap)) {\n const value = process.env[envVar];\n const configSchemaEntry = configSchema[configKey];\n if (value) {\n const type = configSchemaEntry?.type ?? 'string';\n configs.push({\n key: configKey,\n type: type,\n value: formatLocalConfigValue(value, type),\n });\n }\n }\n\n return configs;\n}\n\n// Where an attached local dev server actually serves: the default for\n// `_system.site.url`, and what Studio shows when it has to tell someone the\n// environment is running on a developer's machine instead of in the cloud.\nexport function getLocalSiteUrl(): string {\n const port = process.env.MODELENCE_PORT || process.env.PORT || 3000;\n // Reads the env var directly on purpose: this is what *produces* the\n // `_system.site.url` config below, so getConfig would be circular.\n // eslint-disable-next-line no-restricted-syntax\n return process.env.MODELENCE_SITE_URL || `http://localhost:${port}`;\n}\n\nexport function getLocalConfigs(\n configSchema: ConfigSchema,\n variant: LocalConfigVariant = 'withoutRemoteServer'\n): AppConfig[] {\n const configMap = localConfigMap[variant];\n const configs = getConfigsFromEnvMap(configMap, configSchema);\n\n // Attached local dev (`modelence setup` writes the marker; Studio sandboxes\n // pre-set \"sandbox\"): default the site URL to this process instead of the\n // environment URL from the cloud config, so OAuth callbacks and email links\n // point at the server actually running. An explicit MODELENCE_SITE_URL wins.\n if (\n variant === 'withRemoteServer' &&\n process.env.MODELENCE_RUNTIME === 'local' &&\n !configs.some(({ key }) => key === '_system.site.url')\n ) {\n configs.push({\n key: '_system.site.url',\n type: 'string',\n value: getLocalSiteUrl(),\n });\n }\n\n return configs;\n}\n","import { createHash, randomUUID } from 'crypto';\nimport fs from 'fs';\nimport os from 'os';\nimport path from 'path';\n\nlet instanceId: string | undefined;\n\n// Random once-per-machine ID persisted in the user's home dir — outside any\n// repo so it can never be committed and cloned. Containers without a writable\n// home fall back to hostname, which is unique per container.\nfunction getMachineKey(): string {\n try {\n const dir = path.join(os.homedir(), '.modelence');\n const file = path.join(dir, 'machine-id');\n try {\n const existing = fs.readFileSync(file, 'utf8').trim();\n if (existing) {\n return existing;\n }\n } catch {\n // Missing or unreadable — generate below.\n }\n const generated = randomUUID();\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(file, generated + '\\n');\n return generated;\n } catch {\n return os.hostname();\n }\n}\n\n// Stable identity of \"this working copy connected to this environment\": the\n// same directory reconnecting (e.g. a dev-server restart) presents the same\n// ID, while the same directory pointed at a different environment gets a new\n// one — containers.id is unique across environments in Studio.\nexport function getInstanceId(): string {\n if (!instanceId) {\n instanceId = createHash('sha256')\n .update(`${getMachineKey()}\\n${process.cwd()}\\n${process.env.MODELENCE_ENVIRONMENT_ID || ''}`)\n .digest('hex')\n .slice(0, 32);\n }\n return instanceId;\n}\n\n// A dev-time instance — a developer machine or a Studio sandbox, as marked by\n// `modelence setup` — as opposed to a deployed cloud container.\nexport function isDevRuntime(): boolean {\n const runtime = process.env.MODELENCE_RUNTIME;\n return runtime === 'local' || runtime === 'sandbox';\n}\n\n// A developer's own machine — the only runtime that may force-take an\n// environment lease held by another instance.\nexport function isLocalRuntime(): boolean {\n return process.env.MODELENCE_RUNTIME === 'local';\n}\n\n// For dev runtimes the container ID is the instance ID; cloud containers keep\n// the Studio-issued MODELENCE_CONTAINER_ID.\nexport function getContainerId(): string | undefined {\n return isDevRuntime() ? getInstanceId() : process.env.MODELENCE_CONTAINER_ID;\n}\n","import os from 'os';\nimport { getContainerId, isDevRuntime, isLocalRuntime } from './instance';\nimport { getLocalSiteUrl } from '../config/local';\nimport { ConfigSchema } from '../config/types';\nimport { CronJobMetadata } from '../cron/types';\nimport { RoleDefinition } from '../auth/types';\nimport { Store } from '../data/store';\nimport { AppConfig } from '../config/types';\nimport { ModelSchema } from '../data/types';\n\ntype CloudBackendConnectOkResponse = {\n status: 'ok';\n configs: AppConfig[];\n environmentId: string;\n appAlias: string;\n environmentAlias: string;\n telemetry: {\n isEnabled: boolean;\n serviceName: string;\n };\n};\n\ntype CloudBackendConnectErrorResponse = {\n status: 'error';\n error: string;\n};\n\nexport type CloudBackendConnectResponse =\n | CloudBackendConnectOkResponse\n | CloudBackendConnectErrorResponse;\n\nexport async function connectCloudBackend({\n configSchema,\n cronJobsMetadata,\n stores,\n roles,\n}: {\n configSchema?: ConfigSchema;\n cronJobsMetadata?: CronJobMetadata[];\n stores?: Store<ModelSchema, Record<string, never>>[];\n roles?: Record<string, RoleDefinition>;\n}): Promise<CloudBackendConnectOkResponse> {\n const containerId = getContainerId();\n if (!containerId) {\n throw new Error('Unable to connect to Modelence Cloud: MODELENCE_CONTAINER_ID is not set');\n }\n\n // Set by `modelence dev --takeover`: Studio disconnects whoever holds the\n // environment (pausing a sandbox, or detaching another dev instance) and\n // hands over the lease in the same connect request. Only the `local`\n // runtime may take over — Studio enforces the same rule server-side.\n const takeover = isLocalRuntime() && process.env.MODELENCE_TAKEOVER === '1';\n\n try {\n const dataStores = (stores ?? []).map((store) => ({\n name: store.getName(),\n schema: store.getSerializedSchema(),\n collections: [store.getName()],\n version: 2,\n indexes: store.getIndexes(),\n searchIndexes: store.getSearchIndexes(),\n indexCreationMode: store.getIndexCreationMode(),\n }));\n\n const data = await callCloudApi<CloudBackendConnectResponse>('/api/connect', 'POST', {\n hostname: os.hostname(),\n runtime: process.env.MODELENCE_RUNTIME,\n containerId,\n ...(isLocalRuntime() ? { localSiteUrl: getLocalSiteUrl() } : {}),\n ...(takeover ? { force: true } : {}),\n dataModels: dataStores,\n configSchema,\n cronJobsMetadata,\n roles,\n });\n\n if (data.status === 'error') {\n throw Object.assign(new Error(`Unable to connect to Modelence Cloud: ${data.error}`), {\n responseBody: data,\n });\n }\n\n console.log('Successfully connected to Modelence Cloud');\n\n return data;\n } catch (error) {\n const cloudError = error as { message?: string; status?: number; responseBody?: unknown };\n\n // /api/connect's only 409 is \"another instance already holds this\n // environment\" — point a developer at the takeover flag.\n if (isLocalRuntime() && cloudError?.status === 409 && !takeover) {\n const detail =\n (cloudError.responseBody as { error?: string } | undefined)?.error ?? cloudError.message;\n console.error(\n `${detail}\\nRe-run with the --takeover flag (e.g. npm run dev -- --takeover) to ` +\n 'disconnect it and connect this instance instead.'\n );\n process.exit(1);\n }\n\n // A refusal the server explained (e.g. another instance already holds\n // this environment) is an expected condition on a dev machine: print the\n // message alone and exit instead of an uncaught stack dump.\n if (isDevRuntime() && (cloudError?.status ?? cloudError?.responseBody) !== undefined) {\n console.error(cloudError.message);\n process.exit(1);\n }\n console.error('Unable to connect to Modelence Cloud:', error);\n throw error;\n }\n}\n\nexport async function fetchConfigs() {\n return callCloudApi<{ configs: AppConfig[] }>('/api/configs', 'GET');\n}\n\nexport type CloudSyncResponse = { status: 'ok' } | { status: 'detached'; message?: string };\n\nexport async function syncStatus() {\n const data = await callCloudApi<CloudSyncResponse>('/api/sync', 'POST', {\n containerId: getContainerId(),\n });\n return data;\n}\n\nexport async function callCloudApi<T = unknown>(\n endpoint: string,\n method: string,\n payload?: object\n): Promise<T> {\n const { MODELENCE_SERVICE_ENDPOINT, MODELENCE_SERVICE_TOKEN } = process.env;\n\n if (!MODELENCE_SERVICE_ENDPOINT) {\n throw new Error('Unable to connect to Modelence Cloud: MODELENCE_SERVICE_ENDPOINT is not set');\n }\n\n const response = await fetch(`${MODELENCE_SERVICE_ENDPOINT}${endpoint}`, {\n method,\n headers: {\n Authorization: `Bearer ${MODELENCE_SERVICE_TOKEN}`,\n ...(payload ? { 'Content-Type': 'application/json' } : {}),\n },\n body: payload ? JSON.stringify(payload) : undefined,\n });\n\n if (!response.ok) {\n const data = await response.text();\n let parsed: unknown;\n let messageDetail: string = data;\n try {\n parsed = JSON.parse(data);\n const errorField = (parsed as { error?: unknown }).error;\n if (typeof errorField === 'string') {\n messageDetail = errorField;\n } else if (errorField && typeof errorField === 'object') {\n // Structured error envelope from newer endpoints — keep the body on\n // the thrown Error so callers can pull a code if they need to.\n const message = (errorField as { message?: unknown }).message;\n if (typeof message === 'string') {\n messageDetail = message;\n }\n }\n } catch {\n /* response was not JSON — fall back to raw text */\n }\n const error = new Error(\n `Unable to connect to Modelence Cloud: HTTP status: ${response.status}, ${messageDetail}`\n );\n if (parsed !== undefined) {\n (error as Error & { responseBody?: unknown }).responseBody = parsed;\n }\n (error as Error & { status?: number }).status = response.status;\n throw error;\n }\n\n if (response.status === 204 || response.headers?.get('content-length') === '0') {\n return undefined as T;\n }\n\n return (await response.json()) as T;\n}\n","import { time } from '../time';\nimport { fetchConfigs, syncStatus } from '../app/backendApi';\nimport { isDevRuntime } from '../app/instance';\nimport { getLocalConfigs } from './local';\nimport { loadConfigs, getSchema } from './server';\nimport { AppConfig } from './types';\n\nlet isSyncing = false;\n\nconst SYNC_INTERVAL = time.seconds(10);\n\nexport function startConfigSync() {\n setInterval(async () => {\n if (isSyncing) {\n return;\n }\n\n isSyncing = true;\n\n // TODO: move this sync outside of config\n try {\n const sync = await syncStatus();\n // Another instance took over this environment. The dev-runtime guard\n // makes sure a Studio bug can never take down cloud containers.\n if (sync?.status === 'detached' && isDevRuntime()) {\n console.error(\n `Detached from Modelence Cloud: ${sync.message ?? 'this environment is now connected from another instance.'}`\n );\n process.exit(1);\n }\n } catch (error) {\n console.error('Error syncing status', error);\n }\n\n try {\n await syncConfig();\n } catch (error) {\n console.error('Error syncing config', error);\n }\n\n isSyncing = false;\n }, SYNC_INTERVAL);\n}\n\nexport function loadRemoteConfigs(configs: AppConfig[]) {\n loadConfigs(configs);\n loadConfigs(getLocalConfigs(getSchema(), 'withRemoteServer'));\n}\n\nasync function syncConfig() {\n const { configs } = await fetchConfigs();\n loadRemoteConfigs(configs);\n}\n","import { schema } from '../data/types';\nimport { Store } from '../data/store';\n\n/**\n * Database collection for storing distributed locks.\n *\n * Locks are used to ensure that only one instance of the application can perform\n * a specific action at a time, such as running cron jobs or migrations\n */\nexport const locksCollection = new Store('_modelenceLocks', {\n schema: {\n _id: schema.string(), // unique identifier for the lock, used as the primary key\n instanceId: schema.string(),\n acquiredAt: schema.date(),\n\n resource: schema.string(), // deprecated, will be dropped in v1.0.0 (use _id instead)\n },\n indexes: [\n {\n // TODO(v1.0.0): remove after dropping legacy `resource` compatibility.\n key: { resource: 1 },\n unique: true,\n },\n {\n // TODO(v1.0.0): remove after dropping legacy `resource` compatibility.\n key: { resource: 1, instanceId: 1 },\n },\n {\n // TODO(v1.0.0): remove after dropping legacy `resource` compatibility.\n key: { resource: 1, acquiredAt: 1 },\n },\n ],\n indexCreationMode: 'blocking',\n});\n","import { randomBytes } from 'crypto';\nimport { MongoError } from 'mongodb';\nimport { locksCollection } from './db';\nimport { logDebug } from '../telemetry';\nimport { time } from '@/time';\n\n/**\n * In-memory cache to avoid frequent lock acquisition attempts\n * for the same resource within a short time frame.\n */\nconst lockCache: Record<\n string,\n {\n value: boolean;\n expiresAt: number;\n }\n> = {};\n\n/**\n * Time duration to consider a cached lock acquisition valid\n */\nconst DEFAULT_CACHE_DURATION = time.seconds(10);\n\n/**\n * Unique identifier for this application instance.\n * Generated once per application instance to track which container owns which locks.\n */\nconst INSTANCE_ID = randomBytes(32).toString('base64url');\n\n/**\n * Time after which a lock is expired\n */\nconst DEFAULT_LOCK_DURATION = time.seconds(30);\n\ntype LockOptions = {\n lockDuration?: number;\n successfulLockCacheDuration?: number;\n failedLockCacheDuration?: number;\n heartbeat?: boolean;\n bypassCache?: boolean;\n instanceId?: string;\n};\n\ntype LockHeartbeat = {\n timer: ReturnType<typeof setTimeout> | null;\n stopRequested: boolean;\n lockDuration: number;\n heartbeatInterval: number;\n};\n\nconst lockHeartbeats = new Map<string, LockHeartbeat>();\n\ntype DuplicateKeyMongoError = MongoError & {\n code?: number;\n keyPattern?: Record<string, unknown>;\n};\n\nconst isDuplicateKeyError = (error: unknown): error is DuplicateKeyMongoError =>\n error instanceof MongoError && error.code === 11000;\n\nconst hasKeyPatternField = (error: DuplicateKeyMongoError, field: string): boolean =>\n typeof error.keyPattern === 'object' &&\n error.keyPattern !== null &&\n Object.prototype.hasOwnProperty.call(error.keyPattern, field);\n\n/**\n * TODO(v1.0.0): Remove legacy `resource` fallback support.\n * This helper only exists for pre-1.0.0 lock documents where `_id !== resource`.\n * Delete this function and its call sites when releasing 1.0.0.\n */\nconst shouldFallbackToLegacyStrategy = async ({\n error,\n resource,\n}: {\n error: DuplicateKeyMongoError;\n resource: string;\n}): Promise<boolean> => {\n if (hasKeyPatternField(error, 'resource')) {\n return true;\n }\n\n if (hasKeyPatternField(error, '_id')) {\n return false;\n }\n\n const existingLock = (await locksCollection.findOne({ resource })) as {\n _id?: unknown;\n } | null;\n\n return !!existingLock && existingLock._id !== resource;\n};\n\nconst tryAcquireLockById = async ({\n resource,\n staleThresholdDate,\n instanceId,\n}: {\n resource: string;\n staleThresholdDate: Date;\n instanceId: string;\n}): Promise<boolean> => {\n const result = await locksCollection.upsertOne(\n {\n _id: resource,\n $or: [{ instanceId }, { acquiredAt: { $lt: staleThresholdDate } }],\n },\n {\n $set: {\n resource,\n instanceId,\n acquiredAt: new Date(),\n },\n $setOnInsert: {\n _id: resource,\n },\n }\n );\n\n return result.upsertedCount > 0 || result.modifiedCount > 0;\n};\n\nconst deleteLegacyLock = async ({\n resource,\n instanceId,\n staleThresholdDate,\n}: {\n resource: string;\n instanceId: string;\n staleThresholdDate?: Date;\n}): Promise<boolean> => {\n const selector = staleThresholdDate\n ? {\n resource,\n _id: { $ne: resource },\n $or: [{ instanceId }, { acquiredAt: { $lt: staleThresholdDate } }],\n }\n : {\n resource,\n instanceId,\n };\n\n const legacyDeleteResult = await locksCollection.deleteOne(selector);\n\n return legacyDeleteResult.deletedCount > 0;\n};\n\nconst stopLockHeartbeat = (resource: string) => {\n const heartbeatKey = resource;\n const heartbeat = lockHeartbeats.get(heartbeatKey);\n if (!heartbeat) {\n return;\n }\n\n heartbeat.stopRequested = true;\n if (heartbeat.timer) {\n clearTimeout(heartbeat.timer);\n heartbeat.timer = null;\n }\n\n lockHeartbeats.delete(heartbeatKey);\n};\n\nconst startLockHeartbeat = ({\n resource,\n lockDuration,\n instanceId,\n}: {\n resource: string;\n lockDuration: number;\n instanceId: string;\n}) => {\n const heartbeatInterval = Math.floor(lockDuration / 3);\n const heartbeatKey = resource;\n const existingHeartbeat = lockHeartbeats.get(heartbeatKey);\n\n if (\n existingHeartbeat &&\n !existingHeartbeat.stopRequested &&\n existingHeartbeat.heartbeatInterval === heartbeatInterval &&\n existingHeartbeat.lockDuration === lockDuration\n ) {\n return;\n }\n\n if (existingHeartbeat) {\n existingHeartbeat.stopRequested = true;\n if (existingHeartbeat.timer) {\n clearTimeout(existingHeartbeat.timer);\n existingHeartbeat.timer = null;\n }\n lockHeartbeats.delete(heartbeatKey);\n }\n\n const heartbeat: LockHeartbeat = {\n timer: null,\n stopRequested: false,\n lockDuration,\n heartbeatInterval,\n };\n\n const scheduleRefresh = () => {\n heartbeat.timer = setTimeout(() => {\n void acquireLock(resource, {\n lockDuration,\n bypassCache: true,\n instanceId,\n })\n .then((isLockAcquired) => {\n if (!isLockAcquired) {\n heartbeat.stopRequested = true;\n logDebug(`Lost lock while refreshing heartbeat: ${resource}`, {\n source: 'lock',\n resource,\n instanceId,\n });\n }\n })\n .finally(() => {\n if (heartbeat.stopRequested) {\n lockHeartbeats.delete(heartbeatKey);\n return;\n }\n scheduleRefresh();\n });\n }, heartbeatInterval);\n };\n\n lockHeartbeats.set(heartbeatKey, heartbeat);\n scheduleRefresh();\n};\n\n/**\n * Acquires a lock of the specified resource.\n * If the lock already exists and is owned by another container, this will fail.\n * If the lock is expired (no recent heartbeat), it will be taken over.\n *\n * @param resource - The type of lock to acquire\n * @param options.lockDuration - Time in ms after which a lock is considered expired (default: 30s)\n * @param options.successfulLockCacheDuration - Time in ms to cache successful lock acquisition\n * @param options.failedLockCacheDuration - Time in ms to cache failed lock acquisition\n * @param options.heartbeat - If true, auto-refreshes lock ownership at lockDuration/3 intervals\n * @param options.bypassCache - If true, skips the in-memory cache and always hits the DB\n * @param options.instanceId - The unique identifier for this application instance\n * @returns true if lock was acquired, false otherwise\n */\nexport async function acquireLock(\n resource: string,\n {\n lockDuration = DEFAULT_LOCK_DURATION,\n successfulLockCacheDuration = DEFAULT_CACHE_DURATION,\n failedLockCacheDuration = DEFAULT_CACHE_DURATION,\n heartbeat,\n bypassCache,\n instanceId = INSTANCE_ID,\n }: LockOptions = {}\n): Promise<boolean> {\n const now = Date.now();\n if (!bypassCache && lockCache[resource] && now < lockCache[resource].expiresAt) {\n if (lockCache[resource].value && heartbeat) {\n startLockHeartbeat({\n resource,\n lockDuration,\n instanceId,\n });\n }\n return lockCache[resource].value;\n }\n\n const staleThresholdDate = new Date(now - lockDuration);\n\n logDebug(`Attempting to acquire lock: ${resource}`, {\n source: 'lock',\n resource,\n instanceId,\n });\n\n try {\n const isLockAcquired = await acquireLockById({\n resource,\n staleThresholdDate,\n instanceId,\n });\n\n lockCache[resource] = {\n value: isLockAcquired,\n expiresAt: now + (isLockAcquired ? successfulLockCacheDuration : failedLockCacheDuration),\n };\n\n if (isLockAcquired) {\n if (heartbeat) {\n startLockHeartbeat({\n resource,\n lockDuration,\n instanceId,\n });\n }\n\n logDebug(`Lock acquired: ${resource}`, {\n source: 'lock',\n resource,\n instanceId,\n });\n } else {\n logDebug(`Failed to acquire lock (already held): ${resource}`, {\n source: 'lock',\n resource,\n instanceId,\n });\n }\n\n return isLockAcquired;\n } catch {\n lockCache[resource] = {\n value: false,\n expiresAt: now + failedLockCacheDuration,\n };\n logDebug(`Failed to acquire lock (already held): ${resource}`, {\n source: 'lock',\n resource,\n instanceId,\n });\n return false;\n }\n}\n\nconst acquireLockById = async ({\n resource,\n staleThresholdDate,\n instanceId,\n}: {\n resource: string;\n staleThresholdDate: Date;\n instanceId: string;\n}): Promise<boolean> => {\n try {\n return await tryAcquireLockById({ resource, staleThresholdDate, instanceId });\n } catch (error) {\n // TODO(v1.0.0): Remove the legacy fallback\n // Backward compatibility: if an old lock doc exists with a non-resource _id and\n // a unique `resource` index, upserting the new `_id = resource` shape can fail.\n // Migrate that legacy lock document to `_id = resource` and retry current strategy.\n if (isDuplicateKeyError(error) && (await shouldFallbackToLegacyStrategy({ error, resource }))) {\n const hasDeletedLegacyLock = await deleteLegacyLock({\n resource,\n staleThresholdDate,\n instanceId,\n });\n\n if (!hasDeletedLegacyLock) {\n return false;\n }\n\n try {\n return await tryAcquireLockById({ resource, staleThresholdDate, instanceId });\n } catch (retryError) {\n if (isDuplicateKeyError(retryError)) {\n return false;\n }\n throw retryError;\n }\n }\n\n // Duplicate _id means the lock exists but was not eligible for update (owned + fresh).\n if (isDuplicateKeyError(error)) {\n return false;\n }\n\n throw error;\n }\n};\n\n/**\n * Releases a lock of the specified resource owned by this container.\n *\n * @param resource - The resource to release the lock for\n * @param options.instanceId - The unique identifier for this application instance\n * @returns true if lock was released, false if lock wasn't owned by this container or release failed.\n */\nexport async function releaseLock(\n resource: string,\n {\n instanceId = INSTANCE_ID,\n }: {\n instanceId?: string;\n } = {}\n): Promise<boolean> {\n stopLockHeartbeat(resource);\n\n try {\n const result = await locksCollection.deleteOne({\n _id: resource,\n instanceId,\n });\n\n // TODO(v1.0.0): Remove the legacy fallback\n if (result.deletedCount === 0) {\n const hasDeletedLegacyLock = await deleteLegacyLock({\n resource,\n instanceId,\n });\n\n return hasDeletedLegacyLock;\n }\n\n return result.deletedCount > 0;\n } catch {\n return false;\n } finally {\n delete lockCache[resource];\n }\n}\n","// import { Worker, isMainThread, parentPort, workerData } from 'worker_threads';\n\nimport { time } from '../time';\nimport { CronJob, CronJobInputParams } from './types';\nimport { startTransaction, captureError } from '@/telemetry';\nimport { Module } from '../app/module';\nimport { schema } from '../data/types';\nimport { Store } from '../data/store';\nimport { acquireLock } from '../lock/helpers';\nimport { getMongodbUri } from '@/db/client';\n\nconst cronJobs: Record<string, CronJob> = {};\nlet cronJobsInterval: NodeJS.Timeout | null = null;\n\nconst cronJobsCollection = new Store('_modelenceCronJobs', {\n schema: {\n alias: schema.string(),\n lastStartDate: schema.date().optional(),\n },\n indexes: [{ key: { alias: 1 }, unique: true, background: true }],\n});\n\n// TODO: allow changing interval and timeout with cron jobconfigs\nexport function defineCronJob(\n alias: CronJob['alias'],\n {\n description = '',\n interval,\n timeout = Math.min(Math.max(interval, time.minutes(1)), time.days(1)),\n handler,\n }: CronJobInputParams\n) {\n if (cronJobs[alias]) {\n throw new Error(`Duplicate cron job declaration: '${alias}' already exists`);\n }\n\n if (cronJobsInterval) {\n throw new Error(\n `Unable to add a cron job - cron jobs have already been initialized: [${alias}]`\n );\n }\n\n if (interval < time.seconds(5)) {\n throw new Error(`Cron job interval should not be less than 5 second [${alias}]`);\n }\n\n if (timeout > time.days(1)) {\n throw new Error(`Cron job timeout should not be longer than 1 day [${alias}]`);\n }\n\n cronJobs[alias] = {\n alias,\n params: { description, interval, timeout },\n handler,\n state: {\n isRunning: false,\n },\n };\n}\n\nexport async function startCronJobs() {\n if (cronJobsInterval) {\n throw new Error('Cron jobs already started');\n }\n\n const aliasList = Object.keys(cronJobs);\n if (aliasList.length > 0) {\n if (!getMongodbUri()) {\n console.log('MongoDB URI is not configured. Skipping cron jobs.');\n return;\n }\n\n const aliasSelector = { alias: { $in: aliasList } };\n\n const cronJobRecords = await cronJobsCollection.fetch(aliasSelector);\n const now = Date.now();\n cronJobRecords.forEach((record) => {\n const job = cronJobs[record.alias];\n if (!job) {\n return;\n }\n job.state.scheduledRunTs = record.lastStartDate\n ? record.lastStartDate.getTime() + job.params.interval\n : now;\n });\n\n Object.values(cronJobs).forEach((job) => {\n if (!job.state.scheduledRunTs) {\n job.state.scheduledRunTs = now;\n }\n });\n\n cronJobsInterval = setInterval(tickCronJobs, time.seconds(1));\n }\n}\n\nasync function tickCronJobs() {\n const now = Date.now();\n\n const ownsLock = await acquireLock('cron', {\n successfulLockCacheDuration: time.seconds(10),\n failedLockCacheDuration: time.seconds(30),\n });\n\n if (!ownsLock) {\n return;\n }\n\n for (const job of Object.values(cronJobs)) {\n const { alias, params, state } = job;\n if (state.isRunning) {\n if (state.startTs && state.startTs + params.timeout < now) {\n const timeoutError = new Error(`Cron job '${alias}' timed out after ${params.timeout}ms`);\n captureError(timeoutError);\n state.isRunning = false;\n }\n continue;\n }\n\n // TODO: limit the number of jobs running concurrently\n\n if (state.scheduledRunTs && state.scheduledRunTs <= now) {\n void runCronJob(job);\n }\n }\n}\n\nasync function runCronJob(job: CronJob) {\n const { alias, params, handler, state } = job;\n state.isRunning = true;\n state.startTs = Date.now();\n\n const transaction = startTransaction('cron', `cron:${alias}`);\n // TODO: enforce job timeout\n try {\n await cronJobsCollection.upsertOne(\n { alias },\n {\n $set: { lastStartDate: new Date(state.startTs) },\n $setOnInsert: { alias },\n }\n );\n await handler();\n handleCronJobCompletion(state, params);\n transaction.end('success');\n } catch (err) {\n handleCronJobCompletion(state, params);\n const error = err instanceof Error ? err : new Error(String(err));\n captureError(error);\n transaction.end('error');\n console.error(`Error in cron job '${alias}':`, err);\n }\n}\n\nfunction handleCronJobCompletion(state: CronJob['state'], params: CronJob['params']) {\n state.scheduledRunTs = state.startTs ? state.startTs + params.interval : Date.now();\n state.startTs = undefined;\n state.isRunning = false;\n}\n\nexport function getCronJobsMetadata() {\n return Object.values(cronJobs).map(({ alias, params }) => ({\n alias,\n description: params.description,\n interval: params.interval,\n timeout: params.timeout,\n }));\n}\n\nexport default new Module('_system.cron', {\n stores: [cronJobsCollection],\n});\n\n// const runCronJob = () => {\n// const worker = new Worker(filePath, {\n// workerData: {},\n// execArgv: ['--loader', 'tsx'],\n// });\n\n// const timeoutId = setTimeout(() => {\n// worker.terminate();\n// console.error(`Cron job '${alias}' timed out after ${timeout}ms`);\n// }, timeout);\n\n// worker.on('message', (message) => {\n// if (message === 'done') {\n// clearTimeout(timeoutId);\n// worker.terminate();\n// }\n// });\n\n// worker.on('error', (err) => {\n// clearTimeout(timeoutId);\n// console.error(`Error in cron job '${alias}':`, err);\n// });\n\n// worker.on('exit', (code) => {\n// console.error(`Cron job '${alias}' exited with code ${code}`);\n// setTimeout(runCronJob, interval);\n// });\n// };\n","import { Store } from './store';\nimport { ModelSchema } from './types';\n\ntype NormalizedStore = Store<ModelSchema, Record<string, never>>;\n\nexport type ResolvedStores = {\n storesToInit: NormalizedStore[];\n effectiveStores: NormalizedStore[];\n};\n\n/**\n * Resolves module stores into two sets:\n *\n * - `storesToInit`: unique runtime instances that must be initialized.\n * - `effectiveStores`: the latest tail of each extension chain, used for\n * index reconciliation and cloud metadata.\n *\n * Throws if two unrelated chains share the same collection name.\n *\n * @internal\n */\nexport function resolveStores(stores: NormalizedStore[]): ResolvedStores {\n const uniqueStores = [...new Set(stores)];\n\n // Collect the chain tail for each unique root\n const rootToTail = new Map<NormalizedStore, NormalizedStore>();\n for (const store of uniqueStores) {\n const root = store.getChainRoot();\n rootToTail.set(root, root.getChainTail());\n }\n\n const effectiveStores = [...new Set(rootToTail.values())] as NormalizedStore[];\n\n // Detect collisions: different chains with the same collection name\n const nameToRoot = new Map<string, NormalizedStore>();\n for (const [root, tail] of rootToTail) {\n const name = tail.getName();\n const existing = nameToRoot.get(name);\n if (existing !== undefined && existing !== root) {\n throw new Error(\n `Store collision: multiple unrelated stores use collection name '${name}'. ` +\n `Use .extend() to create a single extension chain instead of independent stores.`\n );\n }\n nameToRoot.set(name, root);\n }\n\n return { storesToInit: uniqueStores, effectiveStores };\n}\n","import { Module } from '../app/module';\nimport { locksCollection } from './db';\n\n/**\n * Lock module for distributed locking across multiple instances.\n */\nexport default new Module('_system.lock', {\n stores: [locksCollection],\n});\n","import { Store } from '../data/store';\nimport { schema } from '../data/types';\n\nexport const dbMigrations = new Store('_modelenceMigrations', {\n schema: {\n version: schema.number(),\n status: schema.enum(['completed', 'failed']),\n description: schema.string().optional(),\n output: schema.string().optional(),\n appliedAt: schema.date(),\n },\n indexes: [{ key: { version: 1 }, unique: true }, { key: { version: 1, status: 1 } }],\n});\n","import { acquireLock, releaseLock } from '@/lock';\nimport { Module } from '../app/module';\nimport { dbMigrations } from './db';\nimport { logInfo } from '../telemetry';\n\nexport type MigrationScript = {\n version: number;\n description: string;\n handler: () => Promise<string | void>;\n};\n\ntype MigrationRunOptions = {\n lockMode?: 'acquire' | 'skip';\n};\n\nexport async function runMigrations(\n migrations: MigrationScript[],\n { lockMode = 'acquire' }: MigrationRunOptions = {}\n) {\n if (migrations.length === 0) {\n return;\n }\n\n if (lockMode === 'acquire') {\n const hasLock = await acquireLock('migrations');\n\n if (!hasLock) {\n logInfo('Another instance is running migrations. Skipping migration run.', {\n source: 'migrations',\n });\n return;\n }\n }\n\n try {\n const versions = migrations.map(({ version }) => version);\n\n const existingVersions = await dbMigrations.fetch({\n version: { $in: versions },\n });\n const existingVersionSet = new Set(existingVersions.map(({ version }) => version));\n const pendingMigrations = migrations.filter(({ version }) => !existingVersionSet.has(version));\n\n if (pendingMigrations.length === 0) {\n return;\n }\n\n logInfo(`Running migrations (${pendingMigrations.length})...`, {\n source: 'migrations',\n });\n for (const { version, description, handler } of pendingMigrations) {\n logInfo(`Running migration v${version}: ${description}`, {\n source: 'migrations',\n });\n try {\n const output = await handler();\n const outputStr = (output || '').toString().trim();\n const maxSize = 15 * 1024 * 1024; // 15MB (leaving 1MB buffer for other fields and MongoDB overhead)\n const truncatedOutput =\n outputStr.length > maxSize\n ? outputStr.slice(0, maxSize) + '\\n[Output truncated - exceeded size limit]'\n : outputStr;\n await dbMigrations.upsertOne(\n {\n version,\n },\n {\n $set: {\n version,\n status: 'completed',\n description,\n output: truncatedOutput,\n appliedAt: new Date(),\n },\n }\n );\n logInfo(`Migration v${version} complete`, {\n source: 'migrations',\n });\n } catch (e) {\n if (e instanceof Error) {\n await dbMigrations.upsertOne(\n {\n version,\n },\n {\n $set: {\n version,\n status: 'failed',\n description,\n output: e.message || '',\n appliedAt: new Date(),\n },\n }\n );\n logInfo(`Migration v${version} is failed: ${e.message}`, {\n source: 'migrations',\n });\n }\n }\n }\n } finally {\n if (lockMode === 'acquire') {\n await releaseLock('migrations');\n }\n }\n}\n\nexport function startMigrations(migrations: MigrationScript[]) {\n setTimeout(() => {\n runMigrations(migrations).catch((err) => {\n console.error('Error running migrations:', err);\n });\n }, 0);\n}\n\nexport default new Module('_system.migration', {\n stores: [dbMigrations],\n});\n","import { Module } from '../app/module';\nimport { dbRateLimits } from './db';\n\nexport default new Module('_system.rateLimit', {\n stores: [dbRateLimits],\n});\n","import { Module } from '../app/module';\nimport { callCloudApi } from '../app/backendApi';\nexport type { FileVisibility, GetUploadUrlResult } from './types';\nimport type { FileVisibility, GetUploadUrlResult } from './types';\n\ntype DownloadFileResult = {\n downloadUrl: string;\n};\n\ntype GetFileUrlResult = {\n url: string;\n};\n\nexport async function getUploadUrl({\n filePath,\n contentType,\n visibility,\n}: {\n filePath: string;\n contentType: string;\n visibility: FileVisibility;\n}): Promise<GetUploadUrlResult> {\n return await callCloudApi<GetUploadUrlResult>('/api/files/upload', 'POST', {\n filePath,\n contentType,\n visibility,\n });\n}\n\nexport async function deleteFile(filePath: string): Promise<void> {\n await callCloudApi<void>('/api/files/delete', 'POST', { filePath });\n}\n\nexport async function downloadFile(filePath: string): Promise<DownloadFileResult> {\n return await callCloudApi<DownloadFileResult>('/api/files/download', 'POST', { filePath });\n}\n\nexport async function getFileUrl(filePath: string): Promise<GetFileUrlResult> {\n return await callCloudApi<GetFileUrlResult>('/api/files/url', 'POST', { filePath });\n}\n\n// The file operations above are server-only. They proxy to Modelence Cloud\n// using the app's service token and accept any `filePath`, so they must NOT be\n// callable directly from an untrusted client — exposing them would let any\n// caller mint signed URLs for, download, or delete arbitrary paths. Apps reach\n// these through their own queries/mutations, which enforce authorization. The\n// module therefore registers no client-callable methods.\nexport default new Module('_system.files', {});\n","/**\n * Escape a JSON payload for safe embedding inside a `<script>` tag:\n * `</script>`, HTML chars, and U+2028/U+2029 (illegal in JS source).\n * Single-pass replace avoids order-dependent double-escape pitfalls.\n */\nexport function escapeJsonForScript(json: string): string {\n return json.replace(\n /[<>&\\u2028\\u2029]/g,\n (ch) => `\\\\u${ch.charCodeAt(0).toString(16).padStart(4, '0')}`\n );\n}\n","import {\n createServer,\n defineConfig,\n ViteDevServer,\n loadConfigFromFile,\n UserConfig,\n mergeConfig,\n Plugin,\n PluginOption,\n} from 'vite';\nimport reactPlugin from '@vitejs/plugin-react';\nimport path from 'path';\nimport fs from 'fs';\nimport express from 'express';\nimport type { AppServer, AppServerInitOptions, ExpressMiddleware } from './types';\nimport { escapeJsonForScript } from './ssr/escapeJsonForScript';\n\nconst CLIENT_BUILD_DIR = './.modelence/build/client'.replace(/\\\\/g, '/');\nconst SSR_BUILD_DIR = './.modelence/build/ssr'.replace(/\\\\/g, '/');\n// Resolved relative to Vite's `root` (./src/client).\nconst SSR_ENTRY_VIRTUAL_PATH = '/index.tsx';\n// Match the empty root container regardless of attribute order/quoting/extra\n// attributes (e.g. `<div id='root' class=\"app\">`) and whitespace variants Vite\n// may emit. Capture group 1 is the opening tag so its attributes are preserved\n// when we stream our own content into it.\nconst ROOT_PLACEHOLDER_REGEX = /(<div\\b[^>]*\\bid\\s*=\\s*[\"']root[\"'][^>]*>)\\s*<\\/div>/i;\nconst HEAD_CLOSE_TAG = '</head>';\n\nclass ViteServer implements AppServer {\n private viteServer?: ViteDevServer;\n private config?: UserConfig;\n private ssrEnabled = false;\n private ssrTransportInstalled = false;\n private prodEntryLoaded = false;\n // Manifest and template are immutable after build; cache to avoid sync fs\n // reads per request. Dev mode recomputes because HMR can shift the graph.\n private prodCssAssetsCache?: import('./ssr/collectCss').CssAssets;\n private prodTemplateCache?: string;\n\n enableSsr() {\n this.ssrEnabled = true;\n }\n\n async init({ httpServer }: AppServerInitOptions) {\n this.config = await getConfig(this.isDev() ? httpServer : undefined, { ssr: this.ssrEnabled });\n if (this.isDev()) {\n console.log('Starting Vite dev server...');\n this.viteServer = await createServer(this.config);\n } else if (this.ssrEnabled) {\n // `postBuildCommand` skips the Vite SSR build, so check upfront instead\n // of crashing on the first document request with a \"Cannot find module\".\n const ssrEntryPath = path.resolve(process.cwd(), SSR_BUILD_DIR, 'index.mjs');\n if (!fs.existsSync(ssrEntryPath)) {\n throw new Error(\n `Modelence: SSR is enabled (startApp({ ssr: true })) but the SSR ` +\n `bundle is missing at ${ssrEntryPath}.\\n\\n` +\n `This usually means \\`postBuildCommand\\` is set in modelence.config.ts, ` +\n `which replaces the default Vite client build (and the SSR build along ` +\n `with it). Either:\\n` +\n ` • remove \\`postBuildCommand\\` so Modelence builds the SSR bundle, or\\n` +\n ` • remove \\`ssr: true\\` from startApp() if your custom toolchain ` +\n `handles SSR itself.`\n );\n }\n }\n\n if (this.ssrEnabled && !this.ssrTransportInstalled) {\n const { installSsrCallMethodTransport } = await import('./ssr/transport');\n installSsrCallMethodTransport();\n this.ssrTransportInstalled = true;\n }\n }\n\n middlewares(): ExpressMiddleware[] {\n if (this.isDev()) {\n return (this.viteServer?.middlewares ?? []) as ExpressMiddleware[];\n }\n\n // `index: false` stops express.static from auto-serving `index.html`\n // for `/`, which would shadow the SSR handler. Asset requests still serve.\n const staticOptions = this.ssrEnabled ? { index: false } : undefined;\n const staticFolders = [express.static(CLIENT_BUILD_DIR, staticOptions)];\n if (this.config?.publicDir) {\n staticFolders.push(express.static(this.config.publicDir, staticOptions));\n }\n return staticFolders;\n }\n\n async handler(req: express.Request, res: express.Response) {\n if (this.ssrEnabled && isDocumentRequest(req)) {\n // Skip the SSR pipeline for HEAD — Node strips the body anyway.\n if (req.method === 'HEAD') {\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.setHeader('Cache-Control', 'no-store');\n res.status(200).end();\n return;\n }\n\n try {\n await this.handleSsr(req, res);\n } catch (error) {\n if (this.isDev() && this.viteServer && error instanceof Error) {\n this.viteServer.ssrFixStacktrace(error);\n }\n console.error('SSR render error:', {\n url: req.originalUrl,\n method: req.method,\n userAgent: req.get('user-agent'),\n error,\n });\n // Mid-stream: can't recover; terminate with a truncated page.\n if (res.headersSent) {\n res.end();\n return;\n }\n this.serveStaticShell(res);\n }\n return;\n }\n\n if (this.ssrEnabled) {\n res.status(404).end();\n return;\n }\n\n this.serveStaticShell(res);\n }\n\n private async handleSsr(req: express.Request, res: express.Response) {\n const template = await this.getTemplate(req.originalUrl);\n const snapshot = await this.captureSsrSnapshot();\n if (!snapshot) {\n throw new Error(\n 'Modelence SSR is enabled but no SSR snapshot was captured. ' +\n \"Make sure 'src/client/index.tsx' calls renderApp(...) from 'modelence/client'.\"\n );\n }\n\n // Lazy: keep react-dom/server off the boot path for non-SSR requests.\n const [{ renderSsrTreeStream }, { getCallContext }, cssModule] = await Promise.all([\n import('./ssr/render'),\n import('./app/server'),\n import('./ssr/collectCss'),\n ]);\n\n const callContext = await getCallContext(req, res);\n\n const cssAssets = this.collectCssAssets(cssModule);\n\n // 103 Early Hints so the browser prefetches CSS during render. No-op on\n // older Node / proxies that strip 1xx.\n sendEarlyHints(res, cssModule.buildEarlyHintsLink(cssAssets));\n\n const { sessionState, pipe, getQueryState } = await renderSsrTreeStream({\n callContext,\n loadingElement: snapshot.loadingElement,\n routesElement: snapshot.routesElement,\n router: snapshot.router,\n location: req.originalUrl,\n });\n\n const {\n prelude: rawPrelude,\n rootOpenTag,\n epilogue: rawEpilogue,\n } = splitTemplateAtRoot(template);\n const prelude = injectStylesheets(rawPrelude, cssModule.renderStylesheetLinks(cssAssets));\n\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.setHeader('Cache-Control', 'no-store');\n res.status(200);\n\n // Flush head + session state up front; session is resolved before render.\n res.write(prelude);\n res.write(\n `<script id=\"__MODELENCE_STATE__\" type=\"application/json\">${escapeJsonForScript(\n sessionState\n )}</script>`\n );\n res.write(rootOpenTag);\n\n await pipe(res as unknown as import('node:stream').Writable);\n\n res.write('</div>');\n res.write(\n `<script id=\"__MODELENCE_QUERY_STATE__\" type=\"application/json\">${escapeJsonForScript(\n getQueryState()\n )}</script>`\n );\n res.end(rawEpilogue);\n }\n\n private collectCssAssets(\n cssModule: typeof import('./ssr/collectCss')\n ): import('./ssr/collectCss').CssAssets {\n if (this.isDev()) {\n if (!this.viteServer) {\n return { hrefs: [], source: 'dev' };\n }\n return cssModule.collectDevCssAssets(this.viteServer, SSR_ENTRY_VIRTUAL_PATH);\n }\n\n if (!this.prodCssAssetsCache) {\n this.prodCssAssetsCache = cssModule.loadProdCssAssets(CLIENT_BUILD_DIR);\n }\n return this.prodCssAssetsCache;\n }\n\n private async getTemplate(url: string): Promise<string> {\n if (this.isDev()) {\n const templatePath = path.resolve(process.cwd(), 'src/client/index.html');\n let template = fs.readFileSync(templatePath, 'utf-8');\n if (this.viteServer) {\n template = await this.viteServer.transformIndexHtml(url, template);\n }\n return template;\n }\n\n if (!this.prodTemplateCache) {\n const templatePath = path.resolve(process.cwd(), CLIENT_BUILD_DIR, 'index.html');\n this.prodTemplateCache = fs.readFileSync(templatePath, 'utf-8');\n }\n return this.prodTemplateCache;\n }\n\n private async captureSsrSnapshot(): Promise<\n import('./client/renderApp').RenderAppOptions | null\n > {\n // Evaluating the entry runs `renderApp(...)`, which stores the snapshot\n // on globalThis. Read it immediately after evaluation.\n const { _getSsrSnapshot } = await import('./client/renderApp');\n\n if (this.isDev()) {\n // Re-evaluate per request so HMR edits apply.\n if (!this.viteServer) {\n throw new Error('Vite dev server not initialized');\n }\n await this.viteServer.ssrLoadModule(SSR_ENTRY_VIRTUAL_PATH);\n return _getSsrSnapshot();\n }\n\n if (!this.prodEntryLoaded) {\n await import(path.resolve(process.cwd(), SSR_BUILD_DIR, 'index.mjs'));\n this.prodEntryLoaded = true;\n }\n return _getSsrSnapshot();\n }\n\n private serveStaticShell(res: express.Response) {\n if (this.isDev()) {\n try {\n // Prevent browser from caching the HTML entrypoint in dev mode.\n // Vite's transformMiddleware uses no-cache + ETag for .ts/.tsx modules,\n // which revalidates correctly. But the HTML served by Express's sendFile\n // can be cached by the browser (e.g. bfcache on back/forward navigation).\n // Without HMR WebSocket, stale HTML leads to dynamic import() URLs that\n // reference modules the current Vite instance doesn't recognize.\n res.setHeader('Cache-Control', 'no-store');\n res.sendFile('index.html', { root: './src/client' });\n } catch (e) {\n console.error('Error serving index.html:', e);\n res.status(500).send('Internal Server Error');\n }\n } else {\n res.sendFile('index.html', { root: CLIENT_BUILD_DIR });\n }\n }\n\n private isDev() {\n return process.env.NODE_ENV !== 'production';\n }\n}\n\nfunction isDocumentRequest(req: express.Request): boolean {\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n return false;\n }\n\n // Treat as a document unless the client explicitly asked for something\n // other than HTML. `*/*` and missing Accept (curl, health checks) count\n // as document-ok; path-based filters below catch the genuinely non-doc cases.\n const accept = req.get('accept') ?? '';\n if (accept && !accept.includes('text/html') && !accept.includes('*/*')) {\n return false;\n }\n\n const pathname = (req.path ?? req.url ?? '').split('?')[0];\n\n if (pathname.startsWith('/api/')) {\n return false;\n }\n\n // Non-HTML extensions: favicons, sourcemaps, asset probes.\n const lastSegment = pathname.split('/').pop() ?? '';\n const dotIndex = lastSegment.lastIndexOf('.');\n if (dotIndex > 0) {\n const ext = lastSegment.slice(dotIndex).toLowerCase();\n if (ext !== '.html' && ext !== '.htm') {\n return false;\n }\n }\n\n return true;\n}\n\n/** @internal Exported for testing. */\nexport function splitTemplateAtRoot(template: string): {\n prelude: string;\n rootOpenTag: string;\n epilogue: string;\n} {\n const match = template.match(ROOT_PLACEHOLDER_REGEX);\n if (!match || match.index === undefined) {\n throw new Error('SSR template is missing the expected `<div id=\"root\"></div>` placeholder.');\n }\n const prelude = template.slice(0, match.index);\n const epilogue = template.slice(match.index + match[0].length);\n // Preserve the template's own opening tag (with any attributes) so the\n // streamed root matches what the client hydrates against.\n return { prelude, rootOpenTag: match[1], epilogue };\n}\n\n// Insert CSS <link>s before </head> so styles ship with the first chunk.\nfunction injectStylesheets(prelude: string, linksHtml: string): string {\n if (!linksHtml) {\n return prelude;\n }\n const headCloseIndex = prelude.lastIndexOf(HEAD_CLOSE_TAG);\n if (headCloseIndex === -1) {\n return linksHtml + prelude;\n }\n return prelude.slice(0, headCloseIndex) + linksHtml + prelude.slice(headCloseIndex);\n}\n\ntype EarlyHintsCapable = {\n writeEarlyHints?: (hints: { link?: string | string[] }) => void;\n};\n\n// 103 Early Hints (Node 18.11+); no-op when unsupported or proxies strip 1xx.\nfunction sendEarlyHints(res: express.Response, links: string[]) {\n if (links.length === 0) {\n return;\n }\n const target = res as unknown as EarlyHintsCapable;\n if (typeof target.writeEarlyHints !== 'function') {\n return;\n }\n try {\n target.writeEarlyHints({ link: links });\n } catch (error) {\n // Best-effort: some proxies reject 1xx after headers are touched.\n if (process.env.NODE_ENV !== 'production') {\n console.warn('Modelence SSR: writeEarlyHints failed', error);\n }\n }\n}\n\nasync function loadUserViteConfig() {\n const appDir = process.cwd();\n\n try {\n const result = await loadConfigFromFile(\n { command: 'serve', mode: 'development' },\n undefined,\n appDir\n );\n return result?.config || {};\n } catch (error) {\n console.warn(`Could not load vite config:`, error);\n return {};\n }\n}\n\nfunction safelyMergeConfig(baseConfig: UserConfig, userConfig: UserConfig) {\n const mergedConfig = mergeConfig(baseConfig, userConfig);\n\n // Deduplicate plugins by name, keeping user plugins over framework plugins\n if (mergedConfig.plugins && Array.isArray(mergedConfig.plugins)) {\n const seenPlugins = new Set<string>();\n mergedConfig.plugins = mergedConfig.plugins\n .flat()\n .filter((plugin: PluginOption) => {\n if (!plugin || typeof plugin !== 'object' || Array.isArray(plugin)) {\n return true;\n }\n const pluginName = (plugin as Plugin).name;\n if (!pluginName || seenPlugins.has(pluginName)) {\n return false;\n }\n seenPlugins.add(pluginName);\n return true;\n })\n .reverse(); // Reverse to prioritize user plugins over framework plugins\n mergedConfig.plugins.reverse(); // Reverse back to maintain original order\n }\n\n return mergedConfig;\n}\n\nasync function getConfig(httpServer?: import('http').Server, options: { ssr?: boolean } = {}) {\n const appDir = process.cwd();\n const userConfig = await loadUserViteConfig();\n\n const eslintConfigFile = [\n '.eslintrc.js',\n '.eslintrc.json',\n '.eslintrc',\n 'eslint.config.js',\n '.eslintrc.yml',\n '.eslintrc.yaml',\n ].find((file) => fs.existsSync(path.join(appDir, file)));\n\n const plugins = [reactPlugin(), modelenceAssetPlugin()];\n\n if (eslintConfigFile) {\n const eslintPlugin = (await import('vite-plugin-eslint')).default;\n plugins.push(\n eslintPlugin({\n failOnError: false,\n include: ['src/**/*.js', 'src/**/*.jsx', 'src/**/*.ts', 'src/**/*.tsx'],\n cwd: appDir,\n overrideConfigFile: path.resolve(appDir, eslintConfigFile),\n })\n );\n }\n\n const baseConfig = defineConfig({\n plugins,\n build: {\n outDir: CLIENT_BUILD_DIR,\n emptyOutDir: true,\n },\n server: {\n middlewareMode: true,\n hmr: httpServer ? { server: httpServer } : undefined,\n },\n appType: options.ssr ? 'custom' : 'spa',\n root: './src/client',\n resolve: {\n alias: {\n '@': path.resolve(appDir, 'src').replace(/\\\\/g, '/'),\n },\n },\n });\n\n return safelyMergeConfig(baseConfig, userConfig);\n}\n\nfunction modelenceAssetPlugin(): Plugin {\n return {\n name: 'modelence-asset-handler',\n async transform(code: string, id: string) {\n const assetRegex = /\\.(png|jpe?g|gif|svg|mpwebm|ogg|mp3|wav|flac|aac)$/;\n if (assetRegex.test(id)) {\n if (process.env.NODE_ENV === 'development') {\n return code;\n }\n // TODO: Upload to CDN\n // return `export default \"${cdnUrl}\"`;\n return code;\n }\n },\n };\n}\n\nexport const viteServer = new ViteServer();\n","import type { EmailPayload, EmailProvider } from '../types';\nimport { callCloudApi } from './backendApi';\n\ninterface ManagedEmailSendResponse {\n messageId: string;\n isOverage: boolean;\n acceptedRecipients: string[];\n}\n\ninterface ManagedEmailErrorBody {\n error?: { code?: string; message?: string };\n}\n\n// Studio's /api/email/send only accepts a curated subset of fields. The\n// sender domain is enforced server-side (locked to a Modelence-owned\n// subdomain in v1), so we drop the address half of `from` and forward only\n// the friendly name. cc/bcc/attachments/headers are not yet supported by the\n// relay — surfaced as a clear error before the network call.\nfunction extractFromName(from: string | undefined): string | undefined {\n if (!from) return undefined;\n const match = from.match(/^\\s*\"?([^\"<]+?)\"?\\s*<[^>]+>\\s*$/);\n return match?.[1]?.trim();\n}\n\nfunction normalizeAddressList(value: string | string[] | undefined): string[] | undefined {\n if (!value) return undefined;\n return Array.isArray(value) ? value : [value];\n}\n\nfunction buildRequestBody(payload: EmailPayload) {\n return {\n to: Array.isArray(payload.to) ? payload.to : [payload.to],\n subject: payload.subject,\n html: payload.html,\n text: payload.text,\n fromName: extractFromName(payload.from),\n replyTo: normalizeAddressList(payload.replyTo),\n };\n}\n\nfunction describeManagedEmailError(error: unknown): Error {\n if (!(error instanceof Error)) {\n return new Error('Managed email send failed');\n }\n const body = (error as Error & { responseBody?: ManagedEmailErrorBody }).responseBody;\n const code = body?.error?.code;\n const message = body?.error?.message;\n if (code && message) {\n return new Error(`Managed email rejected (${code}): ${message}`);\n }\n return error;\n}\n\nexport const managedEmailProvider: EmailProvider = {\n async sendEmail(payload: EmailPayload): Promise<void> {\n if (payload.cc || payload.bcc || payload.attachments || payload.headers) {\n throw new Error(\n 'Modelence managed email does not support cc, bcc, attachments, or custom headers in v1. ' +\n 'Configure your own provider (Resend, SES, SMTP) to use these features. ' +\n 'See https://docs.modelence.com/email/managed.'\n );\n }\n\n try {\n await callCloudApi<ManagedEmailSendResponse>(\n '/api/email/send',\n 'POST',\n buildRequestBody(payload)\n );\n } catch (error) {\n throw describeManagedEmailError(error);\n }\n },\n};\n","/**\n * Security configuration for the application\n *\n * By default, the app is protected against clickjacking by setting\n * `Content-Security-Policy: frame-ancestors 'self'` and `X-Frame-Options: SAMEORIGIN`\n * on all responses, preventing the app from being embedded in iframes on other domains.\n *\n * @example\n * ```typescript\n * import { startApp } from 'modelence/server';\n *\n * // Allow embedding in iframes on specific domains\n * startApp({\n * security: {\n * frameAncestors: ['https://modelence.com', 'https://app.example.com'],\n * trustedProxies: ['loopback', 'linklocal', 'uniquelocal'],\n * clientIpHeader: 'cf-connecting-ip',\n * },\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Allow a browser client on another origin to call this app's API (e.g. Expo\n * // Web, which Metro serves on :8081 while the API runs on :3000). Applies to\n * // module routes and framework API routes, not to SSR pages or static assets.\n * startApp({\n * security: {\n * allowedOrigins: ['http://localhost:8081'],\n * },\n * });\n * ```\n */\nexport type SecurityConfig = {\n /**\n * Additional origins allowed to embed this app in an iframe.\n * The app's own origin (`'self'`) is always included automatically.\n *\n * When not set, only same-origin framing is allowed.\n * When set, `X-Frame-Options` is omitted since it cannot express multiple origins.\n */\n frameAncestors?: string[];\n /**\n * Origins allowed to read this app's responses from a browser (CORS).\n *\n * Browsers block a cross-origin `fetch` unless the response carries a\n * matching `Access-Control-Allow-Origin`. The common case is Expo Web, which\n * Metro serves on a different port from the API — a different port is a\n * different origin, so every method call is blocked without this.\n *\n * Scope: this covers your module routes and the framework's own API routes\n * (method calls and the OAuth endpoints). SSR pages and static assets are\n * excluded, so a listed origin can call your API but cannot read your rendered\n * pages with credentials.\n *\n * The scope is derived from the routes actually registered, not matched by\n * path prefix: module routes carry no framework-imposed prefix (the docs'\n * example mounts `/todos` at the root), so a prefix rule would silently drop\n * CORS from the user-defined routes that most need it.\n *\n * Each entry must be an exact origin (`scheme://host[:port]`); patterns and\n * wildcards are not supported, since the response header carries one concrete\n * origin. Entries are normalized (trimmed, lowercased, default port and\n * trailing slash dropped) to match what browsers send, and anything that is\n * not a valid origin throws at startup rather than silently never matching.\n *\n * The matched origin is echoed back rather than `*`, and\n * `Access-Control-Allow-Credentials` is sent, so the browser will expose a\n * credentialed response to JS. Note this only covers same-site requests: the\n * auth cookie is `SameSite=Lax`, so a genuinely cross-site caller\n * (`app.example.com` → `api.other.com`) never has the cookie attached in the\n * first place, regardless of this setting. The Expo Web case works because\n * `localhost:8081` and `localhost:3000` differ only by port, and port is not\n * part of a site.\n *\n * Opt-in by design: when unset, no CORS headers are sent at all. Deployments\n * that already add CORS at a proxy or router therefore stay untouched — a\n * duplicated `Access-Control-Allow-Origin` is invalid and would break them.\n *\n * Native iOS/Android do not enforce CORS and never need this.\n */\n allowedOrigins?: string[];\n\n /**\n * IP addresses or CIDR ranges of reverse proxies that are allowed to supply\n * the client IP through `X-Forwarded-For`. This uses Express's `trust proxy`\n * address syntax, which also supports the named ranges `loopback`,\n * `linklocal`, and `uniquelocal`.\n *\n * For backward compatibility, all proxy addresses are trusted when neither\n * this option nor `MODELENCE_TRUSTED_PROXIES` is set. Configure one of them in\n * production so only addresses that cannot be reached directly by untrusted\n * clients are trusted. Once configured,\n * `connectionInfo.ip` is resolved by walking the proxy chain from the app\n * toward the client and stopping at the first untrusted address. This keeps a\n * caller from choosing its rate-limit identity by prepending a forged\n * `X-Forwarded-For` value.\n *\n * @example\n * ```typescript\n * trustedProxies: ['loopback', '10.0.0.0/8']\n * ```\n */\n trustedProxies?: string | string[];\n\n /**\n * Name of a single-value header the trusted proxy sets to the originating\n * client IP, used instead of walking the `X-Forwarded-For` chain.\n *\n * Cloudflare recommends reading `CF-Connecting-IP` (or `True-Client-IP` on\n * Enterprise plans) rather than `X-Forwarded-For`, because Cloudflare\n * *appends* to an inbound `X-Forwarded-For` instead of overwriting it, while\n * these headers always carry exactly one address.\n *\n * This header is only read when the immediate peer is a trusted proxy, so\n * `trustedProxies` (or `MODELENCE_TRUSTED_PROXIES`) must also be configured\n * with the proxy's addresses. Without that, a direct caller could set the\n * header themselves and choose their own rate-limit identity. When the peer\n * is untrusted or the header is absent, the IP falls back to the normal\n * `trust proxy` resolution.\n *\n * @example\n * ```typescript\n * // Behind Cloudflare, with Cloudflare's published ranges trusted:\n * clientIpHeader: 'cf-connecting-ip'\n * ```\n */\n clientIpHeader?: string;\n};\n\nlet securityConfig: SecurityConfig = Object.freeze({});\n\n/**\n * Canonicalizes a configured origin into the form browsers actually send in the\n * `Origin` header, so the middleware can match it with a plain string compare.\n *\n * Browsers lowercase the scheme and host, omit the default port, and never\n * include a path — so `http://LOCALHOST:8081` and `http://localhost:8081/` must\n * both collapse to `http://localhost:8081` or they would silently never match.\n *\n * Throws rather than skipping a bad entry: a typo here surfaces to the developer\n * as an opaque CORS failure in the browser with nothing logged server-side, so\n * failing at startup is the only place it is cheap to diagnose.\n */\nfunction normalizeOrigin(entry: string): string {\n if (typeof entry !== 'string' || entry.trim() === '') {\n throw new Error(\n `Invalid security.allowedOrigins entry: expected a non-empty string, received ${JSON.stringify(entry)}.`\n );\n }\n\n const trimmed = entry.trim();\n\n // Called out separately from the generic parse failure below: '*' is a\n // plausible guess that would parse as neither an origin nor a URL, and the\n // reason it is unsupported (credentialed requests) is worth stating.\n if (trimmed === '*') {\n throw new Error(\n \"Invalid security.allowedOrigins entry '*': wildcards are not supported, since the \" +\n 'response echoes one concrete origin to keep credentialed requests working. ' +\n 'List each allowed origin explicitly.'\n );\n }\n\n let url: URL;\n try {\n url = new URL(trimmed);\n } catch {\n throw new Error(\n `Invalid security.allowedOrigins entry ${JSON.stringify(entry)}: expected an origin of the ` +\n \"form 'scheme://host[:port]' (for example 'http://localhost:8081').\"\n );\n }\n\n // URL keeps userinfo and a query/fragment out of `origin`, so they would be\n // silently dropped rather than rejected. Reject them: they signal the author\n // meant something the allowlist cannot express.\n if (url.username || url.password) {\n throw new Error(\n `Invalid security.allowedOrigins entry ${JSON.stringify(entry)}: an origin must not carry credentials.`\n );\n }\n if (url.search || url.hash) {\n throw new Error(\n `Invalid security.allowedOrigins entry ${JSON.stringify(entry)}: an origin must not carry a query or fragment.`\n );\n }\n // Checked before the path rule below: for a non-special scheme the whole\n // opaque body lands in `pathname`, so the path rule would fire first and\n // suggest the useless 'null' origin. `localhost:8081` (no scheme) parses this\n // way too, which is exactly the typo worth naming clearly.\n //\n // Non-special schemes (and opaque ones like `data:`) serialize as 'null',\n // which would otherwise match the opaque origin sandboxed iframes send.\n if (url.origin === 'null') {\n throw new Error(\n `Invalid security.allowedOrigins entry ${JSON.stringify(entry)}: expected an origin of the ` +\n \"form 'scheme://host[:port]' (for example 'http://localhost:8081'). \" +\n `Scheme ${JSON.stringify(url.protocol.replace(':', ''))} does not form a comparable origin.`\n );\n }\n // `*` is a legal URL hostname character, so `https://*.example.com` parses and\n // would be stored as-is. Browsers never send a literal `*` in `Origin`, so the\n // entry could only ever fail to match — the same silent CORS failure the bare\n // '*' check above exists to prevent.\n if (url.hostname.includes('*')) {\n throw new Error(\n `Invalid security.allowedOrigins entry ${JSON.stringify(entry)}: wildcards are not supported, ` +\n 'since the response echoes one concrete origin to keep credentialed requests working. ' +\n 'List each allowed origin explicitly.'\n );\n }\n // A bare origin parses with pathname '/' — anything longer is a real path.\n if (url.pathname !== '/') {\n throw new Error(\n `Invalid security.allowedOrigins entry ${JSON.stringify(entry)}: an origin must not include a path. ` +\n `Use ${JSON.stringify(url.origin)} instead.`\n );\n }\n\n // `URL.origin` is exactly the serialization browsers send: lowercased, default\n // port dropped, no trailing slash.\n return url.origin;\n}\n\nfunction normalizeAllowedOrigins(allowedOrigins: string[]): string[] {\n if (!Array.isArray(allowedOrigins)) {\n throw new Error(\n `Invalid security.allowedOrigins: expected an array of origin strings, received ${JSON.stringify(allowedOrigins)}.`\n );\n }\n return allowedOrigins.map(normalizeOrigin);\n}\n\nexport function setSecurityConfig(newSecurityConfig: SecurityConfig) {\n const validated = newSecurityConfig.allowedOrigins\n ? {\n ...newSecurityConfig,\n allowedOrigins: normalizeAllowedOrigins(newSecurityConfig.allowedOrigins),\n }\n : newSecurityConfig;\n securityConfig = Object.freeze(Object.assign({}, securityConfig, validated));\n}\n\nexport function getSecurityConfig() {\n return securityConfig;\n}\n","import { WebsocketServerProvider } from '@/websocket/types';\n\nexport type WebsocketConfig = {\n provider?: WebsocketServerProvider;\n};\n\nlet websocketConfig: WebsocketConfig = Object.freeze({});\n\nexport function setWebsocketConfig(newWebsocketConfig: WebsocketConfig) {\n websocketConfig = Object.freeze(Object.assign({}, websocketConfig, newWebsocketConfig));\n}\n\nexport function getWebsocketConfig() {\n return websocketConfig;\n}\n","import dotenv from 'dotenv';\nimport fs from 'fs/promises';\nimport os from 'os';\nimport path from 'path';\n\nimport type { AppServer, ModelSchema } from '../types';\nimport socketioServer from '@/websocket/socketio/server';\nimport { initRoles } from '../auth/role';\nimport sessionModule from '../auth/session';\nimport { RoleDefinition } from '../auth/types';\nimport userModule from '../auth/user';\nimport { getLocalConfigs } from '../config/local';\nimport { loadConfigs, setSchema } from '../config/server';\nimport { startConfigSync, loadRemoteConfigs } from '../config/sync';\nimport { ConfigSchema } from '../config/types';\nimport cronModule, { defineCronJob, getCronJobsMetadata, startCronJobs } from '../cron/jobs';\nimport { type IndexReconcileMode, Store } from '../data/store';\nimport { resolveStores } from '../data/resolveStores';\nimport { connect, getClient, getMongodbUri } from '../db/client';\nimport { _createSystemMutation, _createSystemQuery, createMutation, createQuery } from '../methods';\nimport {\n MigrationScript,\n default as migrationModule,\n runMigrations,\n startMigrations,\n} from '../migration';\nimport rateLimitModule from '../rate-limit';\nimport { initRateLimits } from '../rate-limit/rules';\nimport systemModule from '../system';\nimport lockModule, { acquireLock, releaseLock } from '../lock';\nimport filesModule from '../files';\nimport { viteServer } from '../viteServer';\nimport { connectCloudBackend } from './backendApi';\nimport { initMetrics } from './metrics';\nimport { Module } from './module';\nimport { startServer } from './server';\nimport { markAppStarted, setMetadata } from './state';\nimport { time } from '@/time';\nimport { EmailConfig, setEmailConfig } from './emailConfig';\nimport { managedEmailProvider } from './managedEmailProvider';\nimport { AuthConfig, setAuthConfig } from './authConfig';\nimport { SecurityConfig, setSecurityConfig } from './securityConfig';\nimport { WebsocketConfig, setWebsocketConfig } from './websocketConfig';\nimport { buildAuthRateLimits } from '../auth/user';\n\nexport type AppOptions = {\n modules?: Module[];\n server?: AppServer;\n email?: EmailConfig;\n auth?: AuthConfig;\n /** Security settings such as clickjacking protection. See {@link SecurityConfig}. */\n security?: SecurityConfig;\n /**\n * Custom role definitions keyed by role name. Defined roles are synced to the\n * Modelence Cloud dashboard for user management. See {@link RoleDefinition}.\n *\n * @example\n * ```typescript\n * startApp({\n * roles: {\n * admin: { description: 'Full access to all features' },\n * editor: { description: 'Can edit content' },\n * viewer: {},\n * },\n * });\n * ```\n */\n roles?: Record<string, RoleDefinition>;\n /** @internal */\n defaultRoles?: Record<string, string>;\n migrations?: Array<MigrationScript>;\n websocket?: WebsocketConfig;\n /** Enable server-side rendering of the user's React tree. */\n ssr?: boolean;\n};\n\nexport async function startApp({\n modules = [],\n roles = {},\n defaultRoles = {},\n server = viteServer,\n migrations = [],\n email = {},\n auth = {},\n security = {},\n websocket = {},\n ssr = false,\n}: AppOptions) {\n if (ssr && server === viteServer) {\n viteServer.enableSsr();\n }\n dotenv.config();\n\n dotenv.config({ path: '.modelence.env' });\n\n const hasRemoteBackend = Boolean(process.env.MODELENCE_SERVICE_ENDPOINT);\n\n trackAppStart()\n .then(() => {\n // Do nothing\n })\n .catch(() => {\n // Silently ignore tracking errors to not disrupt app startup\n });\n\n for (const module of modules) {\n if (module.name.toLowerCase().startsWith('_system.')) {\n throw new Error(\n `Invalid module name: '${module.name}'\\n\\n` +\n `The '_system.' prefix is reserved for internal use and cannot be used in user-defined modules.\\n\\n` +\n `Rename your module to something that does not start with '_system.'`\n );\n }\n }\n const systemModules = [\n userModule,\n sessionModule,\n cronModule,\n migrationModule,\n rateLimitModule,\n systemModule,\n lockModule,\n filesModule,\n ];\n const combinedModules = [...systemModules, ...modules];\n\n markAppStarted();\n\n initSystemMethods(systemModules);\n initCustomMethods(modules);\n\n initRoles(roles, defaultRoles);\n\n const configSchema = getConfigSchema(combinedModules);\n setSchema(configSchema);\n const rawStores = getStores(combinedModules) as Store<ModelSchema, never>[];\n const channels = getChannels(combinedModules);\n\n defineCronJobs(combinedModules);\n\n // Apply user-provided auth rate limit overrides directly onto the user\n // module so its `rateLimits` array reflects the effective configuration\n // (the Modelence Cloud reads these from modules for display).\n userModule.rateLimits = buildAuthRateLimits(auth.rateLimits);\n\n const rateLimits = getRateLimits(combinedModules);\n initRateLimits(rateLimits);\n\n const { storesToInit, effectiveStores } = resolveStores(rawStores) as {\n storesToInit: Store<ModelSchema, never>[];\n effectiveStores: Store<ModelSchema, never>[];\n };\n\n if (hasRemoteBackend) {\n const { configs, environmentId, appAlias, environmentAlias, telemetry } =\n await connectCloudBackend({\n configSchema,\n cronJobsMetadata: getCronJobsMetadata(),\n stores: effectiveStores,\n roles,\n });\n loadRemoteConfigs(configs);\n setMetadata({ environmentId, appAlias, environmentAlias, telemetry });\n } else {\n loadConfigs(getLocalConfigs(configSchema));\n }\n\n // Default to Modelence's managed email provider when the app is connected\n // to Modelence Cloud and no provider was configured. This relays through\n // Studio's /api/email/send (which sends via SES on a Modelence-owned\n // domain). Locally — without a service endpoint — keep the historical\n // \"no provider configured\" behavior so misconfigurations still throw loudly.\n if (hasRemoteBackend && !email.provider) {\n setEmailConfig({ ...email, provider: managedEmailProvider });\n } else {\n setEmailConfig(email);\n }\n setAuthConfig(auth);\n setSecurityConfig(security);\n setWebsocketConfig({\n ...websocket,\n provider: websocket.provider || socketioServer,\n });\n\n const mongodbUri = getMongodbUri();\n if (mongodbUri) {\n await connect();\n const allStoresToInit = [...new Set([...storesToInit, ...effectiveStores])];\n initStores(allStoresToInit);\n await createIndexesAndMigrationsWithLock(effectiveStores, migrations);\n } else {\n startMigrations(migrations);\n }\n\n if (hasRemoteBackend) {\n await initMetrics();\n startConfigSync();\n }\n\n startCronJobs().catch(console.error);\n\n await startServer(server, { combinedModules, channels });\n}\n\nfunction initCustomMethods(modules: Module[]) {\n for (const module of modules) {\n for (const [key, handler] of Object.entries(module.queries)) {\n createQuery(`${module.name}.${key}`, handler);\n }\n for (const [key, handler] of Object.entries(module.mutations)) {\n createMutation(`${module.name}.${key}`, handler);\n }\n }\n}\n\nfunction initSystemMethods(modules: Module[]) {\n for (const module of modules) {\n for (const [key, handler] of Object.entries(module.queries)) {\n _createSystemQuery(`${module.name}.${key}`, handler);\n }\n for (const [key, handler] of Object.entries(module.mutations)) {\n _createSystemMutation(`${module.name}.${key}`, handler);\n }\n }\n}\n\nfunction getStores(modules: Module[]) {\n return modules.flatMap((module) => module.stores);\n}\n\nfunction getChannels(modules: Module[]) {\n return modules.flatMap((module) => module.channels);\n}\n\nfunction getRateLimits(modules: Module[]) {\n return modules.flatMap((module) => module.rateLimits);\n}\n\nfunction warnIndexCreationFailure(storeName: string, error: unknown) {\n console.warn(`Failed to create indexes for store '${storeName}'. Continuing startup.`, error);\n}\n\nconst MIGRATIONS_LOCK_RESOURCE = 'migrations';\n\nasync function createIndexesAndMigrationsWithLock(\n effectiveStores: Store<ModelSchema, never>[],\n migrations: MigrationScript[]\n) {\n const hasLock = await acquireLock(MIGRATIONS_LOCK_RESOURCE, {\n lockDuration: time.seconds(30),\n heartbeat: true,\n });\n if (!hasLock) {\n return;\n }\n\n let blockingStores: Store<ModelSchema, never>[];\n let backgroundStores: Store<ModelSchema, never>[];\n\n try {\n blockingStores = effectiveStores.filter((store) => store.getIndexCreationMode() === 'blocking');\n backgroundStores = effectiveStores.filter(\n (store) => store.getIndexCreationMode() === 'background'\n );\n\n for (const store of blockingStores) {\n await createStoreIndexes(store, 'full');\n }\n for (const store of backgroundStores) {\n await createStoreIndexes(store, 'drop-only');\n }\n } catch (error) {\n await releaseLock(MIGRATIONS_LOCK_RESOURCE);\n throw error;\n }\n\n const backgroundIndexCreationPromise = (async () => {\n for (const store of backgroundStores) {\n await createStoreIndexes(store, 'create-only');\n }\n })();\n const migrationPromise = runMigrations(migrations, { lockMode: 'skip' });\n\n void Promise.allSettled([backgroundIndexCreationPromise, migrationPromise])\n .then(([backgroundIndexesResult, migrationResult]) => {\n if (backgroundIndexesResult.status === 'rejected') {\n console.error('Error creating background indexes:', backgroundIndexesResult.reason);\n }\n\n if (migrationResult.status === 'rejected') {\n console.error('Error running migrations:', migrationResult.reason);\n }\n })\n .finally(async () => {\n await releaseLock(MIGRATIONS_LOCK_RESOURCE);\n });\n}\n\nasync function createStoreIndexes(\n store: Store<ModelSchema, never>,\n reconcileMode: IndexReconcileMode = 'full'\n) {\n const storeName = store.getName();\n\n try {\n await store.createIndexes(reconcileMode);\n } catch (error) {\n warnIndexCreationFailure(storeName, error);\n }\n}\n\nfunction getConfigSchema(modules: Module[]): ConfigSchema {\n const merged: ConfigSchema = {};\n\n for (const module of modules) {\n for (const [key, value] of Object.entries(module.configSchema)) {\n const absoluteKey = `${module.name}.${key}`;\n if (absoluteKey in merged) {\n throw new Error(`Duplicate config schema key: ${absoluteKey} (${module.name})`);\n }\n\n merged[absoluteKey] = value;\n }\n }\n\n return merged;\n}\n\nfunction defineCronJobs(modules: Module[]) {\n for (const module of modules) {\n for (const [cronAlias, cronJobParams] of Object.entries(module.cronJobs)) {\n defineCronJob(`${module.name}.${cronAlias}`, cronJobParams);\n }\n }\n}\n\nfunction initStores(stores: Store<ModelSchema, never>[]) {\n const client = getClient();\n if (!client) {\n throw new Error('Failed to initialize stores: MongoDB client not initialized');\n }\n\n for (const store of stores) {\n store.init(client);\n }\n}\n\nasync function trackAppStart() {\n const isTrackingEnabled = process.env.MODELENCE_TRACKING_ENABLED !== 'false';\n\n if (isTrackingEnabled) {\n const serviceEndpoint = process.env.MODELENCE_SERVICE_ENDPOINT ?? 'https://cloud.modelence.com';\n const environmentId = process.env.MODELENCE_ENVIRONMENT_ID;\n\n const appDetails = await getAppDetails();\n const modelencePackageJson = await import('../../package.json');\n\n await fetch(`${serviceEndpoint}/api/track/app-start`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n projectName: appDetails.name,\n version: modelencePackageJson.default.version,\n localHostname: os.hostname(),\n environmentId,\n }),\n });\n }\n}\n\nasync function getAppDetails() {\n try {\n const packageJsonPath = path.join(process.cwd(), 'package.json');\n const packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8');\n const packageJson = JSON.parse(packageJsonContent);\n\n return {\n name: packageJson.name || 'unknown',\n };\n } catch {\n return {\n name: 'unknown',\n };\n }\n}\n","import { ObjectId } from 'mongodb';\nimport { randomUUID } from 'crypto';\nimport {\n usersCollection,\n emailVerificationTokensCollection,\n resetPasswordTokensCollection,\n magicLinkTokensCollection,\n} from './db';\n\nexport async function clearTokens(userId: ObjectId) {\n await emailVerificationTokensCollection.deleteMany({\n userId,\n });\n\n await resetPasswordTokensCollection.deleteMany({\n userId,\n });\n\n // Magic link tokens and one-time codes are keyed by email, not userId, so they\n // must be revoked by the user's addresses. Without this, disabling a user leaves\n // any link issued before the disable able to authenticate once re-enabled, and\n // (with allowSignup) a deleted user's old link could create a fresh account for\n // the same email. Read the user before the caller wipes `emails`. Magic link\n // tokens are stored under the `validateEmail`-lowercased address while stored\n // addresses may keep original casing (e.g. OAuth accounts), so match lowercased.\n const userDoc = await usersCollection.findOne({ _id: userId });\n const userEmails = Array.from(\n new Set(\n (userDoc?.emails ?? [])\n .map((e) => e.address?.toLowerCase())\n .filter((address): address is string => Boolean(address))\n )\n );\n if (userEmails.length > 0) {\n await magicLinkTokensCollection.deleteMany({ email: { $in: userEmails } });\n }\n}\n\nexport async function disableUser(userId: ObjectId) {\n await clearTokens(userId);\n\n await usersCollection.updateOne(userId, {\n $set: {\n status: 'disabled',\n disabledAt: new Date(),\n },\n });\n}\n\nexport async function deleteUser(userId: ObjectId) {\n await clearTokens(userId);\n\n await usersCollection.updateOne(\n {\n _id: userId,\n },\n {\n $set: {\n handle: `deleted-${userId}-${randomUUID()}`,\n status: 'deleted',\n deletedAt: new Date(),\n authMethods: {},\n emails: [],\n },\n }\n );\n}\n","import { Session, User } from '@/auth/types';\nimport { getWebsocketConfig } from '@/app/websocketConfig';\nimport { logError } from '../telemetry';\n\ntype canAccessChannel = (props: {\n user: User | null;\n session: Session | null;\n roles: string[];\n}) => Promise<boolean>;\n\nexport class ServerChannel<T = unknown> {\n public readonly category: string;\n public readonly canAccessChannel: canAccessChannel | null;\n\n constructor(category: string, canAccessChannel?: canAccessChannel) {\n this.category = category;\n this.canAccessChannel = canAccessChannel || null;\n }\n\n broadcast(id: string, data: T) {\n const websocketProvider = getWebsocketConfig().provider;\n if (!websocketProvider) {\n logError('Websockets provider should be added to startApp', {});\n return;\n }\n\n websocketProvider.broadcast({\n category: this.category,\n id,\n data,\n });\n }\n}\n","import { getEmailConfig } from '@/app/emailConfig';\nimport { EmailPayload } from '../types';\n\nexport function sendEmail(payload: EmailPayload) {\n if (!getEmailConfig().provider) {\n throw new Error(\n 'Email provider is not configured, see https://docs.modelence.com/email for more details.'\n );\n }\n return getEmailConfig().provider?.sendEmail(payload);\n}\n","import { getAuthConfig } from '@/app/authConfig';\nimport { getConfig } from '@/config/server';\n\n/**\n * Schemes that must never be used as a redirect target, whatever the allowlist\n * says. These execute or read local content rather than navigating, so allowing\n * one would turn the OAuth callback into an XSS or file-disclosure vector.\n */\nconst FORBIDDEN_SCHEMES = new Set(['javascript:', 'data:', 'file:', 'vbscript:', 'blob:']);\n\nfunction parseRedirectUrl(url: string): URL | null {\n try {\n return new URL(url);\n } catch {\n return null;\n }\n}\n\n/**\n * Deep links the mobile OAuth flow may redirect back to, from both sources:\n * the `auth.mobile.redirectUrls` config value (Studio- and env-manageable) and\n * the typed `AuthConfig.mobile.redirectUrls` array.\n *\n * The config value is declared `type: 'text'`, so the Cloud dashboard renders a\n * textarea and one-URL-per-line is as natural to write as a comma-separated\n * list. Both separators are accepted: splitting on commas alone would read a\n * newline-delimited value as a single unparseable entry, silently emptying the\n * allowlist and rejecting every mobile sign-in.\n *\n * There is no default. An empty result disables mobile OAuth entirely rather\n * than falling back to something permissive.\n */\nexport function getAllowedMobileRedirectUrls(): string[] {\n const fromConfig = String(getConfig('_system.user.auth.mobile.redirectUrls') ?? '')\n .split(/[\\n,]/)\n .map((url) => url.trim())\n .filter(Boolean);\n\n const fromAuthConfig = (getAuthConfig().mobile?.redirectUrls ?? [])\n .map((url) => String(url).trim())\n .filter(Boolean);\n\n return [...new Set([...fromConfig, ...fromAuthConfig])];\n}\n\n/**\n * Whether `url` is an allowed deep-link target for the mobile OAuth flow.\n *\n * Matching compares parsed scheme, host (including port) and path — never raw\n * string prefixes, which would let `myapp://authorize.evil` pass against an\n * allowlisted `myapp://auth`. A candidate may add query parameters or a\n * fragment (the callback itself appends `?code=`), but may not change where it\n * points.\n *\n * Host and path are compared as separate fields, so the two ways of writing a\n * custom scheme are distinct targets rather than synonyms: `myapp://auth`\n * parses as host `auth` with an empty path, while `myapp:auth` parses as an\n * empty host with path `auth`. Nothing is normalized between the two forms —\n * write the allowlist entry exactly as the app registers its scheme, which for\n * React Native and Expo is the `myapp://auth` form used throughout the docs.\n */\nexport function isAllowedMobileRedirectUrl(url: string): boolean {\n if (!url || typeof url !== 'string') return false;\n\n const candidate = parseRedirectUrl(url);\n if (!candidate) return false;\n if (FORBIDDEN_SCHEMES.has(candidate.protocol.toLowerCase())) return false;\n if (hasUserinfo(candidate)) return false;\n\n return getAllowedMobileRedirectUrls().some((allowed) => {\n const entry = parseRedirectUrl(allowed);\n if (!entry) return false;\n if (FORBIDDEN_SCHEMES.has(entry.protocol.toLowerCase())) return false;\n if (hasUserinfo(entry)) return false;\n\n return (\n candidate.protocol.toLowerCase() === entry.protocol.toLowerCase() &&\n // `host` carries the port, so a differing port fails here too.\n candidate.host.toLowerCase() === entry.host.toLowerCase() &&\n normalizePath(candidate.pathname) === normalizePath(entry.pathname)\n );\n });\n}\n\n/**\n * Whether a URL carries a `user:password@` prefix.\n *\n * Userinfo is not part of the host comparison above — `myapp://evil@auth` and\n * `myapp://auth` share the host `auth` — but it survives into the redirect that\n * `buildMobileRedirect` emits. Rejecting it keeps the allowlisted target and\n * the emitted URL identical.\n */\nfunction hasUserinfo(url: URL): boolean {\n return url.username !== '' || url.password !== '';\n}\n\nfunction normalizePath(pathname: string): string {\n const trimmed = pathname.replace(/\\/+$/, '');\n return trimmed === '' ? '/' : trimmed;\n}\n\n/**\n * Query parameters this module owns on a deep link.\n *\n * The allowlist matches on scheme, host and path only, so a configured target\n * is free to carry query parameters of its own — including, accidentally or\n * deliberately, one of these. Any that the caller does not set are removed, so\n * the app reads the outcome of *this* flow rather than a value baked into the\n * allowlisted URL. Without that, `redirectUri=myapp://auth?error=nope` would\n * make a successful sign-in look failed.\n */\nconst OUTCOME_PARAMS = ['code', 'error', 'errorCode', 'linked'] as const;\n\n/**\n * Appends query parameters to an already-allowlisted deep link.\n *\n * Callers must have validated `redirectUri` with {@link isAllowedMobileRedirectUrl}\n * first; this only builds the final URL.\n */\nexport function buildMobileRedirect(redirectUri: string, params: Record<string, string>): string {\n const url = new URL(redirectUri);\n\n for (const key of OUTCOME_PARAMS) {\n url.searchParams.delete(key);\n }\n\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, value);\n }\n return url.toString();\n}\n","/**\n * Query params a failed web OAuth flow reports through. Stripped from the\n * configured target before the fresh values are set, so a stale\n * `?error=` left in `oauthErrorRedirectUrl` can never shadow the real one.\n * Mirrors the mobile deep-link contract (see `mobileRedirect.ts`).\n */\nconst ERROR_PARAMS = ['error', 'errorCode'] as const;\n\nexport type OAuthErrorRedirectParams = {\n /** Human-readable message for display. Not a stable contract. */\n error: string;\n /** Stable identifier the app can branch on. */\n errorCode: string;\n};\n\n/**\n * Builds the absolute URL a failed web OAuth flow redirects to.\n *\n * `target` is `authConfig.oauthErrorRedirectUrl`: a server-side, trusted value,\n * so it is not allowlisted the way a mobile `redirectUri` is. A path such as\n * `/login` is resolved against `siteUrl` (`_system.site.url`), the same base\n * the email landing routes use; an absolute URL is taken as is. Existing\n * unrelated query params and any hash fragment are preserved.\n */\nexport function buildOAuthErrorRedirect(\n siteUrl: string,\n target: string,\n params: OAuthErrorRedirectParams\n): string {\n const url = new URL(target, siteUrl);\n\n for (const key of ERROR_PARAMS) {\n url.searchParams.delete(key);\n }\n\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, value);\n }\n\n return url.toString();\n}\n","import { randomBytes } from 'crypto';\nimport { type Request, type Response } from 'express';\nimport { MongoServerError, ObjectId } from 'mongodb';\nimport { usersCollection } from '@/auth/db';\nimport {\n createSession,\n setAuthTokenCookie,\n consumeLinkNonce,\n issueOAuthExchangeCode,\n} from '@/auth/session';\nimport { getAuthConfig } from '@/app/authConfig';\nimport { getCallContext } from '@/app/server';\nimport { getConfig } from '@/config/server';\nimport { getLocalSiteUrl } from '@/config/local';\nimport { time } from '@/time';\nimport { resolveUniqueHandle } from '../utils';\nimport { User, Session, UserEmail, OAuthProvider } from '@/auth/types';\nimport { ConnectionInfo } from '@/methods/types';\nimport {\n isAllowedMobileRedirectUrl,\n buildMobileRedirect,\n getAllowedMobileRedirectUrls,\n} from './mobileRedirect';\nimport { buildOAuthErrorRedirect } from './oauthErrorRedirect';\n\n/** Which kind of client started an OAuth flow. */\nexport type OAuthPlatform = 'web' | 'mobile';\n\n/**\n * How a completed OAuth flow is handed back to the client.\n *\n * Web sets the session cookie and redirects to the site root, as it always has.\n * Mobile has no shared cookie jar, so it redirects to the app's deep link with a\n * single-use exchange code the app redeems for a session (see\n * `issueOAuthExchangeCode`).\n */\nexport type OAuthOutcome =\n | { platform: 'web' }\n | {\n platform: 'mobile';\n redirectUri: string;\n /**\n * PKCE-style challenge from the device that started the flow.\n *\n * Present for a sign-in, where it binds the exchange code to that device.\n * Absent for linking, which redirects with `linked=<provider>` and mints\n * no credential — so there is nothing to bind. `authenticateUser` is the\n * only caller that mints a code, and it requires\n * {@link BoundMobileOutcome}, so an unbound value cannot reach it.\n */\n codeChallenge?: string;\n };\n\n/**\n * A mobile outcome that is allowed to mint an exchange code.\n *\n * Narrower than {@link OAuthOutcome}: `codeChallenge` is required, so the\n * compiler — not a runtime check — rejects any attempt to mint a code for a\n * flow that carries no device binding.\n */\nexport type BoundMobileOutcome = {\n platform: 'mobile';\n redirectUri: string;\n codeChallenge: string;\n};\n\n/**\n * Where an *error* should be delivered.\n *\n * A failure carries no credential, so it needs a destination but no binding.\n *\n * `api` is for failures raised at the initiation endpoint, which a client calls\n * directly and can read a JSON body from. `web` is for the provider callback,\n * where the browser has landed on a URL outside any client bundle and a JSON\n * body would strand it.\n */\nexport type OAuthErrorTarget =\n | { platform: 'web' }\n | { platform: 'api' }\n | { platform: 'mobile'; redirectUri: string };\n\n/**\n * Resolves the outcome for a validated OAuth state.\n *\n * A mobile state stays mobile whether or not it carries a challenge: dropping to\n * web here would strand a native client in the device browser at `/` instead of\n * deep-linking back. Whether a code may be minted is decided at the point of\n * minting, by {@link isBoundMobileOutcome}.\n */\nexport function toOAuthOutcome(\n state: Pick<OAuthStateResult, 'platform' | 'redirectUri' | 'codeChallenge'>\n): OAuthOutcome {\n if (state.platform === 'mobile' && state.redirectUri) {\n return {\n platform: 'mobile',\n redirectUri: state.redirectUri,\n ...(state.codeChallenge ? { codeChallenge: state.codeChallenge } : {}),\n };\n }\n return { platform: 'web' };\n}\n\n/** Whether a mobile outcome carries the binding required to mint a code. */\nexport function isBoundMobileOutcome(outcome: OAuthOutcome): outcome is BoundMobileOutcome {\n return outcome.platform === 'mobile' && typeof outcome.codeChallenge === 'string';\n}\n\n/**\n * Whether the callback should fire the *login* hooks itself.\n *\n * On mobile it must not: this runs in the device browser against a throwaway\n * guest session, and the sign-in is only real once the app redeems the code.\n * `handleLoginWithOAuth` fires them there with the user's real session. Firing\n * in both places delivered every login hook twice, the first time with the\n * wrong session.\n *\n * Signup hooks are deliberately not gated: account creation genuinely happens\n * here, fires once, and redemption cannot tell a new account from a returning\n * one.\n */\nfunction shouldFireLoginHooks(outcome: OAuthOutcome): boolean {\n return outcome.platform !== 'mobile';\n}\n\nexport interface OAuthUserData {\n id: string;\n email: string;\n emailVerified: boolean;\n providerName: OAuthProvider;\n firstName?: string;\n lastName?: string;\n avatarUrl?: string;\n}\n/** Consumes a single-use link nonce; returns the bound userId string or null. */\nexport async function resolveUserIdFromLinkNonce(\n nonce: string | undefined\n): Promise<string | null> {\n if (!nonce || typeof nonce !== 'string') return null;\n return consumeLinkNonce(nonce);\n}\n\n/**\n * Machine-readable failure reasons handed to a native app alongside the\n * human-readable message, so the app can branch on the outcome without parsing\n * English prose.\n */\nexport type OAuthErrorCode =\n | 'missing_code'\n | 'invalid_state'\n | 'invalid_redirect'\n | 'invalid_link_nonce'\n | 'account_inactive'\n | 'email_exists'\n | 'email_required'\n | 'link_failed'\n | 'not_signed_in'\n | 'oauth_failed';\n\n/*\n * Sends OAuth error response.\n * If `oauthErrorRedirectUrl` is configured, redirects there with the failure\n * in the query string. Otherwise, if `errorComponent` is configured, renders\n * HTML. Otherwise falls back to JSON.\n *\n * On a mobile flow, an HTML or JSON body would strand the user in the device\n * browser with no route back into the app, so the error is delivered as a\n * redirect to the app's deep link instead. Errors raised before the state is\n * decoded have no known platform and keep the default behaviour.\n *\n * `oauthErrorRedirectUrl` covers the `web` platform only: it exists for the\n * provider callback, where the browser has landed outside any client bundle.\n * An initiation-time failure (`api`) is answered by a client that called the\n * endpoint directly and can read the JSON body, so it is never redirected.\n */\nexport function sendOAuthError(\n res: Response,\n statusCode: number,\n errorMessage: string,\n // Only a destination is needed to report a failure; an OAuthOutcome is\n // accepted too, since it is a strict subtype.\n outcome: OAuthErrorTarget = { platform: 'web' },\n errorCode: OAuthErrorCode = 'oauth_failed'\n) {\n if (outcome.platform === 'mobile') {\n // The human-readable message is for display; `errorCode` is what an app\n // should branch on, since the message text is not a stable contract.\n res.set('Referrer-Policy', 'no-referrer');\n return res.redirect(\n buildMobileRedirect(outcome.redirectUri, { error: errorMessage, errorCode })\n );\n }\n\n const authConfig = getAuthConfig();\n\n if (authConfig.oauthErrorRedirectUrl && outcome.platform === 'web') {\n // `_system.site.url` is set by the time a *callback* runs, since\n // `getRedirectUri` needs it to start the flow at all. It can still be\n // missing in local dev, so fall back to the same value that seeds it there.\n const siteUrl = (getConfig('_system.site.url') as string) || getLocalSiteUrl();\n let redirectUrl: string | null = null;\n try {\n redirectUrl = buildOAuthErrorRedirect(siteUrl, authConfig.oauthErrorRedirectUrl, {\n error: errorMessage,\n errorCode,\n });\n } catch (err) {\n // A relative `oauthErrorRedirectUrl` against an unusable base throws here.\n // Report the original failure below rather than turning it into a 500.\n console.error(\n `[modelence] Could not build the OAuth error redirect from ` +\n `${JSON.stringify(authConfig.oauthErrorRedirectUrl)} and site URL ` +\n `${JSON.stringify(siteUrl)}. Falling back to the default error response.`,\n err\n );\n }\n\n if (redirectUrl) {\n // Same shape as the mobile deep link, so a client can handle both alike.\n // The message is not a credential, but it is user-facing text about a\n // failed sign-in; keep it out of Referer like the mobile branch does.\n res.set('Referrer-Policy', 'no-referrer');\n return res.redirect(redirectUrl);\n }\n }\n\n const response = res.status(statusCode);\n if (authConfig.errorComponent) {\n try {\n const html = authConfig.errorComponent({ error: errorMessage, statusCode });\n if (html) return response.send(html);\n } catch (err) {\n console.error('Unhandled error in authConfig.errorComponent:', err);\n }\n }\n\n return response.json({ error: errorMessage });\n}\n\nexport async function authenticateUser(\n res: Response,\n userId: ObjectId,\n provider: OAuthProvider,\n outcome: OAuthOutcome = { platform: 'web' }\n) {\n if (outcome.platform === 'mobile') {\n // Fail closed rather than falling through to a cookie session: a mobile\n // sign-in always carries a challenge (initiation rejects it otherwise), so\n // reaching here without one means the state was tampered with or truncated.\n // Silently issuing a browser session instead would strand the app at `/`.\n if (!isBoundMobileOutcome(outcome)) {\n sendOAuthError(\n res,\n 400,\n 'This sign-in could not be completed. Please try signing in again.',\n outcome,\n 'invalid_state'\n );\n return;\n }\n\n // Deliberately no session and no cookie: the deep link is the weakest hop (a\n // custom scheme can be claimed by any installed app), so it carries only a\n // single-use code. The session is minted at redemption over TLS, so an\n // intercepted-but-unredeemed code never corresponds to a live session.\n const code = await issueOAuthExchangeCode(userId.toString(), provider, outcome.codeChallenge);\n\n // The deep link carries a credential; keep it out of any Referer header.\n res.set('Referrer-Policy', 'no-referrer');\n res.redirect(buildMobileRedirect(outcome.redirectUri, { code }));\n return;\n }\n\n const { authToken } = await createSession(userId);\n\n setAuthTokenCookie(res, authToken);\n res.redirect('/');\n}\n\nasync function handleExistingProviderLogin(\n res: Response,\n userData: OAuthUserData,\n existingUser: User,\n session: Session | null,\n connectionInfo: ConnectionInfo,\n outcome: OAuthOutcome\n) {\n const authConfig = getAuthConfig();\n\n try {\n if (existingUser.status === 'disabled' || existingUser.status === 'deleted') {\n sendOAuthError(res, 400, 'User account is not active.', outcome);\n return;\n }\n\n //Add User FirstName,LastName, AvatarURL if not exists\n const update: Partial<Pick<OAuthUserData, 'firstName' | 'lastName' | 'avatarUrl'>> = {};\n\n if (existingUser.firstName === undefined && userData.firstName) {\n update.firstName = userData.firstName;\n }\n if (existingUser.lastName === undefined && userData.lastName) {\n update.lastName = userData.lastName;\n }\n if (existingUser.avatarUrl === undefined && userData.avatarUrl) {\n update.avatarUrl = userData.avatarUrl;\n }\n\n let user = existingUser;\n\n if (Object.keys(update).length > 0) {\n await usersCollection.updateOne({ _id: existingUser._id }, { $set: update });\n user = { ...existingUser, ...update } as typeof existingUser;\n }\n\n await authenticateUser(res, existingUser._id, userData.providerName, outcome);\n\n if (shouldFireLoginHooks(outcome)) {\n authConfig.onAfterLogin?.({\n provider: userData.providerName,\n user,\n session,\n connectionInfo,\n });\n authConfig.login?.onSuccess?.(user);\n }\n } catch (error) {\n if (error instanceof Error) {\n authConfig.login?.onError?.(error);\n\n authConfig.onLoginError?.({\n provider: userData.providerName,\n error,\n session,\n connectionInfo,\n });\n }\n throw error;\n }\n}\n\nasync function handleExistingEmailLogin(\n res: Response,\n userData: OAuthUserData,\n existingUserByEmail: User,\n session: Session | null,\n connectionInfo: ConnectionInfo,\n outcome: OAuthOutcome\n) {\n const authConfig = getAuthConfig();\n const linkingMode = authConfig.oauthAccountLinking ?? 'manual';\n\n if (linkingMode === 'auto' && userData.emailVerified) {\n if (existingUserByEmail.status === 'disabled' || existingUserByEmail.status === 'deleted') {\n sendOAuthError(res, 400, 'User account is not active.', outcome);\n return;\n }\n\n const matchedEmail = existingUserByEmail.emails?.find(\n (emailDoc: UserEmail) => emailDoc.address.toLowerCase() === userData.email.toLowerCase()\n );\n\n // Prevent pre-registration takeover by requiring local ownership verification too.\n if (!matchedEmail?.verified) {\n sendOAuthError(\n res,\n 400,\n 'User with this email already exists. Please log in instead.',\n outcome\n );\n return;\n }\n\n try {\n // Build profile fields to backfill from provider data if missing\n const profileUpdate: Partial<Pick<OAuthUserData, 'firstName' | 'lastName' | 'avatarUrl'>> = {\n ...(existingUserByEmail.firstName === undefined &&\n userData.firstName && { firstName: userData.firstName }),\n ...(existingUserByEmail.lastName === undefined &&\n userData.lastName && { lastName: userData.lastName }),\n ...(existingUserByEmail.avatarUrl === undefined &&\n userData.avatarUrl && { avatarUrl: userData.avatarUrl }),\n };\n\n // Single atomic update — link provider + backfill profile in one round trip\n const updateResult = await usersCollection.updateOne(\n {\n _id: existingUserByEmail._id,\n status: { $nin: ['deleted', 'disabled'] },\n $or: [\n { [`authMethods.${userData.providerName}.id`]: { $exists: false } },\n { [`authMethods.${userData.providerName}.id`]: userData.id },\n ],\n },\n {\n $set: {\n [`authMethods.${userData.providerName}.id`]: userData.id,\n ...profileUpdate,\n },\n }\n );\n\n const autoLinkSuccessful = updateResult.matchedCount > 0;\n\n if (!autoLinkSuccessful) {\n // User was deleted/disabled between findOne and updateOne, or linked to a *different* ID\n sendOAuthError(\n res,\n 400,\n 'User with this email already exists. Please log in instead.',\n outcome\n );\n return;\n }\n\n await authenticateUser(res, existingUserByEmail._id, userData.providerName, outcome);\n\n // Construct updated user in-memory to provide fresh data to callbacks\n const updatedUser: User = {\n ...existingUserByEmail,\n ...profileUpdate,\n authMethods: {\n ...existingUserByEmail.authMethods,\n [userData.providerName]: {\n id: userData.id,\n },\n },\n };\n\n if (shouldFireLoginHooks(outcome)) {\n authConfig.onAfterLogin?.({\n provider: userData.providerName,\n user: updatedUser,\n session,\n connectionInfo,\n });\n authConfig.login?.onSuccess?.(updatedUser);\n }\n\n return;\n } catch (error) {\n if (error instanceof Error) {\n authConfig.login?.onError?.(error);\n\n authConfig.onLoginError?.({\n provider: userData.providerName,\n error,\n session,\n connectionInfo,\n });\n }\n throw error;\n }\n }\n\n // Manual mode (default) or unverified email — reject\n sendOAuthError(res, 400, 'User with this email already exists. Please log in instead.', outcome);\n return;\n}\n\nasync function handleNewUserSignup(\n res: Response,\n userData: OAuthUserData,\n session: Session | null,\n connectionInfo: ConnectionInfo,\n outcome: OAuthOutcome\n) {\n const authConfig = getAuthConfig();\n\n try {\n let handle: string;\n\n if (authConfig.generateHandle) {\n const generated = await authConfig.generateHandle!({\n email: userData.email,\n firstName: userData.firstName,\n lastName: userData.lastName,\n });\n //Don't throw error if handle is already taken, instead add a suffix '_2', '_3', etc. to the handle\n handle = await resolveUniqueHandle(generated, userData.email, {\n throwOnConflict: false,\n });\n } else {\n handle = await resolveUniqueHandle(undefined, userData.email);\n }\n\n const userDoc = {\n handle: handle,\n status: 'active' as const,\n emails: [\n {\n address: userData.email,\n verified: userData.emailVerified,\n },\n ],\n createdAt: new Date(),\n authMethods: {\n [userData.providerName]: {\n id: userData.id,\n },\n },\n ...(userData.firstName !== undefined && { firstName: userData.firstName }),\n ...(userData.lastName !== undefined && { lastName: userData.lastName }),\n ...(userData.avatarUrl !== undefined && { avatarUrl: userData.avatarUrl }),\n };\n\n const newUser = await usersCollection.insertOne(userDoc);\n\n await authenticateUser(res, newUser.insertedId, userData.providerName, outcome);\n\n const userDocument = await usersCollection.findOne(\n { _id: newUser.insertedId },\n { readPreference: 'primary' }\n );\n\n if (userDocument) {\n authConfig.onAfterSignup?.({\n provider: userData.providerName,\n user: userDocument,\n session,\n connectionInfo,\n });\n\n authConfig.signup?.onSuccess?.(userDocument);\n }\n } catch (error) {\n if (error instanceof Error) {\n authConfig.onSignupError?.({\n provider: userData.providerName,\n error,\n session,\n connectionInfo,\n });\n\n authConfig.signup?.onError?.(error);\n }\n throw error;\n }\n}\n\nexport function getRedirectUri(provider: string): string {\n return `${getConfig('_system.site.url')}/api/_internal/auth/${provider}/callback`;\n}\n\nexport async function handleOAuthUserAuthentication(\n req: Request,\n res: Response,\n userData: OAuthUserData,\n outcome: OAuthOutcome = { platform: 'web' }\n): Promise<void> {\n // 1. Try to fetch existing user by OAuth ID\n const existingUser = await usersCollection.findOne({\n [`authMethods.${userData.providerName}.id`]: userData.id,\n });\n\n const { session, connectionInfo } = await getCallContext(req, res);\n\n if (existingUser) {\n return handleExistingProviderLogin(\n res,\n userData,\n existingUser,\n session,\n connectionInfo,\n outcome\n );\n }\n\n // 2. Validate Email is provided by Provider\n if (!userData.email) {\n sendOAuthError(\n res,\n 400,\n `Email address is required for ${userData.providerName} authentication.`,\n outcome\n );\n return;\n }\n\n // 3. Try to fetch existing user by Email\n let existingUserByEmail;\n\n try {\n existingUserByEmail = await usersCollection.findOne(\n { 'emails.address': userData.email, status: { $ne: 'deleted' } },\n { collation: { locale: 'en', strength: 2 } }\n );\n } catch (error) {\n if (error instanceof Error) {\n const authConfig = getAuthConfig();\n authConfig.onSignupError?.({\n provider: userData.providerName,\n error,\n session,\n connectionInfo,\n });\n\n authConfig.signup?.onError?.(error);\n }\n throw error;\n }\n\n //User Already existed via email verification but now trying to login via OAuth Providers from the same email\n if (existingUserByEmail) {\n return handleExistingEmailLogin(\n res,\n userData,\n existingUserByEmail,\n session,\n connectionInfo,\n outcome\n );\n }\n\n //New User\n return handleNewUserSignup(res, userData, session, connectionInfo, outcome);\n}\n\nexport function clearOAuthLinkCookie(res: Response) {\n // Important: must clear the httpOnly cookie used during OAuth linking\n res.cookie('oauthLinkToken', '', {\n httpOnly: true,\n maxAge: 0,\n path: '/api/_internal/auth/',\n sameSite: 'lax',\n secure: process.env.NODE_ENV === 'production',\n });\n}\n\nfunction safelyCallHook(hook?: () => void) {\n if (!hook) return;\n\n try {\n hook();\n } catch (err) {\n console.error('Error executing OAuth hook:', err);\n }\n}\n\nexport interface OAuthStateResult {\n mode: string;\n /** Set only in the React Native linking flow. */\n linkedUserId?: string;\n /** Which client started the flow; decides how the callback hands back the result. */\n platform: OAuthPlatform;\n /** Deep link to return to. Set only when `platform` is 'mobile'. */\n redirectUri?: string;\n /** Device binding for the exchange code. Set only when `platform` is 'mobile'. */\n codeChallenge?: string;\n}\n\n/**\n * Builds the value of the per-provider OAuth state cookie.\n *\n * Fields are colon-separated for backwards compatibility with in-flight cookies\n * written before mobile support (`state:mode` and `state:mode:userId` both still\n * parse). `redirectUri` contains colons and slashes of its own, so it is\n * base64url-encoded rather than embedded raw — otherwise a deep link would split\n * into several bogus fields.\n */\nexport function encodeOAuthState(params: {\n state: string;\n mode: string;\n linkedUserId?: string | null;\n platform?: OAuthPlatform;\n redirectUri?: string | null;\n codeChallenge?: string | null;\n}): string {\n const { state, mode, linkedUserId, platform = 'web', redirectUri, codeChallenge } = params;\n const fields = [state, mode, linkedUserId ?? ''];\n\n // Only extend the cookie when there is something to say — a web flow keeps\n // producing exactly the value it produced before mobile existed.\n if (platform !== 'web' || redirectUri) {\n fields.push(platform);\n fields.push(redirectUri ? Buffer.from(redirectUri, 'utf8').toString('base64url') : '');\n fields.push(codeChallenge ?? '');\n }\n\n return fields.join(':');\n}\n\nfunction decodeOAuthState(storedState: string): {\n stateValue: string;\n mode: string;\n linkedUserId?: string;\n platform: OAuthPlatform;\n redirectUri?: string;\n codeChallenge?: string;\n} {\n const [stateValue, mode, linkedUserId, platform, encodedRedirectUri, codeChallenge] = (\n storedState || ''\n ).split(':');\n\n // Never throws: invalid base64url decodes to garbage rather than raising, and\n // garbage is rejected by the allowlist check in the caller.\n const redirectUri = encodedRedirectUri\n ? Buffer.from(encodedRedirectUri, 'base64url').toString('utf8') || undefined\n : undefined;\n\n return {\n stateValue,\n mode: mode || 'login',\n ...(linkedUserId ? { linkedUserId } : {}),\n platform: platform === 'mobile' ? 'mobile' : 'web',\n ...(redirectUri ? { redirectUri } : {}),\n ...(codeChallenge ? { codeChallenge } : {}),\n };\n}\n\nexport function validateOAuthStateAndGetMode(\n req: Request,\n res: Response,\n stateCookieName: string\n): OAuthStateResult | null {\n const state = req.query.state as string;\n const storedState = req.cookies[stateCookieName];\n\n const decoded = decodeOAuthState(storedState || '');\n\n // Re-check against the current allowlist: it may have changed while the user\n // was on the consent screen, and a redirect target is worth validating on both\n // sides of a round trip.\n const mobileOutcome = resolveDecodedMobileOutcome(decoded);\n\n if (!state || !storedState || state !== decoded.stateValue) {\n // Deep-link the failure back when the decoded target survives re-validation,\n // rather than stranding the user on JSON. Trusted because the allowlist just\n // approved it, not because the cookie said so.\n //\n // The message says \"sign in again\" rather than naming CSRF: the overwhelming\n // majority of the users who see it are victims of an expired cookie, a stale\n // bookmarked callback, or a back button — not of an attack. Someone actually\n // forging a state learns nothing from being told so, while everyone else is\n // handed an accusation instead of the one action that fixes it. `invalid_state`\n // still identifies the case precisely for anything branching on it.\n sendOAuthError(\n res,\n 400,\n 'Your sign-in session has expired. Please sign in again.',\n mobileOutcome ?? { platform: 'web' },\n 'invalid_state'\n );\n return null;\n }\n\n res.clearCookie(stateCookieName);\n\n if (decoded.platform === 'mobile' && !mobileOutcome) {\n sendOAuthError(\n res,\n 400,\n 'Invalid OAuth redirect target.',\n { platform: 'web' },\n 'invalid_redirect'\n );\n return null;\n }\n\n return {\n mode: decoded.mode,\n ...(decoded.linkedUserId ? { linkedUserId: decoded.linkedUserId } : {}),\n platform: decoded.platform,\n ...(decoded.redirectUri ? { redirectUri: decoded.redirectUri } : {}),\n ...(decoded.codeChallenge ? { codeChallenge: decoded.codeChallenge } : {}),\n };\n}\n\n/**\n * Mobile outcome for an already-decoded state cookie, or null when the flow is\n * not mobile or its target no longer passes the allowlist.\n *\n * Used to answer \"can this failure be deep-linked back into the app?\" on error\n * paths that run before — or instead of — a successful state validation.\n */\nfunction resolveDecodedMobileOutcome(decoded: {\n platform: OAuthPlatform;\n redirectUri?: string;\n codeChallenge?: string;\n}): OAuthErrorTarget | null {\n if (decoded.platform !== 'mobile') return null;\n if (!decoded.redirectUri || !isAllowedMobileRedirectUrl(decoded.redirectUri)) return null;\n\n return { platform: 'mobile', redirectUri: decoded.redirectUri };\n}\n\n/**\n * Mobile outcome recovered from the raw state cookie, for failures that happen\n * before the state is validated at all — a missing `code`, or a consent screen\n * the user declined. The provider sends those back to the same callback with no\n * usable query state, but the cookie still says where the app lives.\n */\nexport function resolveMobileOutcomeFromCookie(\n req: Request,\n stateCookieName: string\n): OAuthErrorTarget | null {\n return resolveDecodedMobileOutcome(decodeOAuthState(req.cookies?.[stateCookieName] || ''));\n}\n\n/**\n * Explains why a redirect URI was rejected, in terms of what to change.\n *\n * Every scheme is a legitimate target — Expo Web serves the app over https,\n * Expo Go over exp:// — so the scheme only selects which hint to add.\n *\n * Classifies rather than echoes: the URI is caller-controlled and this message\n * reaches `authConfig.errorComponent`, which renders unescaped HTML.\n */\nfunction describeRejectedRedirectUri(redirectUri: string): string {\n const configured = getAllowedMobileRedirectUrls();\n\n if (configured.length === 0) {\n return (\n 'Mobile sign-in is not configured: auth.mobile.redirectUrls is empty. ' +\n \"Add your app's deep link (e.g. 'myapp://auth') to enable it.\"\n );\n }\n\n let protocol: string;\n try {\n protocol = new URL(redirectUri).protocol.toLowerCase();\n } catch {\n return (\n \"This redirectUri is not a valid URL. Pass your app's deep link, \" +\n \"e.g. 'myapp://auth', and list it in auth.mobile.redirectUrls.\"\n );\n }\n\n // http(s) and exp: are legitimate — Expo Web and Expo Go serve the app over\n // them — so the advice matches any other scheme, plus a note on why the value\n // differs between build targets.\n if (protocol === 'http:' || protocol === 'https:' || protocol === 'exp:') {\n return (\n 'This redirectUri is not in auth.mobile.redirectUrls. Add it verbatim. ' +\n 'Note that Linking.createURL returns a different value per build target — ' +\n \"your app's scheme in a native build, an https URL on Expo Web, an exp:// URL \" +\n 'in Expo Go — so each one you test needs its own entry.'\n );\n }\n\n return (\n 'This redirectUri is not in auth.mobile.redirectUrls. Add it verbatim — ' +\n 'entries match on scheme, host and path, so a differing path or port is a different target.'\n );\n}\n\n/**\n * Reads and validates the mobile handoff parameters from an OAuth initiation\n * request. Returns null when the request is not a mobile flow.\n *\n * Validation happens here, before the redirect to the provider, so a\n * misconfigured or malicious target fails immediately instead of after the user\n * has already granted consent.\n */\nexport function resolveMobileRedirectRequest(\n req: Request,\n res: Response,\n mode: string\n): { ok: true; redirectUri: string | null; codeChallenge: string | null } | { ok: false } {\n if (req.query.platform !== 'mobile') {\n return { ok: true, redirectUri: null, codeChallenge: null };\n }\n\n const redirectUri = typeof req.query.redirectUri === 'string' ? req.query.redirectUri : '';\n\n if (!redirectUri) {\n sendOAuthError(res, 400, 'A redirectUri is required for mobile authentication.', {\n platform: 'api',\n });\n return { ok: false };\n }\n\n if (!isAllowedMobileRedirectUrl(redirectUri)) {\n // The full value goes to the log, not the response: `errorMessage` reaches\n // `authConfig.errorComponent`, whose HTML is sent unescaped, so echoing\n // caller-controlled text back would hand app authors an XSS foot-gun.\n console.error(\n `[modelence] Rejected mobile OAuth redirectUri: ${JSON.stringify(redirectUri)}. ` +\n `Allowed: ${JSON.stringify(getAllowedMobileRedirectUrls())}`\n );\n\n sendOAuthError(\n res,\n 400,\n describeRejectedRedirectUri(redirectUri),\n { platform: 'api' },\n 'invalid_redirect'\n );\n return { ok: false };\n }\n\n const rawChallenge = typeof req.query.codeChallenge === 'string' ? req.query.codeChallenge : '';\n const hasValidChallenge = /^[A-Za-z0-9._~-]{16,256}$/.test(rawChallenge);\n\n // Only a login mints an exchange code, so only a login needs the binding. An\n // omitted challenge there would produce an unbound code, redeemable by anyone\n // — including a victim mid-flow whose verifier the server would then ignore.\n // Linking redirects with `linked=<provider>` and no credential, so there is\n // nothing to bind. The charset is constrained because a colon would corrupt\n // the delimiter-separated state cookie.\n if (mode !== 'link' && !hasValidChallenge) {\n sendOAuthError(\n res,\n 400,\n 'This sign-in request is missing a valid codeChallenge. Update the Modelence ' +\n 'client package — signInWithOAuth generates it automatically.',\n { platform: 'api' }\n );\n return { ok: false };\n }\n\n return { ok: true, redirectUri, codeChallenge: hasValidChallenge ? rawChallenge : null };\n}\n\n/**\n * Everything an OAuth initiation route needs to do between \"auth is configured\"\n * and \"redirect the user to the provider\": validate the mobile handoff, resolve\n * a link nonce, and build the state cookie value.\n *\n * Shared so a newly added provider cannot quietly ship a mobile flow that skips\n * redirect validation or device binding. Returns null when a response has\n * already been sent.\n */\nexport async function prepareOAuthInitiation(\n req: Request,\n res: Response,\n stateCookieName: string\n): Promise<{ state: string; mode: string } | null> {\n const state = randomBytes(32).toString('hex');\n const mode = req.query.mode === 'link' ? 'link' : 'login';\n\n // Validate the mobile deep link before leaving the app, so a bad target\n // fails here rather than after the user has granted consent.\n const mobileRequest = resolveMobileRedirectRequest(req, res, mode);\n if (!mobileRequest.ok) return null;\n const { redirectUri: mobileRedirectUri, codeChallenge } = mobileRequest;\n\n // React Native: consume single-use nonce and embed resolved userId in state cookie.\n let linkedUserId: string | null = null;\n if (mode === 'link' && req.query.linkNonce) {\n linkedUserId = await resolveUserIdFromLinkNonce(req.query.linkNonce as string);\n if (!linkedUserId) {\n // The redirect target was validated just above, so an expired nonce can\n // be reported into the app rather than as JSON in the device browser.\n sendOAuthError(\n res,\n 401,\n 'Invalid or expired link nonce for OAuth linking.',\n mobileRedirectUri\n ? { platform: 'mobile', redirectUri: mobileRedirectUri }\n : // Also an initiation-time failure: the caller gets JSON, not a redirect.\n { platform: 'api' },\n 'invalid_link_nonce'\n );\n return null;\n }\n }\n\n const stateValue = encodeOAuthState({\n state,\n mode,\n linkedUserId,\n platform: mobileRedirectUri ? 'mobile' : 'web',\n redirectUri: mobileRedirectUri,\n codeChallenge,\n });\n\n res.cookie(stateCookieName, stateValue, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: time.minutes(10),\n });\n\n return { state, mode };\n}\n\nexport async function handleOAuthProviderLink(\n req: Request,\n res: Response,\n userData: OAuthUserData,\n linkedUserId?: string,\n outcome: OAuthOutcome = { platform: 'web' }\n): Promise<void> {\n const authConfig = getAuthConfig();\n const { session, connectionInfo } = await getCallContext(req, res);\n\n // React Native: linkedUserId (from state cookie) takes precedence over the\n // browser session, which may belong to a different user.\n let resolvedUserId: ObjectId | null = null;\n if (linkedUserId) {\n if (!ObjectId.isValid(linkedUserId)) {\n clearOAuthLinkCookie(res);\n sendOAuthError(res, 400, 'Invalid OAuth linking state.', outcome);\n return;\n }\n resolvedUserId = new ObjectId(linkedUserId);\n } else {\n resolvedUserId = session?.userId ?? null;\n }\n\n if (!resolvedUserId) {\n clearOAuthLinkCookie(res);\n sendOAuthError(res, 401, 'You must be signed in to link a provider.', outcome);\n return;\n }\n\n const userId = resolvedUserId;\n\n try {\n // Atomically attach the provider to the current user while preventing\n // overwriting an existing provider ID on the same user.\n // A unique index on the provider ID ensures it cannot be linked to another user.\n const providerField = `authMethods.${userData.providerName}.id`;\n\n const updateResult = await usersCollection.updateOne(\n {\n _id: userId,\n status: { $nin: ['deleted', 'disabled'] },\n $or: [{ [providerField]: { $exists: false } }, { [providerField]: userData.id }],\n },\n {\n $set: {\n [providerField]: userData.id,\n },\n }\n );\n\n // If no document matched, figure out why\n if (updateResult.matchedCount === 0) {\n const currentUser = await usersCollection.findOne({ _id: userId });\n\n if (!currentUser || currentUser.status === 'deleted' || currentUser.status === 'disabled') {\n safelyCallHook(() =>\n authConfig.onOAuthLinkError?.({\n provider: userData.providerName,\n error: new Error('User account not found or not active'),\n session,\n connectionInfo,\n })\n );\n\n clearOAuthLinkCookie(res);\n\n sendOAuthError(res, 400, 'User account is not active.', outcome);\n return;\n }\n\n // Detect if the user already linked a different OAuth account\n const existingProviderId = currentUser?.authMethods?.[userData.providerName]?.id;\n\n if (existingProviderId && existingProviderId !== userData.id) {\n safelyCallHook(() =>\n authConfig.onOAuthLinkError?.({\n provider: userData.providerName,\n error: new Error(\n `User already has a different ${userData.providerName} account linked`\n ),\n session,\n connectionInfo,\n })\n );\n\n clearOAuthLinkCookie(res);\n\n sendOAuthError(\n res,\n 400,\n `You have already linked a different ${userData.providerName} account.`,\n outcome\n );\n return;\n }\n\n // Fallback safety guard in case the DB state does not match any expected branch\n safelyCallHook(() =>\n authConfig.onOAuthLinkError?.({\n provider: userData.providerName,\n error: new Error(`Unexpected OAuth linking state for ${userData.providerName}`),\n session,\n connectionInfo,\n })\n );\n\n clearOAuthLinkCookie(res);\n\n sendOAuthError(res, 400, `Unable to link ${userData.providerName} account.`, outcome);\n return;\n }\n\n const updatedUser = await usersCollection.findOne(\n { _id: userId },\n { readPreference: 'primary' }\n );\n\n if (updatedUser) {\n safelyCallHook(() =>\n authConfig.onAfterOAuthLink?.({\n provider: userData.providerName,\n user: updatedUser,\n session,\n connectionInfo,\n })\n );\n }\n\n // Redirect back to the app after successful link. On mobile this returns to\n // the deep link with no code: linking happens for an already-signed-in user,\n // so there is no new session to hand over.\n clearOAuthLinkCookie(res);\n\n if (outcome.platform === 'mobile') {\n res.set('Referrer-Policy', 'no-referrer');\n res.redirect(buildMobileRedirect(outcome.redirectUri, { linked: userData.providerName }));\n return;\n }\n\n res.redirect('/');\n } catch (error) {\n if (error instanceof MongoServerError && error.code === 11000) {\n safelyCallHook(() =>\n authConfig.onOAuthLinkError?.({\n provider: userData.providerName,\n error,\n session,\n connectionInfo,\n })\n );\n\n clearOAuthLinkCookie(res);\n\n sendOAuthError(\n res,\n 400,\n `This ${userData.providerName} account is already linked to a different user.`,\n outcome\n );\n return;\n }\n\n if (error instanceof Error) {\n safelyCallHook(() =>\n authConfig.onOAuthLinkError?.({\n provider: userData.providerName,\n error,\n session,\n connectionInfo,\n })\n );\n }\n\n clearOAuthLinkCookie(res);\n if (!res.headersSent) {\n throw error;\n }\n }\n}\n\nexport function validateOAuthCode(code: unknown): string | null {\n if (!code || typeof code !== 'string') {\n return null;\n }\n return code;\n}\n","import { getConfig } from '@/server';\nimport {\n Router,\n type Request,\n type Response,\n type NextFunction,\n type Router as ExpressRouter,\n} from 'express';\nimport {\n getRedirectUri,\n handleOAuthUserAuthentication,\n handleOAuthProviderLink,\n validateOAuthCode,\n type OAuthUserData,\n clearOAuthLinkCookie,\n validateOAuthStateAndGetMode,\n sendOAuthError,\n prepareOAuthInitiation,\n resolveMobileOutcomeFromCookie,\n toOAuthOutcome,\n} from './oauthCommon';\n\ninterface GoogleTokenResponse {\n access_token: string;\n expires_in: number;\n scope: string;\n token_type: string;\n id_token: string;\n}\n\ninterface GoogleUserInfo {\n id: string;\n name: string;\n given_name?: string;\n family_name?: string;\n email: string;\n verified_email: boolean;\n picture?: string;\n}\n\nasync function exchangeCodeForToken(\n code: string,\n clientId: string,\n clientSecret: string,\n redirectUri: string\n): Promise<GoogleTokenResponse> {\n const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n code,\n client_id: clientId,\n client_secret: clientSecret,\n redirect_uri: redirectUri,\n grant_type: 'authorization_code',\n }),\n });\n\n if (!tokenResponse.ok) {\n throw new Error(`Failed to exchange code for token: ${tokenResponse.statusText}`);\n }\n\n return tokenResponse.json();\n}\n\nasync function fetchGoogleUserInfo(accessToken: string): Promise<GoogleUserInfo> {\n const userInfoResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!userInfoResponse.ok) {\n throw new Error(`Failed to fetch user info: ${userInfoResponse.statusText}`);\n }\n\n return userInfoResponse.json();\n}\n\nasync function handleGoogleAuthenticationCallback(req: Request, res: Response) {\n const code = validateOAuthCode(req.query.code);\n\n if (!code) {\n // No usable state to validate yet (a declined consent screen sends none),\n // but the state cookie still records where the app lives, so this can be\n // reported into the app instead of as JSON in the device browser.\n sendOAuthError(\n res,\n 400,\n 'Missing authorization code',\n resolveMobileOutcomeFromCookie(req, 'authStateGoogle') ?? undefined,\n 'missing_code'\n );\n return;\n }\n\n const stateResult = validateOAuthStateAndGetMode(req, res, 'authStateGoogle');\n if (!stateResult) return;\n const { mode, linkedUserId } = stateResult;\n const outcome = toOAuthOutcome(stateResult);\n\n const googleClientId = String(getConfig('_system.user.auth.google.clientId'));\n const googleClientSecret = String(getConfig('_system.user.auth.google.clientSecret'));\n const redirectUri = getRedirectUri('google');\n\n try {\n // Exchange code for tokens\n const tokenData = await exchangeCodeForToken(\n code,\n googleClientId,\n googleClientSecret,\n redirectUri\n );\n\n // Fetch user info\n const googleUser = await fetchGoogleUserInfo(tokenData.access_token);\n\n const userData: OAuthUserData = {\n id: googleUser.id,\n email: googleUser.email,\n emailVerified: googleUser.verified_email,\n providerName: 'google',\n firstName: googleUser.given_name || undefined,\n lastName: googleUser.family_name || undefined,\n avatarUrl: googleUser.picture || undefined,\n };\n if (mode === 'link') {\n await handleOAuthProviderLink(req, res, userData, linkedUserId, outcome);\n } else {\n await handleOAuthUserAuthentication(req, res, userData, outcome);\n }\n } catch (error) {\n console.error('Google OAuth error:', error);\n if (mode === 'link') {\n clearOAuthLinkCookie(res);\n }\n sendOAuthError(res, 500, 'Authentication failed', outcome);\n }\n}\n\nfunction getRouter(): ExpressRouter {\n const googleAuthRouter = Router();\n\n // Middleware to check if Google auth is enabled and configured\n const checkGoogleEnabled = (_req: Request, res: Response, next: NextFunction) => {\n const googleEnabled = Boolean(getConfig('_system.user.auth.google.enabled'));\n const googleClientId = String(getConfig('_system.user.auth.google.clientId'));\n const googleClientSecret = String(getConfig('_system.user.auth.google.clientSecret'));\n\n if (!googleEnabled || !googleClientId || !googleClientSecret) {\n sendOAuthError(res, 503, 'Google authentication is not configured', {\n platform: 'api',\n });\n return;\n }\n\n next();\n };\n\n // Initiate OAuth flow\n googleAuthRouter.get(\n '/api/_internal/auth/google',\n checkGoogleEnabled,\n async (req: Request, res: Response) => {\n const googleClientId = String(getConfig('_system.user.auth.google.clientId'));\n const redirectUri = getRedirectUri('google');\n\n const initiation = await prepareOAuthInitiation(req, res, 'authStateGoogle');\n if (!initiation) return;\n const { state } = initiation;\n\n const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');\n authUrl.searchParams.append('client_id', googleClientId);\n authUrl.searchParams.append('redirect_uri', redirectUri);\n authUrl.searchParams.append('response_type', 'code');\n authUrl.searchParams.append('scope', 'profile email');\n authUrl.searchParams.append('access_type', 'online');\n authUrl.searchParams.append('state', state);\n\n res.redirect(authUrl.toString());\n }\n );\n\n // Handle OAuth callback\n googleAuthRouter.get(\n '/api/_internal/auth/google/callback',\n checkGoogleEnabled,\n handleGoogleAuthenticationCallback\n );\n\n return googleAuthRouter;\n}\n\nexport default getRouter;\n","import { getConfig } from '@/server';\nimport {\n Router,\n type Request,\n type Response,\n type NextFunction,\n type Router as ExpressRouter,\n} from 'express';\nimport {\n getRedirectUri,\n handleOAuthUserAuthentication,\n handleOAuthProviderLink,\n validateOAuthCode,\n type OAuthUserData,\n clearOAuthLinkCookie,\n validateOAuthStateAndGetMode,\n sendOAuthError,\n prepareOAuthInitiation,\n resolveMobileOutcomeFromCookie,\n toOAuthOutcome,\n} from './oauthCommon';\n\ninterface GitHubTokenResponse {\n access_token: string;\n token_type: string;\n scope: string;\n}\n\ninterface GitHubUserInfo {\n id: number;\n login: string;\n name: string;\n email: string | null;\n avatar_url?: string;\n}\n\ninterface GitHubEmail {\n email: string;\n primary: boolean;\n verified: boolean;\n visibility: 'public' | 'private' | null;\n}\n\nasync function exchangeCodeForToken(\n code: string,\n clientId: string,\n clientSecret: string,\n redirectUri: string\n): Promise<GitHubTokenResponse> {\n const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({\n client_id: clientId,\n client_secret: clientSecret,\n code,\n redirect_uri: redirectUri,\n }),\n });\n\n if (!tokenResponse.ok) {\n throw new Error(`Failed to exchange code for token: ${tokenResponse.statusText}`);\n }\n\n return tokenResponse.json();\n}\n\nasync function fetchGitHubUserInfo(accessToken: string): Promise<GitHubUserInfo> {\n const userInfoResponse = await fetch('https://api.github.com/user', {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/vnd.github.v3+json',\n },\n });\n\n if (!userInfoResponse.ok) {\n throw new Error(`Failed to fetch user info: ${userInfoResponse.statusText}`);\n }\n\n return userInfoResponse.json();\n}\n\nasync function fetchGitHubUserEmails(accessToken: string): Promise<GitHubEmail[]> {\n const response = await fetch('https://api.github.com/user/emails', {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/vnd.github.v3+json',\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch user emails: ${response.statusText}`);\n }\n\n return response.json();\n}\n\nasync function getGitHubUserEmail(\n githubUser: GitHubUserInfo,\n accessToken: string\n): Promise<string | null> {\n if (githubUser.email) {\n return githubUser.email;\n }\n\n const emails = await fetchGitHubUserEmails(accessToken);\n return emails.find((e) => e.primary && e.verified)?.email ?? null;\n}\n\nasync function handleGitHubAuthenticationCallback(req: Request, res: Response) {\n const code = validateOAuthCode(req.query.code);\n\n if (!code) {\n // No usable state to validate yet (a declined consent screen sends none),\n // but the state cookie still records where the app lives, so this can be\n // reported into the app instead of as JSON in the device browser.\n sendOAuthError(\n res,\n 400,\n 'Missing authorization code',\n resolveMobileOutcomeFromCookie(req, 'authStateGithub') ?? undefined,\n 'missing_code'\n );\n return;\n }\n\n const stateResult = validateOAuthStateAndGetMode(req, res, 'authStateGithub');\n if (!stateResult) return;\n const { mode, linkedUserId } = stateResult;\n const outcome = toOAuthOutcome(stateResult);\n\n const githubClientId = String(getConfig('_system.user.auth.github.clientId'));\n const githubClientSecret = String(getConfig('_system.user.auth.github.clientSecret'));\n const redirectUri = getRedirectUri('github');\n\n try {\n // Exchange code for access token\n const tokenData = await exchangeCodeForToken(\n code,\n githubClientId,\n githubClientSecret,\n redirectUri\n );\n\n // Fetch user info\n const githubUser = await fetchGitHubUserInfo(tokenData.access_token);\n\n // Resolve a usable GitHub email (public email or fallback to primary verified email)\n const githubEmail = await getGitHubUserEmail(githubUser, tokenData.access_token);\n\n if (!githubEmail) {\n if (mode === 'link') {\n clearOAuthLinkCookie(res);\n }\n\n sendOAuthError(\n res,\n 400,\n 'Unable to retrieve a primary verified email from GitHub. Please ensure your GitHub account has a verified email set as primary.',\n outcome\n );\n return;\n }\n\n const nameParts = githubUser.name ? githubUser.name.trim().split(/\\s+/) : [];\n const firstName = nameParts[0] || undefined;\n const lastName = nameParts.length > 1 ? nameParts.slice(1).join(' ') : undefined;\n\n const userData: OAuthUserData = {\n id: String(githubUser.id),\n email: githubEmail,\n emailVerified: true, // Assume public email is verified\n providerName: 'github',\n firstName,\n lastName,\n avatarUrl: githubUser.avatar_url || undefined,\n };\n\n if (mode === 'link') {\n await handleOAuthProviderLink(req, res, userData, linkedUserId, outcome);\n } else {\n await handleOAuthUserAuthentication(req, res, userData, outcome);\n }\n } catch (error) {\n console.error('GitHub OAuth error:', error);\n if (mode === 'link') {\n clearOAuthLinkCookie(res);\n }\n sendOAuthError(res, 500, 'Authentication failed', outcome);\n }\n}\n\nfunction getRouter(): ExpressRouter {\n const githubAuthRouter = Router();\n\n // Middleware to check if GitHub auth is enabled and configured\n const checkGitHubEnabled = (_req: Request, res: Response, next: NextFunction) => {\n const githubEnabled = Boolean(getConfig('_system.user.auth.github.enabled'));\n const githubClientId = String(getConfig('_system.user.auth.github.clientId'));\n const githubClientSecret = String(getConfig('_system.user.auth.github.clientSecret'));\n\n if (!githubEnabled || !githubClientId || !githubClientSecret) {\n sendOAuthError(res, 503, 'GitHub authentication is not configured', {\n platform: 'api',\n });\n return;\n }\n\n next();\n };\n\n // Initiate OAuth flow\n githubAuthRouter.get(\n '/api/_internal/auth/github',\n checkGitHubEnabled,\n async (req: Request, res: Response) => {\n const githubClientId = String(getConfig('_system.user.auth.github.clientId'));\n const redirectUri = getRedirectUri('github');\n const githubScopes = getConfig('_system.user.auth.github.scopes');\n const scopes = githubScopes\n ? String(githubScopes)\n .split(',')\n .map((s) => s.trim())\n .join(' ')\n : 'user:email';\n\n const initiation = await prepareOAuthInitiation(req, res, 'authStateGithub');\n if (!initiation) return;\n const { state } = initiation;\n\n const authUrl = new URL('https://github.com/login/oauth/authorize');\n authUrl.searchParams.append('client_id', githubClientId);\n authUrl.searchParams.append('redirect_uri', redirectUri);\n authUrl.searchParams.append('scope', scopes);\n authUrl.searchParams.append('state', state);\n\n res.redirect(authUrl.toString());\n }\n );\n\n // Handle OAuth callback\n githubAuthRouter.get(\n '/api/_internal/auth/github/callback',\n checkGitHubEnabled,\n handleGitHubAuthenticationCallback\n );\n\n return githubAuthRouter;\n}\n\nexport default getRouter;\n","import { Request, Response, NextFunction } from 'express';\nimport { RouteHandler } from './types';\nimport { ModelenceError } from '../error';\nimport { authenticate } from '../auth';\nimport { getMongodbUri } from '../db/client';\nimport type { Context } from '../methods/types';\nimport { startTransaction, redactSensitive, captureError } from '../telemetry';\n\n// TODO: Use cookies for authentication and automatically add session/user to context if accessing from browser\nexport function createRouteHandler(method: string, path: string, handler: RouteHandler) {\n return async (req: Request, res: Response, next: NextFunction) => {\n const authToken = req.headers['x-modelence-auth-token'];\n let context: Pick<Context, 'session' | 'user'> = { session: null, user: null };\n\n if (typeof authToken === 'string' && getMongodbUri()) {\n try {\n const { session, user } = await authenticate(authToken);\n context = { session, user };\n } catch {\n // If authentication fails, context remains null\n }\n }\n\n const transaction = startTransaction('route', `route:${method.toLowerCase()}:${path}`, {\n method,\n path,\n // Redact secrets (e.g. tokens in the URL) before they reach the APM sink.\n query: redactSensitive(req.query),\n body: redactSensitive(req.body),\n params: redactSensitive(req.params),\n });\n\n try {\n const response = await handler(\n {\n query: req.query as Record<string, string>,\n body: req.body,\n params: req.params as Record<string, string>,\n headers: req.headers as Record<string, string>,\n cookies: req.cookies,\n rawBody: Buffer.isBuffer(req.body) ? req.body : undefined,\n req,\n res,\n next,\n },\n context\n );\n\n transaction.end();\n\n // If the handler returns null, we expect it to handle the response itself\n if (response) {\n res.status(response.status || 200);\n\n // Apply response headers before sending the body/redirect, otherwise\n // res.redirect()/res.send() flush the response and the headers are lost.\n if (response.contentType) {\n res.setHeader('Content-Type', response.contentType);\n }\n\n // Custom headers are applied after contentType so callers can override\n // it with headers['Content-Type'] when needed.\n if (response.headers) {\n Object.entries(response.headers).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n }\n\n if (response.redirect) {\n res.redirect(response.redirect);\n } else {\n res.send(response.data);\n }\n }\n } catch (error) {\n captureError(error instanceof Error ? error : new Error(String(error)));\n transaction.end('error');\n\n if (error instanceof ModelenceError) {\n res.status(error.status).send(error.message);\n } else {\n console.error(`Error in route handler: ${req.path}`);\n console.error(error);\n res.status(500).send(String(error));\n }\n }\n };\n}\n","import googleAuthRouter from '@/auth/providers/google';\nimport githubAuthRouter from '@/auth/providers/github';\nimport { runMethod } from '@/methods';\nimport { getResponseTypeMap, sanitizeResult } from '@/methods/serialize';\nimport { createRouteHandler } from '@/routes/handler';\nimport { HttpMethod } from '@/server';\nimport { logInfo } from '@/telemetry';\nimport cookieParser from 'cookie-parser';\nimport express, { Request, Response } from 'express';\nimport http from 'http';\nimport z from 'zod';\nimport type { AppServer } from '../types';\nimport { authenticate } from '../auth';\nimport { getUnauthenticatedRoles } from '../auth/role';\nimport { getMongodbUri } from '../db/client';\nimport { ModelenceError } from '../error';\nimport { Module } from './module';\nimport { isSetupRequired } from './setupStatus';\nimport { ConnectionInfo } from '@/methods/types';\nimport { ServerChannel } from '@/websocket/serverChannel';\nimport { getSecurityConfig } from './securityConfig';\nimport { getWebsocketConfig } from './websocketConfig';\nimport { getConfig } from '@/config/server';\nimport { getLocalSiteUrl } from '@/config/local';\nimport { issueLinkNonce } from '@/auth/session';\n\nfunction getBodyParserMiddleware(config?: {\n json?: boolean | { limit?: string };\n urlencoded?: boolean | { limit?: string; extended?: boolean };\n raw?: boolean | { limit?: string; type?: string | string[] };\n}) {\n const middlewares: express.RequestHandler[] = [];\n\n if (!config) {\n // Default: apply JSON and urlencoded parsing\n middlewares.push(express.json({ limit: '16mb' }));\n middlewares.push(express.urlencoded({ extended: true, limit: '16mb' }));\n return middlewares;\n }\n\n // Handle JSON parsing\n if (config.json !== false) {\n const jsonOptions = typeof config.json === 'object' ? config.json : { limit: '16mb' };\n middlewares.push(express.json(jsonOptions));\n }\n\n // Handle URL-encoded parsing\n if (config.urlencoded !== false) {\n const urlencodedOptions =\n typeof config.urlencoded === 'object' ? config.urlencoded : { extended: true, limit: '16mb' };\n middlewares.push(express.urlencoded(urlencodedOptions));\n }\n\n // Handle raw body parsing\n if (config.raw) {\n const rawOptions = typeof config.raw === 'object' ? config.raw : {};\n const defaultRawOptions = {\n limit: rawOptions.limit || '16mb',\n type: rawOptions.type || '*/*',\n };\n middlewares.push(express.raw(defaultRawOptions));\n }\n\n return middlewares;\n}\n\nfunction registerModuleRoutes(\n app: express.Application,\n modules: Module[],\n cors: express.RequestHandler\n) {\n for (const module of modules) {\n for (const route of module.routes) {\n const { path, handlers, body } = route;\n const middlewares = getBodyParserMiddleware(body);\n\n Object.entries(handlers).forEach(([method, handler]) => {\n // CORS first: it only sets response headers and always calls next(), so\n // it must not sit behind a body parser that could reject the request\n // before the headers are attached.\n app[method as HttpMethod](\n path,\n cors,\n ...middlewares,\n createRouteHandler(method, path, handler)\n );\n });\n }\n }\n}\n\nlet globalProcessListenersRegistered = false;\n\nexport async function startServer(\n server: AppServer,\n {\n combinedModules,\n channels,\n }: {\n combinedModules: Module[];\n channels: ServerChannel[];\n }\n) {\n const app = express();\n\n // Express's proxy-addr implementation walks X-Forwarded-For from the socket\n // toward the client and stops at the first untrusted hop.\n // This is important for IP rate limits: selecting the left-most value would\n // let a caller evade them by prepending an arbitrary address.\n const { trustedProxies } = getSecurityConfig();\n const envTrustedProxies = parseTrustedProxies(process.env.MODELENCE_TRUSTED_PROXIES);\n // Keep the historical trust-all behavior when neither setting is present so\n // upgrading does not collapse every client behind a reverse proxy into one\n // rate-limit identity. Modelence-hosted environments set the trusted proxy\n // ranges before rolling out this framework version.\n const trustProxy = envTrustedProxies ?? trustedProxies;\n app.set('trust proxy', trustProxy === undefined ? true : trustProxy);\n\n app.use(cookieParser());\n\n // CSP and X-Frame-Options belong on every response, SSR pages included, so\n // this one stays global.\n app.use(securityHeadersMiddleware());\n // Preflights only. The matching response headers are attached per-route below,\n // which keeps SSR pages and static assets out of CORS scope.\n app.use(corsPreflightMiddleware());\n\n const cors = corsRouteMiddleware();\n\n // Register module routes first (with per-route body parser config)\n registerModuleRoutes(app, combinedModules, cors);\n\n // Apply global body parsing for remaining routes\n app.use(express.json({ limit: '16mb' }));\n app.use(express.urlencoded({ extended: true, limit: '16mb' }));\n\n // Both OAuth routers register only under this prefix, so one mount covers\n // every route they add, including the callbacks.\n app.use('/api/_internal/auth', cors);\n app.use(googleAuthRouter());\n app.use(githubAuthRouter());\n\n // Browser OAuth linking: set httpOnly cookie so the authToken never travels in a URL.\n app.post('/api/_internal/auth/set-link-cookie', cors, async (req: Request, res: Response) => {\n const { session } = await getCallContext(req, res);\n\n if (!session?.userId) {\n res.status(401).json({ error: 'Not authenticated' });\n return;\n }\n\n res.cookie('oauthLinkToken', session.authToken, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/api/_internal/auth/',\n maxAge: 10 * 60 * 1000, // 10 minutes\n });\n\n res.json({ ok: true });\n });\n\n // React Native OAuth linking: issues a single-use nonce the app puts in the OAuth URL.\n app.post('/api/_internal/auth/issue-link-nonce', cors, async (req: Request, res: Response) => {\n const { session } = await getCallContext(req, res);\n\n if (!session?.userId) {\n res.status(401).json({ error: 'Not authenticated' });\n return;\n }\n\n const nonce = await issueLinkNonce(String(session.userId));\n res.json({ nonce });\n });\n\n app.post('/api/_internal/method/:methodName(*)', cors, async (req: Request, res: Response) => {\n const methodName = req.params.methodName as string;\n const context = await getCallContext(req, res);\n\n try {\n const result = sanitizeResult(await runMethod(methodName, req.body.args, context));\n res.json({\n data: result,\n typeMap: getResponseTypeMap(result),\n });\n } catch (error) {\n handleMethodError(res, methodName, error);\n }\n });\n\n const httpServer = http.createServer(app);\n\n // Keep-alive must exceed the idle timeout of any load balancer in front of the\n // app, or Node destroys pooled sockets the LB still considers live and requests\n // dispatched onto them fail with intermittent 502s. Node's stock default is 5s,\n // below every common LB idle timeout (AWS ALB defaults to 60s). This is the\n // post-response idle timeout only; it never applies to an in-flight request.\n const keepAliveTimeoutMs = Number(process.env.MODELENCE_KEEP_ALIVE_TIMEOUT_MS) || 65000;\n httpServer.keepAliveTimeout = keepAliveTimeoutMs;\n // Must exceed keepAliveTimeout, or requests whose headers are still in flight\n // when keep-alive expires are dropped (nodejs/node#27363).\n httpServer.headersTimeout = keepAliveTimeoutMs + 5000;\n\n await server.init({ httpServer });\n\n if (server.middlewares) {\n app.use(server.middlewares());\n }\n\n app.all('*', (req: Request, res: Response, next) => {\n Promise.resolve(server.handler(req, res)).catch(next);\n });\n\n if (!globalProcessListenersRegistered) {\n globalProcessListenersRegistered = true;\n process.on('unhandledRejection', (reason, promise) => {\n console.error('Unhandled Promise Rejection:');\n console.error(reason instanceof Error ? reason.stack : reason);\n console.error('Promise:', promise);\n });\n\n // Global uncaught exceptions\n process.on('uncaughtException', (error) => {\n console.error('Uncaught Exception:');\n console.error(error.stack); // This gives you the full stack trace\n console.trace('Full application stack:'); // Additional context\n });\n }\n\n const websocketProvider = getWebsocketConfig()?.provider;\n if (websocketProvider) {\n void websocketProvider.init({\n httpServer,\n channels,\n });\n }\n\n const port = process.env.MODELENCE_PORT || process.env.PORT || 3000;\n httpServer.listen(port, () => {\n logInfo(`Application started`, { source: 'app' });\n const siteUrl = getConfig('_system.site.url') || getLocalSiteUrl();\n console.log(`\\nApplication started on ${siteUrl}\\n`);\n\n // The browser shows the setup screen in this state (see\n // client/SetupScreen); say the same thing here for anyone who only sees\n // the terminal — a developer glancing at logs, or a coding agent running\n // the dev server.\n if (isSetupRequired()) {\n console.log(\n 'This project is not connected to a backend yet, so the app serves setup instructions instead of its UI.\\n' +\n 'To connect it to Modelence Cloud, run `npx modelence@latest setup` and restart the dev server.\\n'\n );\n }\n });\n}\n\nexport async function getCallContext(req: Request, res: Response | null = null) {\n const path = (req.path ?? req.url ?? '').split('?')[0];\n\n const isOAuthCallback = path.startsWith('/api/_internal/auth/') && path.endsWith('/callback');\n\n const body = (req.body ?? {}) as Record<string, unknown>;\n\n const authToken = z\n .string()\n .nullish()\n .transform((val) => val ?? null)\n .parse(\n req.cookies.authToken ||\n (isOAuthCallback ? req.cookies.oauthLinkToken : null) ||\n body.authToken\n );\n\n const clientInfo = z\n .object({\n screenWidth: z.number(),\n screenHeight: z.number(),\n windowWidth: z.number(),\n windowHeight: z.number(),\n pixelRatio: z.number(),\n orientation: z.string().nullable(),\n })\n .nullish()\n .parse(body.clientInfo) ?? {\n screenWidth: 0,\n screenHeight: 0,\n windowWidth: 0,\n windowHeight: 0,\n pixelRatio: 1,\n orientation: null,\n };\n\n const connectionInfo: ConnectionInfo = {\n ip: getClientIp(req),\n userAgent: req.get('user-agent'),\n acceptLanguage: req.get('accept-language'),\n referrer: req.get('referrer'),\n baseUrl: getRequestBaseUrl(req),\n };\n\n const hasDatabase = Boolean(getMongodbUri());\n if (hasDatabase) {\n const { session, user, roles } = await authenticate(authToken);\n return {\n clientInfo,\n connectionInfo,\n session,\n user,\n roles,\n req,\n res,\n };\n }\n\n return {\n clientInfo,\n connectionInfo,\n session: null,\n user: null,\n roles: getUnauthenticatedRoles(),\n req,\n res,\n };\n}\n\nfunction handleMethodError(res: Response, methodName: string, error: unknown) {\n // TODO: add an option to silence these error console logs, especially when Elastic logs are configured\n\n if (error instanceof ModelenceError) {\n if (error.status >= 500 && error.status < 600) {\n console.error(`Error calling ${methodName}:`, error);\n }\n // Surface a machine-readable code (when present) via a header so clients can\n // branch on the error kind without parsing the human-readable message. The\n // response body is left as the message text to preserve the existing format.\n if (error.code) {\n res.setHeader('X-Modelence-Error-Code', error.code);\n }\n res.status(error.status).send(error.message);\n return;\n }\n\n if (error instanceof Error && error?.constructor?.name === 'ZodError' && 'errors' in error) {\n let errorMessage = '';\n try {\n errorMessage = parseZodError(error as z.ZodError);\n } catch (parsingError) {\n console.error(`Error parsing Zod error in ${methodName}:`, parsingError);\n errorMessage = 'Validation failed';\n }\n res.status(400).send(errorMessage);\n return;\n }\n\n console.error(`Error calling ${methodName}:`, error);\n res.status(500).send(error instanceof Error ? error.message : String(error));\n}\n\nfunction parseZodError(zodError: z.ZodError): string {\n const flattened = zodError.flatten();\n const fieldMessages = Object.entries(flattened.fieldErrors).map(\n ([key, errors]) => `${key}: ${(errors ?? []).join(', ')}`\n );\n const formMessages = flattened.formErrors;\n const allMessages = [...fieldMessages, ...formMessages].filter(Boolean);\n return allMessages.join('; ');\n}\n\nfunction securityHeadersMiddleware(): express.RequestHandler {\n const { frameAncestors } = getSecurityConfig();\n const hasCustomAncestors = frameAncestors && frameAncestors.length > 0;\n const ancestors = hasCustomAncestors ? [\"'self'\", ...frameAncestors].join(' ') : \"'self'\";\n\n return (_req, res, next) => {\n res.setHeader('Content-Security-Policy', `frame-ancestors ${ancestors}`);\n // X-Frame-Options only supports DENY and SAMEORIGIN (ALLOW-FROM is deprecated).\n // When custom ancestors are configured, only CSP frame-ancestors can express that,\n // so we omit X-Frame-Options to avoid conflicting with the CSP directive.\n if (!hasCustomAncestors) {\n res.setHeader('X-Frame-Options', 'SAMEORIGIN');\n }\n next();\n };\n}\n\n/**\n * Applies the CORS response headers for a single request, returning whether the\n * request's Origin is allowed.\n *\n * Shared by the preflight and per-route middlewares so an allowed origin gets\n * exactly the same headers whether the browser is preflighting or making the\n * real call — a mismatch between the two is invisible in tests and shows up as\n * an intermittent browser-only failure.\n */\nfunction applyCorsHeaders(\n req: express.Request,\n res: express.Response,\n allowed: Set<string>\n): boolean {\n // Set before the match check, not inside it: a response sent to a\n // disallowed origin (or to a request with no Origin at all) still depends on\n // Origin, and without Vary a CDN or shared proxy may cache that\n // header-less response and later replay it to an allowed origin.\n // res.vary appends and dedupes, so a Vary set by static/compression\n // middleware further down the stack survives — setHeader would clobber it.\n res.vary('Origin');\n\n const origin = req.headers.origin;\n const isAllowed = typeof origin === 'string' && allowed.has(origin);\n if (!isAllowed) {\n return false;\n }\n\n // Echo the matched origin rather than '*': a wildcard is rejected by\n // browsers on credentialed requests, which the cookie-based web flows use.\n res.setHeader('Access-Control-Allow-Origin', origin);\n res.setHeader('Access-Control-Allow-Credentials', 'true');\n // Reflect what the preflight asked for, falling back to the only header\n // method calls send. A custom route taking e.g. Authorization would\n // otherwise be blocked before its handler ever ran.\n const requestedHeaders = req.headers['access-control-request-headers'];\n res.setHeader(\n 'Access-Control-Allow-Headers',\n typeof requestedHeaders === 'string' ? requestedHeaders : 'Content-Type'\n );\n // Covers every verb RouteDefinition's HttpMethod allows, so a custom\n // route is not silently blocked by a preflight the framework rejects.\n res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS');\n // Browsers expose only a small safelist of response headers to JS. Without\n // this the error-code header is hidden cross-origin, so MethodError.code\n // silently becomes undefined and clients cannot branch on the error kind.\n res.setHeader('Access-Control-Expose-Headers', 'X-Modelence-Error-Code');\n return true;\n}\n\n/**\n * Answers CORS preflights. Mounted globally, unlike the per-route header\n * middleware: a preflight must be answered before route dispatch, and Express\n * does not reliably match `OPTIONS /todos` against a `.get('/todos')`\n * registration. Scoping this per-route would leave those preflights to fall\n * through to the SSR catch-all, which answers them with HTML.\n *\n * It only ever handles preflights, so it never adds headers to a real response —\n * that stays the per-route middleware's job, which is what keeps SSR and static\n * responses out of scope.\n */\nfunction corsPreflightMiddleware(): express.RequestHandler {\n const { allowedOrigins } = getSecurityConfig();\n const allowed = new Set(allowedOrigins ?? []);\n\n return (req, res, next) => {\n // Opt-in only. With no configured origins this is a no-op, so deployments\n // that add CORS at a proxy/router keep exactly one Access-Control-Allow-Origin\n // header — a duplicate is invalid and browsers reject the response outright.\n //\n // A preflight is OPTIONS *plus* Access-Control-Request-Method. Matching on\n // the method alone would swallow every other OPTIONS request: HttpMethod\n // lets a module register an `options` (or `all`/`use`) handler, and this is\n // mounted ahead of those routes, so a plain OPTIONS must fall through to\n // them rather than be answered here.\n const isPreflight =\n req.method === 'OPTIONS' && typeof req.headers['access-control-request-method'] === 'string';\n if (allowed.size === 0 || !isPreflight) {\n next();\n return;\n }\n\n const isAllowed = applyCorsHeaders(req, res, allowed);\n // Allow-Headers is reflected from the request, so the preflight result\n // depends on this header too and must not be cached across requests that\n // ask for different ones.\n res.vary('Access-Control-Request-Headers');\n if (isAllowed) {\n // Every method call sends Content-Type: application/json, so every one\n // is preflighted. Without Max-Age browsers re-preflight constantly\n // (Chrome defaults to 5s), doubling the request count. 600s is under\n // Chrome's 7200s cap and Firefox's 86400s cap, so it applies as given.\n res.setHeader('Access-Control-Max-Age', '600');\n }\n res.sendStatus(isAllowed ? 204 : 403);\n };\n}\n\n/**\n * Adds CORS headers to a real (non-preflight) response.\n *\n * Attached to the app's route surface — module routes and the framework's own\n * API routes — rather than mounted globally, so an allowlisted origin can call\n * the API without also being able to read SSR pages and static assets with\n * credentials. The scope is derived from the routes actually registered rather\n * than matched by path pattern: module routes carry no framework-imposed prefix\n * (the docs' example mounts `/todos` at the root), so no pattern could express\n * \"the API\" without silently missing user-defined routes.\n */\nfunction corsRouteMiddleware(): express.RequestHandler {\n const { allowedOrigins } = getSecurityConfig();\n const allowed = new Set(allowedOrigins ?? []);\n\n return (req, res, next) => {\n if (allowed.size > 0) {\n applyCorsHeaders(req, res, allowed);\n }\n next();\n };\n}\n\nfunction getRequestBaseUrl(req: Request): string {\n // Behind a reverse proxy the inbound Host header / connection protocol can\n // reflect the internal container address rather than the public URL. Honor\n // the X-Forwarded-Host / X-Forwarded-Proto headers when present (the first\n // value in each comma-separated list is the original client-facing value),\n // falling back to the direct request values otherwise.\n const forwardedHost = req.headers['x-forwarded-host'];\n const host =\n (Array.isArray(forwardedHost) ? forwardedHost[0] : forwardedHost?.split(',')[0])?.trim() ||\n req.get('host');\n\n // Do not parse X-Forwarded-Proto directly: `req.protocol` already resolves it\n // through the `trust proxy` setting, so an untrusted caller cannot influence\n // the base URL used for auth callbacks and emails.\n return `${req.protocol}://${host}`;\n}\n\n/**\n * Parses the comma-separated `MODELENCE_TRUSTED_PROXIES` value into the array\n * form Express expects. Returns undefined when unset or empty so the caller can\n * fall back to the `startApp()` security config.\n */\nfunction parseTrustedProxies(value: string | undefined): string[] | undefined {\n const entries = value\n ?.split(',')\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0);\n\n return entries?.length ? entries : undefined;\n}\n\n/**\n * Whether the immediate peer on the socket is one of the configured trusted\n * proxies. Only the socket address is consulted: it is the one value in the\n * request a caller cannot forge.\n */\nfunction isPeerTrusted(req: Request): boolean {\n const peerIp = req.socket?.remoteAddress;\n if (!peerIp) {\n return false;\n }\n\n // Express compiles the `trust proxy` setting into this predicate. It is\n // absent when the app was never configured (older embeddings, tests), in\n // which case we fall back to the `trust proxy` value itself.\n const trust = req.app?.get?.('trust proxy fn');\n if (typeof trust !== 'function') {\n return req.app?.get?.('trust proxy') !== false;\n }\n\n return Boolean(trust(peerIp, 0));\n}\n\nfunction getClientIp(req: Request): string | undefined {\n // Cloudflare and similar proxies expose the originating client in a\n // single-value header. Prefer it over the X-Forwarded-For chain, which\n // Cloudflare appends to rather than overwrites, but only when the peer is a\n // trusted proxy - otherwise a direct caller could set it themselves.\n const { clientIpHeader } = getSecurityConfig();\n if (clientIpHeader && isPeerTrusted(req)) {\n const forwardedIp = req.headers[clientIpHeader.toLowerCase()];\n const clientIp = (Array.isArray(forwardedIp) ? forwardedIp[0] : forwardedIp)?.trim();\n if (clientIp) {\n return normalizeIp(clientIp);\n }\n }\n\n // `req.ip` only incorporates X-Forwarded-For according to the application's\n // `trust proxy` setting. Never parse the header directly: its left-most value\n // is controlled by the caller unless every ingress proxy overwrites it.\n const directIp = req.ip || req.socket?.remoteAddress;\n if (directIp) {\n return normalizeIp(directIp);\n }\n\n return undefined;\n}\n\nfunction normalizeIp(ip: string): string {\n // Remove IPv6-to-IPv4 mapping prefix\n return ip.startsWith('::ffff:') ? ip.substring(7) : ip;\n}\n"]}
1
+ {"version":3,"sources":["../src/system/index.ts","../src/db/client.ts","../src/websocket/socketio/server.ts","../src/auth/disposableEmails.ts","../src/auth/validators.ts","../src/auth/utils.ts","../src/app/emailConfig.ts","../src/app/authConfig.ts","../src/auth/login.ts","../src/rate-limit/db.ts","../src/rate-limit/rules.ts","../src/auth/profile.ts","../src/auth/types.ts","../src/auth/unlinkOAuthProvider.ts","../src/auth/templates/emailVerficationTemplate.ts","../src/auth/verification.ts","../src/auth/signup.ts","../src/auth/resetPassword.ts","../src/auth/templates/magicLinkTemplate.ts","../src/auth/magicLink.ts","../src/auth/loginWithOAuth.ts","../src/auth/user.ts","../src/config/local.ts","../src/app/instance.ts","../src/app/backendApi.ts","../src/config/sync.ts","../src/lock/db.ts","../src/lock/helpers.ts","../src/cron/jobs.ts","../src/data/resolveStores.ts","../src/lock/module.ts","../src/migration/db.ts","../src/migration/index.ts","../src/rate-limit/index.ts","../src/files/index.ts","../src/ssr/escapeJsonForScript.ts","../src/viteServer.ts","../src/app/managedEmailProvider.ts","../src/app/securityConfig.ts","../src/app/websocketConfig.ts","../src/app/index.ts","../src/auth/deleteUser.ts","../src/websocket/serverChannel.ts","../src/app/email.ts","../src/auth/providers/mobileRedirect.ts","../src/auth/providers/oauthErrorRedirect.ts","../src/auth/providers/oauthCommon.ts","../src/auth/providers/google.ts","../src/auth/providers/github.ts","../src/routes/handler.ts","../src/app/server.ts"],"names":["system_default","Module","isSetupRequired","client","connect","mongodbUri","getMongodbUri","maxPoolSize","MongoClient","package_default","err","getClient","socketServer","COLLECTION","ADAPTER_TTL_SECONDS","init","httpServer","channels","mongodbClient","isMultiInstance","getConfig","mongoCollection","error","retryError","SocketServer","createAdapter","socket","next","token","authenticate","handleLiveQueryDisconnect","channelName","category","authorized","channel","payload","handleSubscribeLiveQuery","handleUnsubscribeLiveQuery","broadcast","id","data","server_default","isDisposableEmail","email","emailParts","domain","dbDisposableEmailDomains","updateDisposableEmailListCron","time","response","domains","now","batchSize","i","batch","MIN_HANDLE_LENGTH","MAX_HANDLE_LENGTH","HANDLE_REGEX","MIN_PASSWORD_LENGTH","MAX_PASSWORD_LENGTH","MAX_EMAIL_LENGTH","trimmedNonEmptyString","opts","z","trimmedOptionalString","val","handleSchema","profileFieldsSchema","validateProfileFields","fields","result","issue","path","msg","ValidationError","validatePassword","value","validateEmail","validateHandle","isDuplicateEmailError","MongoServerError","resolveUrl","baseUrl","configuredUrl","serializeUserForClient","userDoc","findAvailableHandle","baseHandle","truncatedBase","usersCollection","MAX_SUFFIX_VALUE","suffix","suffixStr","candidate","MAX_RANDOM_ATTEMPTS","attempt","randomSuffix","randomBytes","resolveUniqueHandle","rawHandle","throwOnConflict","handle","emailConfig","setEmailConfig","newEmailConfig","getEmailConfig","authConfig","setAuthConfig","newAuthConfig","getAuthConfig","isEmailVerificationRequired","SALT_ROUNDS","DUMMY_PASSWORD_HASH","bcrypt","handleLoginWithPassword","args","user","session","connectionInfo","res","ip","consumeRateLimit","password","passwordHash","hashToCompare","isValidPassword","incorrectCredentialsError","e","AuthError","setSessionUser","setAuthTokenCookie","handleLogout","clearSessionUser","clearAuthTokenCookie","dbRateLimits","Store","schema","allRules","initRateLimits","rateLimits","options","bucket","type","message","rules","rule","createError","RateLimitError","checkRateLimitRule","createRateLimitError","filter","record","currentWindowStart","count","modifier","getCount","prevWindowStart","currentWindowCount","prevWindowCount","prevWindowWeight","weight","getOwnProfile","props","profile","handleUpdateProfile","rawProps","isHandleUnchanged","_ignoredHandle","propsWithoutHandle","update","setFields","unsetFields","key","mongoUpdate","clearedFields","SUPPORTED_OAUTH_PROVIDERS","handleUnlinkOAuthProvider","provider","methods","otherMethods","otherMethodGuard","emailVerificationTemplate","name","verificationUrl","USER_COLLATION","verifyEmailToken","tokenDoc","emailVerificationTokensCollection","updatedUserDoc","existingEmailDoc","handleVerifyEmail","params","emailVerifiedRedirectUrl","authToken","createSession","sendVerificationEmail","userId","requestBaseUrl","emailProvider","verificationToken","expiresAt","htmlTemplate","textContent","htmlToText","resendVerificationResponse","handleResendEmailVerification","emailDoc","handleSignupWithPassword","signupProps","firstName","lastName","avatarUrl","profileFields","resolvedHandle","generated","hash","userDocument","RESET_PASSWORD_COOKIE","RESET_PASSWORD_COOKIE_PATH","RESET_PASSWORD_COOKIE_OPTIONS","findResetTokenDoc","rawToken","resetPasswordTokensCollection","hashToken","claimResetTokenById","defaultPasswordResetTemplate","resetUrl","passwordResetSent","handleSendResetPasswordToken","resetToken","createdAt","handleResetPasswordLanding","resetPasswordUrl","resetTokenDoc","handleResetPassword","context","cookieToken","clearCookie","invalidateAllUserSessions","userEmails","address","magicLinkTokensCollection","magicLinkTemplate","magicLinkUrl","code","MAGIC_LINK_COOKIE","MAGIC_LINK_COOKIE_PATH","MAGIC_LINK_COOKIE_OPTIONS","MAGIC_LINK_EXPIRY_MINUTES","ONE_TIME_CODE_LENGTH","MAX_CODE_ATTEMPTS","fireAuthHook","invoke","hookError","isMagicLinkEnabled","isMagicLinkSignupEnabled","findMagicLinkTokenDoc","claimMagicLinkTokenById","restoreClaimedMagicLinkToken","claimedToken","restoreError","magicLinkSent","handleSendMagicLink","fromAddress","magicLinkToken","oneTimeCode","randomInt","handleMagicLinkLanding","magicLinkPageUrl","authenticateMagicLinkUser","signupEnabled","isSignupAttempt","upsertResult","upsertMagicLinkUser","isNew","currentUserDoc","wasUnverified","hookName","hook","canInsert","runUpsert","doc","handleLoginWithMagicLink","req","handleLoginWithOneTimeCode","invalidCodeError","handleLoginWithOAuth","codeVerifier","claimed","consumeOAuthExchangeCode","ObjectId","ruleKey","defaultAuthRateLimits","collectOverrides","config","overrides","buckets","buildAuthRateLimits","defaults","overrideByKey","merged","consumed","def","override","user_default","localConfigMap","formatLocalConfigValue","numValue","getConfigsFromEnvMap","configMap","configSchema","configs","envVar","configKey","configSchemaEntry","getLocalSiteUrl","port","getLocalConfigs","variant","instanceId","getMachineKey","dir","os","file","existing","fs","randomUUID","getInstanceId","createHash","isDevRuntime","runtime","isLocalRuntime","getContainerId","connectCloudBackend","cronJobsMetadata","stores","roles","containerId","takeover","dataStores","store","callCloudApi","cloudError","detail","fetchConfigs","syncStatus","endpoint","method","MODELENCE_SERVICE_ENDPOINT","MODELENCE_SERVICE_TOKEN","parsed","messageDetail","errorField","isSyncing","SYNC_INTERVAL","startConfigSync","sync","syncConfig","loadRemoteConfigs","loadConfigs","getSchema","locksCollection","lockCache","DEFAULT_CACHE_DURATION","INSTANCE_ID","DEFAULT_LOCK_DURATION","lockHeartbeats","isDuplicateKeyError","MongoError","hasKeyPatternField","field","shouldFallbackToLegacyStrategy","resource","existingLock","tryAcquireLockById","staleThresholdDate","deleteLegacyLock","selector","stopLockHeartbeat","heartbeatKey","heartbeat","startLockHeartbeat","lockDuration","heartbeatInterval","existingHeartbeat","scheduleRefresh","acquireLock","isLockAcquired","logDebug","successfulLockCacheDuration","failedLockCacheDuration","bypassCache","acquireLockById","releaseLock","cronJobs","cronJobsInterval","cronJobsCollection","defineCronJob","alias","description","interval","timeout","handler","startCronJobs","aliasList","aliasSelector","cronJobRecords","job","tickCronJobs","state","timeoutError","captureError","runCronJob","transaction","startTransaction","handleCronJobCompletion","getCronJobsMetadata","jobs_default","resolveStores","uniqueStores","rootToTail","root","effectiveStores","nameToRoot","tail","module_default","dbMigrations","runMigrations","migrations","lockMode","logInfo","versions","version","existingVersions","existingVersionSet","pendingMigrations","outputStr","maxSize","truncatedOutput","startMigrations","migration_default","rate_limit_default","getUploadUrl","filePath","contentType","visibility","deleteFile","downloadFile","getFileUrl","files_default","escapeJsonForScript","json","ch","CLIENT_BUILD_DIR","SSR_BUILD_DIR","SSR_ENTRY_VIRTUAL_PATH","ROOT_PLACEHOLDER_REGEX","HEAD_CLOSE_TAG","ViteServer","createServer","ssrEntryPath","installSsrCallMethodTransport","staticOptions","staticFolders","express","isDocumentRequest","template","snapshot","renderSsrTreeStream","getCallContext","cssModule","callContext","cssAssets","sendEarlyHints","sessionState","pipe","getQueryState","rawPrelude","rootOpenTag","rawEpilogue","splitTemplateAtRoot","prelude","injectStylesheets","url","templatePath","_getSsrSnapshot","accept","pathname","lastSegment","dotIndex","ext","match","epilogue","linksHtml","headCloseIndex","links","target","loadUserViteConfig","appDir","loadConfigFromFile","safelyMergeConfig","baseConfig","userConfig","mergedConfig","mergeConfig","seenPlugins","plugin","pluginName","eslintConfigFile","plugins","reactPlugin","modelenceAssetPlugin","eslintPlugin","defineConfig","viteServer","extractFromName","from","normalizeAddressList","buildRequestBody","describeManagedEmailError","body","managedEmailProvider","securityConfig","normalizeOrigin","entry","trimmed","normalizeAllowedOrigins","allowedOrigins","setSecurityConfig","newSecurityConfig","validated","getSecurityConfig","websocketConfig","setWebsocketConfig","newWebsocketConfig","getWebsocketConfig","startApp","modules","defaultRoles","server","auth","security","websocket","ssr","dotenv","hasRemoteBackend","trackAppStart","module","systemModules","session_default","combinedModules","markAppStarted","initSystemMethods","initCustomMethods","initRoles","getConfigSchema","setSchema","rawStores","getStores","getChannels","defineCronJobs","getRateLimits","storesToInit","environmentId","appAlias","environmentAlias","telemetry","setMetadata","allStoresToInit","initStores","createIndexesAndMigrationsWithLock","initMetrics","startServer","createQuery","createMutation","_createSystemQuery","_createSystemMutation","warnIndexCreationFailure","storeName","MIGRATIONS_LOCK_RESOURCE","blockingStores","backgroundStores","createStoreIndexes","backgroundIndexCreationPromise","migrationPromise","backgroundIndexesResult","migrationResult","reconcileMode","absoluteKey","cronAlias","cronJobParams","serviceEndpoint","appDetails","getAppDetails","modelencePackageJson","packageJsonPath","packageJsonContent","clearTokens","disableUser","deleteUser","ServerChannel","canAccessChannel","websocketProvider","logError","sendEmail","FORBIDDEN_SCHEMES","parseRedirectUrl","getAllowedMobileRedirectUrls","fromConfig","fromAuthConfig","isAllowedMobileRedirectUrl","hasUserinfo","allowed","normalizePath","OUTCOME_PARAMS","buildMobileRedirect","redirectUri","ERROR_PARAMS","buildOAuthErrorRedirect","siteUrl","toOAuthOutcome","isBoundMobileOutcome","outcome","shouldFireLoginHooks","resolveUserIdFromLinkNonce","nonce","consumeLinkNonce","sendOAuthError","statusCode","errorMessage","errorCode","redirectUrl","html","authenticateUser","issueOAuthExchangeCode","handleExistingProviderLogin","userData","existingUser","handleExistingEmailLogin","existingUserByEmail","profileUpdate","updatedUser","handleNewUserSignup","newUser","getRedirectUri","handleOAuthUserAuthentication","clearOAuthLinkCookie","safelyCallHook","encodeOAuthState","mode","linkedUserId","platform","codeChallenge","decodeOAuthState","storedState","stateValue","encodedRedirectUri","validateOAuthStateAndGetMode","stateCookieName","decoded","mobileOutcome","resolveDecodedMobileOutcome","resolveMobileOutcomeFromCookie","describeRejectedRedirectUri","protocol","resolveMobileRedirectRequest","rawChallenge","hasValidChallenge","prepareOAuthInitiation","mobileRequest","mobileRedirectUri","handleOAuthProviderLink","resolvedUserId","providerField","currentUser","existingProviderId","validateOAuthCode","exchangeCodeForToken","clientId","clientSecret","tokenResponse","fetchGoogleUserInfo","accessToken","userInfoResponse","handleGoogleAuthenticationCallback","stateResult","googleClientId","googleClientSecret","tokenData","googleUser","getRouter","googleAuthRouter","Router","checkGoogleEnabled","_req","googleEnabled","initiation","authUrl","google_default","fetchGitHubUserInfo","fetchGitHubUserEmails","getGitHubUserEmail","githubUser","handleGitHubAuthenticationCallback","githubClientId","githubClientSecret","githubEmail","nameParts","githubAuthRouter","checkGitHubEnabled","githubEnabled","githubScopes","scopes","s","github_default","createRouteHandler","redactSensitive","ModelenceError","getBodyParserMiddleware","middlewares","jsonOptions","urlencodedOptions","rawOptions","defaultRawOptions","registerModuleRoutes","app","cors","route","handlers","globalProcessListenersRegistered","trustedProxies","trustProxy","parseTrustedProxies","cookieParser","securityHeadersMiddleware","corsPreflightMiddleware","corsRouteMiddleware","issueLinkNonce","methodName","sanitizeResult","runMethod","getResponseTypeMap","handleMethodError","http","keepAliveTimeoutMs","reason","promise","isOAuthCallback","clientInfo","getClientIp","getRequestBaseUrl","getUnauthenticatedRoles","parseZodError","parsingError","zodError","flattened","fieldMessages","errors","formMessages","frameAncestors","hasCustomAncestors","ancestors","applyCorsHeaders","origin","requestedHeaders","isPreflight","isAllowed","forwardedHost","host","entries","isPeerTrusted","peerIp","trust","clientIpHeader","forwardedIp","clientIp","normalizeIp","directIp"],"mappings":"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","file":"chunk-IQOUXFAM.js","sourcesContent":["import { Module } from '../app/module';\nimport { isSetupRequired } from '../app/setupStatus';\n\nexport default new Module('_system', {\n queries: {\n /*\n Polled by the built-in setup screen (dev only) so it can dismiss itself\n once the project is connected and the dev server restarted. Public and\n unauthenticated by design — it exposes a single boolean that is always\n false outside development.\n */\n setupStatus: async () => ({ setupRequired: isSetupRequired() }),\n },\n configSchema: {\n mongodbUri: {\n type: 'secret',\n isPublic: false,\n default: '',\n },\n mongodbPoolSize: {\n type: 'number',\n isPublic: false,\n default: 10,\n },\n 'env.type': {\n type: 'string',\n isPublic: true,\n default: '',\n },\n 'site.url': {\n type: 'string',\n isPublic: true,\n default: '',\n },\n multiInstance: {\n type: 'boolean',\n isPublic: false,\n default: false,\n },\n },\n});\n","import { MongoClient } from 'mongodb';\nimport systemModule from '../system';\nimport packageJson from '../../package.json';\n\nlet client: MongoClient | null = null;\n\nexport async function connect() {\n if (client) return client;\n\n const mongodbUri = getMongodbUri();\n if (!mongodbUri) {\n throw new Error('MongoDB URI is not set');\n }\n\n const maxPoolSize = systemModule.getConfig('mongodbPoolSize');\n\n client = new MongoClient(mongodbUri, {\n driverInfo: {\n name: 'Modelence',\n version: packageJson.version,\n },\n ignoreUndefined: true,\n maxPoolSize,\n });\n\n try {\n // Connect the client to the server\n await client.connect();\n // Send a ping to confirm a successful connection\n await client.db('admin').command({ ping: 1 });\n console.log('Pinged your deployment. You successfully connected to MongoDB!');\n return client;\n } catch (err) {\n console.error(err);\n client = null;\n throw err;\n }\n}\n\nexport function getMongodbUri() {\n return systemModule.getConfig('mongodbUri') || undefined;\n}\n\nexport function getClient() {\n return client;\n}\n\n// export async function closeConnection() {\n// if (client) {\n// await client.close();\n// client = null;\n// }\n// }\n","import { Server } from 'http';\nimport { Server as SocketServer, Socket } from 'socket.io';\nimport { createAdapter } from '@socket.io/mongo-adapter';\nimport { authenticate } from '@/auth';\nimport { getConfig } from '@/config/server';\nimport { getClient } from '@/db/client';\nimport { WebsocketServerProvider } from '../types';\nimport { ServerChannel } from '../serverChannel';\nimport {\n handleSubscribeLiveQuery,\n handleUnsubscribeLiveQuery,\n handleLiveQueryDisconnect,\n} from '@/live-query';\nimport type { Collection, Document } from 'mongodb';\n\nlet socketServer: SocketServer | null = null;\n\nconst COLLECTION = '_modelenceSocketio';\nconst ADAPTER_TTL_SECONDS = 60;\n\nexport async function init({\n httpServer,\n channels,\n}: {\n httpServer: Server;\n channels: ServerChannel[];\n}) {\n const mongodbClient = getClient();\n const isMultiInstance = Boolean(getConfig('_system.multiInstance'));\n\n console.log('Initializing Socket.IO server...');\n\n let mongoCollection: Collection<Document> | null = null;\n\n if (isMultiInstance && mongodbClient) {\n mongoCollection = mongodbClient.db().collection(COLLECTION);\n\n try {\n await mongoCollection.createIndex(\n { createdAt: 1 },\n { expireAfterSeconds: ADAPTER_TTL_SECONDS, background: true }\n );\n } catch (error: unknown) {\n if (error instanceof Error && 'code' in error && (error as { code: number }).code === 85) {\n try {\n await mongoCollection.dropIndex('createdAt_1');\n await mongoCollection.createIndex(\n { createdAt: 1 },\n { expireAfterSeconds: ADAPTER_TTL_SECONDS, background: true }\n );\n } catch (retryError: unknown) {\n console.error(\n 'Failed to recreate index on MongoDB collection for Socket.IO:',\n retryError\n );\n }\n } else {\n console.error('Failed to create index on MongoDB collection for Socket.IO:', error);\n }\n }\n }\n\n socketServer = new SocketServer(httpServer, {\n cors: {\n origin: '*',\n methods: ['GET', 'POST'],\n },\n adapter: mongoCollection ? createAdapter(mongoCollection) : undefined,\n transports: ['websocket'],\n perMessageDeflate: false,\n });\n\n socketServer.on('error', (error) => {\n console.error('Socket.IO error:', error);\n });\n\n socketServer.use(async (socket, next) => {\n const token = socket.handshake.auth.token;\n\n try {\n socket.data = await authenticate(token);\n next();\n } catch (error) {\n next(error instanceof Error ? error : new Error(String(error)));\n }\n });\n\n socketServer.on('connection', (socket: Socket) => {\n socket.on('disconnect', () => {\n handleLiveQueryDisconnect(socket);\n });\n\n socket.on('joinChannel', async (channelName: string) => {\n const [category] = channelName.split(':');\n let authorized = false;\n\n for (const channel of channels) {\n if (channel.category === category) {\n if (!channel.canAccessChannel || (await channel.canAccessChannel(socket.data))) {\n void socket.join(channelName);\n authorized = true;\n socket.emit('joinedChannel', channelName);\n }\n break; // Found matching channel - stop searching\n }\n }\n\n if (!authorized) {\n socket.emit('joinError', { channel: channelName, error: 'Access denied' });\n }\n });\n\n socket.on('leaveChannel', (channelName: string) => {\n void socket.leave(channelName);\n console.log(`User ${socket.id} left channel ${channelName}`);\n socket.emit('leftChannel', channelName);\n });\n\n socket.on('subscribeLiveQuery', (payload) => handleSubscribeLiveQuery(socket, payload));\n socket.on('unsubscribeLiveQuery', (payload) => handleUnsubscribeLiveQuery(socket, payload));\n });\n\n console.log('Socket.IO server initialized');\n}\n\nfunction broadcast<T>({ category, id, data }: { category: string; id: string; data: T }) {\n socketServer?.to(`${category}:${id}`).emit(category, data);\n}\n\nexport default {\n init,\n broadcast,\n} as WebsocketServerProvider;\n","import { time } from '../time';\nimport { dbDisposableEmailDomains } from './db';\n\nexport async function isDisposableEmail(email: string): Promise<boolean> {\n const emailParts = email.toLowerCase().trim().split('@');\n if (emailParts.length !== 2) {\n return false;\n }\n\n const domain = emailParts[1];\n const result = await dbDisposableEmailDomains.findOne({ domain });\n return Boolean(result);\n}\n\nexport const updateDisposableEmailListCron = {\n interval: time.days(1),\n async handler() {\n const response = await fetch(\n 'https://disposable.github.io/disposable-email-domains/domains.txt'\n );\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const domainsText = await response.text();\n\n const domains = domainsText\n .split('\\n')\n .map((domain) => domain.trim().toLowerCase())\n .filter((domain) => domain.length > 0);\n\n const now = new Date();\n\n // Insert domains in batches to avoid overwhelming the database\n const batchSize = 500;\n for (let i = 0; i < domains.length; i += batchSize) {\n const batch = domains.slice(i, i + batchSize);\n\n try {\n await dbDisposableEmailDomains.insertMany(\n batch.map((domain) => ({\n domain,\n addedAt: now,\n }))\n );\n } catch (error: unknown) {\n // MongoDB throws BulkWriteError when some documents are duplicates\n if (\n error &&\n typeof error === 'object' &&\n 'name' in error &&\n error.name === 'MongoBulkWriteError'\n ) {\n // console.warn(`Error inserting batch starting at index ${i}:`, error.message);\n }\n }\n }\n },\n};\n","import { z } from 'zod';\nimport { UpdateProfileProps } from '../methods/types';\nimport { ValidationError } from '../error';\n\nexport const MIN_HANDLE_LENGTH = 3;\nexport const MAX_HANDLE_LENGTH = 50;\nexport const HANDLE_REGEX = /^[a-zA-Z0-9_-]+$/;\n\nexport const MIN_PASSWORD_LENGTH = 8;\nexport const MAX_PASSWORD_LENGTH = 128;\n\nexport const MAX_EMAIL_LENGTH = 254;\n\n// Reusable string validators\nconst trimmedNonEmptyString = (opts: { min?: number; max: number }) =>\n z\n .string()\n .trim()\n .min(opts.min ?? 1, { message: `must be at least ${opts.min ?? 1} characters` })\n .max(opts.max, { message: `must be at most ${opts.max} characters` });\n\nconst trimmedOptionalString = (opts: { max: number }) =>\n z\n .string()\n .trim()\n .max(opts.max, { message: `must be at most ${opts.max} characters` })\n .transform((val) => (val === '' ? undefined : val))\n .optional();\n\nconst handleSchema = trimmedNonEmptyString({\n min: MIN_HANDLE_LENGTH,\n max: MAX_HANDLE_LENGTH,\n}).regex(HANDLE_REGEX, {\n message: 'must contain only letters, numbers, underscores, and hyphens',\n});\n\n// Base schema (used for both full & partial validation)\nconst profileFieldsSchema = z\n .object({\n firstName: trimmedOptionalString({ max: 50 }),\n lastName: trimmedOptionalString({ max: 50 }),\n avatarUrl: trimmedOptionalString({ max: 400 }),\n handle: handleSchema,\n })\n .strict();\n\n/**\n * Validates and trims profile fields against the defined rules.\n *\n * For each field present in `fields`:\n * - Trims whitespace\n * - Sets fields to `undefined` when cleared (empty string)\n * - Enforces min/max length constraints\n *\n * @returns An object containing only the validated, trimmed fields that were provided.\n */\nexport function validateProfileFields(\n fields: Partial<UpdateProfileProps>\n): Partial<UpdateProfileProps> {\n const result = profileFieldsSchema.partial().safeParse(fields);\n\n if (!result.success) {\n const issue = result.error.issues[0];\n const path = issue.path.join('.');\n const msg = path ? `${path}: ${issue.message}` : issue.message;\n throw new ValidationError(msg);\n }\n\n return result.data;\n}\n\nexport function validatePassword(value: string) {\n return z\n .string()\n .min(MIN_PASSWORD_LENGTH, {\n message: `Password must contain at least ${MIN_PASSWORD_LENGTH} characters`,\n })\n .max(MAX_PASSWORD_LENGTH, {\n message: `Password must be at most ${MAX_PASSWORD_LENGTH} characters`,\n })\n .parse(value);\n}\n\nexport function validateEmail(value: string) {\n return z\n .string()\n .max(MAX_EMAIL_LENGTH, {\n message: `Email must be at most ${MAX_EMAIL_LENGTH} characters`,\n })\n .email({ message: 'Invalid email address' })\n .parse(value)\n .toLowerCase();\n}\n\nexport function validateHandle(value: string) {\n return handleSchema.parse(value);\n}\n","import { randomBytes } from 'crypto';\nimport { MongoServerError } from 'mongodb';\nimport { usersCollection } from './db';\nimport { User } from './types';\nimport { validateHandle, MAX_HANDLE_LENGTH, MIN_HANDLE_LENGTH } from './validators';\n\n/**\n * Detects the duplicate-key error thrown when the unique `emails.address` index\n * rejects a concurrent second insert for the same email. The check-then-insert\n * in the signup paths can interleave, so this is the last line of defense that\n * turns a lost race into a clean \"already exists\" instead of an unhandled 500.\n */\nexport function isDuplicateEmailError(error: unknown): boolean {\n return (\n error instanceof MongoServerError &&\n error.code === 11000 &&\n // keyPattern is present on write errors and names the violated index's keys.\n typeof error.keyPattern === 'object' &&\n error.keyPattern !== null &&\n 'emails.address' in error.keyPattern\n );\n}\n\n/**\n * Resolves a configured redirect URL against the site base URL: absolute URLs\n * are used as-is, relative ones are appended to the base, and a missing value\n * falls back to the base URL itself.\n */\nexport function resolveUrl(baseUrl: string, configuredUrl?: string): string {\n if (!configuredUrl) {\n return baseUrl;\n }\n\n if (configuredUrl.startsWith('http://') || configuredUrl.startsWith('https://')) {\n return configuredUrl;\n }\n\n // Handle relative URL\n return `${baseUrl}${configuredUrl.startsWith('/') ? '' : '/'}${configuredUrl}`;\n}\n\nexport function serializeUserForClient(userDoc: User) {\n return {\n id: userDoc._id,\n handle: userDoc.handle,\n roles: userDoc.roles || [],\n firstName: userDoc.firstName ?? undefined,\n lastName: userDoc.lastName ?? undefined,\n avatarUrl: userDoc.avatarUrl ?? undefined,\n };\n}\n\n/**\nFinds an available handle by appending incremental suffixes (_2, _3, …)\nto the base handle until a unique one is found.\nThe base handle is truncated so that the suffixed candidate never exceeds MAX_HANDLE_LENGTH.\n */\nasync function findAvailableHandle(baseHandle: string): Promise<string> {\n // Truncate base handle to MAX_HANDLE_LENGTH so the unsuffixed form is valid.\n const truncatedBase = baseHandle.slice(0, MAX_HANDLE_LENGTH);\n\n // Check the unsuffixed base handle first.\n try {\n const firstCheck = await usersCollection.findOne(\n { handle: truncatedBase },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (!firstCheck) {\n return truncatedBase;\n }\n } catch (err) {\n throw new Error(`Database error while checking handle availability: ${err}`);\n }\n\n // Try sequential suffixes _2 through _51 (50 attempts).\n const MAX_SUFFIX_VALUE = 51;\n\n for (let suffix = 2; suffix <= MAX_SUFFIX_VALUE; suffix++) {\n const suffixStr = `_${suffix}`;\n const candidate = `${truncatedBase.slice(0, MAX_HANDLE_LENGTH - suffixStr.length)}${suffixStr}`;\n\n try {\n const conflict = await usersCollection.findOne(\n { handle: candidate },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (!conflict) {\n return candidate;\n }\n } catch (err) {\n throw new Error(`Database error while checking handle \"${candidate}\": ${err}`);\n }\n }\n\n // Fallback: sequential suffixes exhausted — use random hex suffixes.\n // Limit the number of attempts to avoid an infinite loop in case of persistent DB issues\n const MAX_RANDOM_ATTEMPTS = 10;\n\n for (let attempt = 0; attempt < MAX_RANDOM_ATTEMPTS; attempt++) {\n const randomSuffix = `_${randomBytes(3).toString('hex')}`;\n const candidate = `${truncatedBase.slice(0, MAX_HANDLE_LENGTH - randomSuffix.length)}${randomSuffix}`;\n\n try {\n const conflict = await usersCollection.findOne(\n { handle: candidate },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (!conflict) {\n return candidate;\n }\n } catch (err) {\n throw new Error(`Database error while checking handle \"${candidate}\": ${err}`);\n }\n }\n\n throw new Error(\n `Could not generate a unique handle for base \"${baseHandle}\" after exhausting all attempts.`\n );\n}\n\n/**\n * Resolves a unique handle for a new user.\n *\n * If the caller supplied a handle it is validated and checked for uniqueness.\n * - When `throwOnConflict` is true (default), an error is thrown if the handle is taken.\n * - When `throwOnConflict` is false, a numeric suffix (_2, _3, …) is appended to make\n * it unique. This is intended for system-generated handles (e.g. from `generateHandle`).\n *\n * If no handle is supplied, the local part of the email address is used as the base handle,\n * and a numeric suffix is appended until an unused handle is found.\n */\nexport async function resolveUniqueHandle(\n rawHandle: string | undefined,\n email: string,\n { throwOnConflict = true }: { throwOnConflict?: boolean } = {}\n): Promise<string> {\n if (rawHandle !== undefined && rawHandle !== null && String(rawHandle).trim() !== '') {\n // Caller explicitly provided a handle – trim and validate it.\n const handle = validateHandle(String(rawHandle).trim());\n\n if (throwOnConflict) {\n const existing = await usersCollection.findOne(\n { handle },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (existing) {\n throw new Error('Handle already taken.');\n }\n\n return handle;\n }\n\n // System-generated handle – find an available variant with suffix if needed.\n return findAvailableHandle(handle);\n }\n\n // Derive handle from the email local-part (everything before '@').\n // Sanitize non-alphanumeric/underscore/hyphen characters to satisfy HANDLE_REGEX.\n // Truncate to MAX_HANDLE_LENGTH since RFC 5321 allows local parts up to 64 chars.\n const rawBase = email.split('@')[0].replace(/[^a-zA-Z0-9_-]/g, '_');\n const baseHandle = rawBase.padEnd(MIN_HANDLE_LENGTH, '_').slice(0, MAX_HANDLE_LENGTH);\n return findAvailableHandle(baseHandle);\n}\n","import type { EmailProvider } from '../types';\n\nexport type EmailConfig = {\n provider?: EmailProvider;\n from?: string;\n verification?: {\n subject?: string;\n template?: (props: { name: string; email: string; verificationUrl: string }) => string;\n redirectUrl?: string;\n };\n passwordReset?: {\n subject?: string;\n template?: (props: { name: string; email: string; resetUrl: string }) => string;\n redirectUrl?: string;\n };\n magicLink?: {\n subject?: string;\n /**\n * Custom email template. Receives both credentials for the sign-in:\n * `magicLinkUrl` (the clickable link) and `code` (the typed one-time\n * code for `loginWithOneTimeCode`) — render either or both.\n */\n template?: (props: {\n name: string;\n email: string;\n magicLinkUrl: string;\n code: string;\n }) => string;\n /** SPA page the landing route redirects to; it must call `loginWithMagicLink()`. */\n redirectUrl?: string;\n };\n // @deprecated use verification.redirectUrl instead\n emailVerifiedRedirectUrl?: string;\n};\n\nlet emailConfig: EmailConfig = Object.freeze({});\n\nexport function setEmailConfig(newEmailConfig: EmailConfig) {\n emailConfig = Object.freeze(Object.assign({}, emailConfig, newEmailConfig));\n}\n\nexport function getEmailConfig() {\n return emailConfig;\n}\n","import { AuthErrorProps, AuthSuccessProps, OAuthErrorInfo, User } from '@/auth/types';\nimport { ConnectionInfo, UpdateProfileProps, SignupProps } from '@/methods/types';\nimport type { RateLimitType } from '@/rate-limit/types';\n\n/**\n * A single rate-limit rule for an authentication bucket. The `bucket` is\n * implied by which auth action you're configuring (e.g. `signup`), so callers\n * only specify the actor type, window size, and limit.\n *\n * @example\n * ```typescript\n * import { time } from 'modelence';\n *\n * const rule: AuthRateLimitOverride = {\n * type: 'ip',\n * window: time.minutes(15),\n * limit: 10,\n * };\n * ```\n */\nexport type AuthRateLimitOverride = {\n /** Identifier type of the actor this rule applies to. */\n type: RateLimitType;\n /** Time window size in milliseconds. Use `time.minutes(15)` etc. */\n window: number;\n /** Maximum allowed hits within the window. */\n limit: number;\n};\n\n/**\n * Per-action rate limit overrides for authentication endpoints.\n *\n * Each bucket accepts an array of rules that are merged into the built-in\n * defaults by `(type, window)` tuple:\n * - A rule whose `(type, window)` matches a default replaces that default's\n * `limit`.\n * - A rule whose `(type, window)` does not match any default is added as\n * an extra rule for the bucket.\n * - Defaults whose `(type, window)` is not overridden are kept.\n *\n * This means you can tighten a single window without accidentally dropping\n * the other built-in protections for that bucket.\n *\n * @example Tighten the 15-minute signup cap; per-day default is preserved.\n * ```typescript\n * import { startApp } from 'modelence/server';\n * import { time } from 'modelence';\n *\n * startApp({\n * auth: {\n * rateLimits: {\n * signup: [\n * { type: 'ip', window: time.minutes(15), limit: 5 },\n * ],\n * },\n * },\n * });\n * ```\n *\n * @example Add an extra window alongside the defaults.\n * ```typescript\n * startApp({\n * auth: {\n * rateLimits: {\n * signup: [\n * { type: 'ip', window: time.minutes(1), limit: 2 },\n * ],\n * },\n * },\n * });\n * ```\n */\nexport type AuthRateLimitsConfig = {\n /** Per-IP limits for the signup endpoint (successful signups only). */\n signup?: AuthRateLimitOverride[];\n /** Per-IP limits for signup attempts (checked before duplicate detection). */\n signupAttempt?: AuthRateLimitOverride[];\n /** Per-IP limits for login attempts. */\n signin?: AuthRateLimitOverride[];\n /** Per-user limits for email verification requests. */\n verification?: AuthRateLimitOverride[];\n /** Rate limits for password reset requests. */\n passwordReset?: AuthRateLimitOverride[];\n /** Rate limits for magic link requests. */\n magicLink?: AuthRateLimitOverride[];\n /** Rate limits for one-time code sign-in attempts. */\n oneTimeCode?: AuthRateLimitOverride[];\n /** Rate limits for redeeming a mobile OAuth exchange code. */\n oauthExchange?: AuthRateLimitOverride[];\n /** Per-user rate limits for profile updates. */\n updateProfile?: AuthRateLimitOverride[];\n};\n\ntype GenerateHandleProps = {\n email: string;\n firstName?: string;\n lastName?: string;\n};\n\n/**\n * The password-setting flow that invoked {@link AuthConfig.validatePassword}.\n *\n * Every path that writes a password hash passes one of these, so a policy can\n * be uniform by default while still special-casing a flow if it needs to.\n */\nexport type PasswordContext = 'signup' | 'reset';\n\n/**\n * Props passed to {@link AuthConfig.validatePassword}.\n */\nexport type ValidatePasswordProps = {\n /** The candidate plaintext password, after the built-in length checks. */\n password: string;\n /**\n * Lowercased email address of the account the password is being set on.\n * Useful for rejecting passwords that contain the local-part.\n */\n email: string;\n /** Which flow is setting the password. */\n context: PasswordContext;\n};\n\n/**\n * Props passed to {@link AuthConfig.onBeforeSignup}.\n *\n * The hook fires after validation and the built-in disposable-email check,\n * but before the user document is inserted. Throwing aborts the signup —\n * the thrown error is re-thrown to the caller and `onSignupError` fires.\n */\nexport type BeforeSignupProps = {\n /** Lowercased, validated email address. */\n email: string;\n firstName?: string;\n lastName?: string;\n handle?: string;\n /** Provider that initiated the signup: `'email'` or `'magicLink'`. */\n provider: 'email' | 'magicLink';\n connectionInfo?: ConnectionInfo;\n};\n\n/**\n * Callback options for authentication operations\n */\nexport type AuthOption = {\n /** Callback executed when authentication succeeds */\n onSuccess?: (user: User) => void;\n /** Callback executed when authentication fails */\n onError?: (error: Error) => void;\n};\n\n/**\n * Authentication configuration for the application\n *\n * @example\n * ```typescript\n * import { startApp } from 'modelence/server';\n * import { time } from 'modelence';\n *\n * startApp({\n * auth: {\n * validateSignup: ({ email, firstName, lastName, password, handle, avatarUrl }) => {\n * // Validating the signup data\n * if (!email || !password) {\n * throw new Error('Email and password are required');\n * }\n * },\n * onAfterLogin: ({ user }) => {\n * console.log('User logged in:', user.name);\n * // Redirect to dashboard\n * },\n * onLoginError: ({ error }) => {\n * console.error('Login failed:', error.message);\n * // Show error toast\n * },\n * onAfterSignup: ({ user }) => {\n * console.log('User signed up:', user.email);\n * // Send welcome email\n * },\n * onSignupError: ({ error }) => {\n * console.error('Signup failed:', error.message);\n * },\n * generateHandle: ({ email }) => {\n * console.log('Generating handle for:', email);\n * // Generate handle\n * return 'user123';\n * },\n * }\n * });\n * ```\n */\nexport type AuthConfig = {\n /**\n * Pre-signup validation hook. Runs before a new user is created during\n * email/password signup, after format checks but before duplicate detection.\n * Throw to reject the signup — the thrown message is surfaced to the client.\n *\n * Receives the raw signup payload (`email`, `password`, and optional\n * `firstName`, `lastName`, `avatarUrl`, `handle`). May be async.\n */\n validateSignup?: (props: SignupProps) => void | Promise<void>;\n\n /**\n * Password policy hook. Runs on **every** path that sets a password — signup\n * and password reset today, plus any future change-password flow — after the\n * built-in length checks (`MIN_PASSWORD_LENGTH`..`MAX_PASSWORD_LENGTH`) and\n * before the password is hashed. Throw to reject the password; the thrown\n * message is surfaced to the client.\n *\n * Prefer this over {@link AuthConfig.validateSignup} for password rules.\n * `validateSignup` only runs at signup, so a policy enforced there alone can\n * be bypassed by signing up with a compliant password and immediately\n * resetting to a weaker one.\n *\n * Receives `{ password, email, context }`, where `context` is the flow that\n * triggered the check (`'signup'` or `'reset'`). May be async.\n *\n * @example Enforce a 12-character minimum everywhere.\n * ```typescript\n * startApp({\n * auth: {\n * validatePassword: ({ password }) => {\n * if (password.length < 12) {\n * throw new Error('Password must be at least 12 characters');\n * }\n * },\n * },\n * });\n * ```\n */\n validatePassword?: (props: ValidatePasswordProps) => void | Promise<void>;\n\n /**\n * Pre-update validation hook. Runs before a user's profile fields\n * (`firstName`, `lastName`, `avatarUrl`, `handle`) are written.\n * Throw to reject the update — the thrown message is surfaced to the client.\n * May be async.\n */\n validateProfileUpdate?: (props: UpdateProfileProps) => void | Promise<void>;\n\n /**\n * Fires after a successful login (email/password or OAuth) once the session\n * has been linked to the user. Receives `{ provider, user, session, connectionInfo }`.\n * Use for analytics, audit logging, or post-login side effects.\n */\n onAfterLogin?: (props: AuthSuccessProps) => void;\n\n /**\n * Fires when a login attempt fails. Receives `{ provider, error, session, connectionInfo }`.\n * Use for failure analytics or alerting — does NOT change the response sent to the client.\n */\n onLoginError?: (props: AuthErrorProps) => void;\n\n /**\n * Hook fired after validation and the built-in disposable-email check, but\n * before the new user document is inserted. Throwing aborts the signup —\n * the thrown error is re-thrown to the caller and `onSignupError` fires.\n *\n * Use this to plug in a custom domain-policy check (e.g. a tenant-specific\n * email-domain verification service) without having to disable the built-in\n * disposable-email check.\n *\n * Invoked for `'email'` and `'magicLink'` provider signups. OAuth signups\n * are not gated because OAuth providers (Google, GitHub, etc.) do not issue\n * disposable accounts.\n */\n onBeforeSignup?: (props: BeforeSignupProps) => void | Promise<void>;\n\n /**\n * Fires after a successful signup once the user record is created and the\n * session is linked. Receives `{ provider, user, session, connectionInfo }`.\n * Common uses: send welcome email, create default workspace, track activation.\n */\n onAfterSignup?: (props: AuthSuccessProps) => void;\n\n /**\n * Fires when a signup attempt fails (validation, duplicate email, etc.).\n * Receives `{ provider, error, session, connectionInfo }`.\n * Use for failure analytics — does NOT change the response sent to the client.\n */\n onSignupError?: (props: AuthErrorProps) => void;\n\n /**\n * Fires after a user's email is successfully verified (via the verification\n * link or implicitly via password reset). Receives `{ provider, user, session, connectionInfo }`.\n */\n onAfterEmailVerification?: (props: AuthSuccessProps) => void;\n\n /**\n * Fires when email verification fails (invalid or expired token).\n * Receives `{ provider, error, session, connectionInfo }`.\n */\n onEmailVerificationError?: (props: AuthErrorProps) => void;\n\n /**\n * Fires after an OAuth provider is linked to an existing account\n * (either automatically when `oauthAccountLinking: 'auto'` or via an\n * explicit link flow). Receives `{ provider, user, session, connectionInfo }`.\n */\n onAfterOAuthLink?: (props: AuthSuccessProps) => void;\n\n /**\n * Fires when OAuth account linking fails. Receives\n * `{ provider, error, session, connectionInfo }`.\n */\n onOAuthLinkError?: (props: AuthErrorProps) => void;\n\n /**\n * Custom handle generator. If provided, overrides the default behavior\n * (which derives the handle from the email local-part). Receives\n * `{ email, firstName?, lastName? }` and returns the desired handle\n * synchronously or as a `Promise<string>`. The returned handle must contain\n * only letters, numbers, underscores, and hyphens (matching `HANDLE_REGEX`).\n * If the returned handle collides with an existing one, Modelence appends\n * a numeric suffix automatically.\n */\n generateHandle?: (props: GenerateHandleProps) => Promise<string> | string;\n\n /** @deprecated Use {@link AuthConfig.onAfterLogin} and {@link AuthConfig.onLoginError} instead. */\n login?: AuthOption;\n /** @deprecated Use {@link AuthConfig.onAfterSignup} and {@link AuthConfig.onSignupError} instead. */\n signup?: AuthOption;\n\n /**\n * Enables passwordless magic link authentication. Disabled by default.\n *\n * Requires an email provider and delivery settings under the `email` option\n * (see `EmailConfig.magicLink`).\n *\n * @example\n * ```typescript\n * startApp({\n * auth: {\n * magicLink: { enabled: true, allowSignup: true },\n * },\n * });\n * ```\n */\n magicLink?: {\n enabled?: boolean;\n /**\n * Allows a magic link (or its one-time code) to create an account when\n * the email has no existing one — combined sign-in/sign-up, like OAuth.\n * Disabled by default: unknown emails get the same generic \"link sent\"\n * response but no email, and no account is ever auto-created.\n */\n allowSignup?: boolean;\n };\n\n /**\n * Controls how OAuth providers handle existing accounts with matching email.\n * - 'manual' (default): Returns an error when an OAuth login matches an existing email.\n * - 'auto': Automatically links the OAuth provider to the existing account\n * if the provider email is verified.\n */\n oauthAccountLinking?: 'auto' | 'manual';\n\n /**\n * Customizes how OAuth authentication errors are rendered. By default,\n * OAuth errors are returned as JSON; providing this returns a custom HTML\n * response instead, which is useful when the OAuth flow runs in a browser\n * context. Receives `{ error, statusCode }` and returns an HTML string\n * (or `null`/`undefined` to fall back to the default JSON response).\n *\n * Always escape interpolated values to prevent XSS.\n *\n * @deprecated Use {@link AuthConfig.oauthErrorRedirectUrl} instead. It sends\n * the browser back into the app, where the error can be shown on a real page\n * with the app's own UI; this only ever renders a standalone document at the\n * callback URL. Ignored when `oauthErrorRedirectUrl` is set.\n */\n errorComponent?: (props: OAuthErrorInfo) => string | null | undefined;\n\n /**\n * Where a failed web OAuth flow sends the browser. A path (`'/login'`),\n * resolved against `_system.site.url`, or an absolute URL. The failure is\n * appended as `?error=<message>&errorCode=<code>`,\n * the same contract a mobile flow delivers on its deep link, so the app can\n * show the message on a real page instead of the raw callback response.\n *\n * OAuth errors surface at the provider callback URL, outside any client\n * bundle, so nothing there can render app UI. Without this (or\n * {@link AuthConfig.errorComponent}) the user lands on a JSON body with no\n * way back. When both are set, the redirect wins and `errorComponent` is not\n * called.\n *\n * `error` is for display and its wording may change; branch on `errorCode`.\n *\n * Applies to the provider callback only. A request rejected at the sign-in\n * *initiation* endpoint (auth not configured, a disallowed mobile\n * `redirectUri`, a missing `codeChallenge`) still answers the calling client\n * with JSON, since nothing has left for the provider yet.\n *\n * @example\n * ```typescript\n * startApp({\n * auth: {\n * oauthErrorRedirectUrl: '/login',\n * },\n * });\n * ```\n */\n oauthErrorRedirectUrl?: string;\n\n /**\n * Overrides the built-in rate limits for authentication endpoints. Each rule\n * you provide is merged into the defaults by `(bucket, type, window)`:\n * matching tuples replace the default `limit`, new tuples are added, and\n * unspecified defaults are preserved. See {@link AuthRateLimitsConfig} for\n * full semantics and examples.\n */\n rateLimits?: AuthRateLimitsConfig;\n\n /**\n * When `true`, the built-in disposable-email check is skipped during signup.\n * Defaults to `false` (built-in check enforced).\n *\n * Set this to `true` when you want to enforce your own domain-policy logic\n * via {@link onBeforeSignup} — for example, a service that classifies\n * domains as public/disposable/custom with its own data sources and cache.\n *\n * Skipping the built-in check without registering an `onBeforeSignup` hook\n * means disposable emails will be allowed to sign up.\n */\n allowDisposableEmails?: boolean;\n\n /**\n * Settings for authenticating from a native (React Native / Expo) client.\n *\n * @example\n * ```typescript\n * startApp({\n * auth: {\n * mobile: { redirectUrls: ['myapp://auth'] },\n * },\n * });\n * ```\n */\n mobile?: {\n /**\n * Deep links the OAuth callback is allowed to redirect a native app back to,\n * e.g. `['myapp://auth']`. A sign-in request naming any other target is\n * rejected before the user ever reaches the provider's consent screen.\n *\n * This is an allowlist because the redirect target decides where an auth\n * flow ends up: without it, a crafted link could point the callback at an\n * attacker-controlled destination. There is no implicit default — mobile\n * OAuth stays disabled until at least one entry is configured here or via\n * the `auth.mobile.redirectUrls` config value (the two are merged).\n *\n * Entries are matched on scheme, host and path; a request may add query\n * parameters but may not change any of those three.\n */\n redirectUrls?: string[];\n };\n};\n\nlet authConfig: AuthConfig = Object.freeze({});\n\nexport function setAuthConfig(newAuthConfig: AuthConfig) {\n authConfig = Object.freeze(Object.assign({}, authConfig, newAuthConfig));\n}\n\nexport function getAuthConfig() {\n return authConfig;\n}\n","import { randomBytes } from 'crypto';\n\nimport bcrypt from 'bcrypt';\nimport { z } from 'zod';\n\nimport { Args, Context } from '../methods/types';\nimport { usersCollection } from './db';\nimport { serializeUserForClient } from './utils';\nimport {\n clearAuthTokenCookie,\n clearSessionUser,\n setAuthTokenCookie,\n setSessionUser,\n} from './session';\nimport { getEmailConfig } from '@/app/emailConfig';\nimport { consumeRateLimit } from '@/server';\nimport { validateEmail } from './validators';\nimport { getAuthConfig } from '@/app/authConfig';\nimport { getConfig } from '@/config/server';\nimport { AuthError, ValidationError } from '../error';\n\n/**\n * Whether unverified emails should be blocked from logging in.\n *\n * Verification can only be enforced when an email provider is configured (there\n * is otherwise no way to deliver a verification email). When a provider exists,\n * the `auth.email.verification` config flag acts as the actual switch — it\n * defaults to `true`, preserving the historical behavior where configuring a\n * provider implicitly turned verification on. Operators can now set the flag to\n * `false` to keep a provider while allowing unverified users to sign in.\n */\nfunction isEmailVerificationRequired(): boolean {\n if (!getEmailConfig()?.provider) {\n return false;\n }\n\n return Boolean(getConfig('_system.user.auth.email.verification'));\n}\n\nconst SALT_ROUNDS = 10;\nconst DUMMY_PASSWORD_HASH = bcrypt.hashSync(randomBytes(32).toString('hex'), SALT_ROUNDS);\n\nexport async function handleLoginWithPassword(\n args: Args,\n { user, session, connectionInfo, res }: Context\n) {\n try {\n if (!session) {\n throw new Error('Session is not initialized');\n }\n\n if (user) {\n throw new ValidationError('User is already authenticated', 'ALREADY_AUTHENTICATED');\n }\n\n const ip = connectionInfo?.ip;\n if (ip) {\n await consumeRateLimit({\n bucket: 'signin',\n type: 'ip',\n value: ip,\n });\n }\n\n const email = validateEmail(args.email as string);\n // password is accepted just as a string, so users can still sign in if the password validation rules are changed\n const password = z.string().parse(args.password);\n\n // TODO: add rate limiting by email (and perhaps IP address overall)\n\n const userDoc = await usersCollection.findOne(\n { 'emails.address': email, status: { $nin: ['deleted', 'disabled'] } },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n const passwordHash = userDoc?.authMethods?.password?.hash;\n\n // Always run bcrypt.compare to mitigate timing attacks that could reveal if an email exists.\n const hashToCompare = passwordHash || DUMMY_PASSWORD_HASH;\n const isValidPassword = await bcrypt.compare(password, hashToCompare);\n\n if (!passwordHash || !isValidPassword) {\n throw incorrectCredentialsError();\n }\n\n // Only check email verification if the user exists and the password is correct,\n // to prevent email enumeration by observing verification errors before password checks.\n const emailDoc = userDoc.emails?.find((e) => e.address.toLowerCase() === email);\n\n if (!emailDoc?.verified && isEmailVerificationRequired()) {\n throw new AuthError(\n \"Your email address hasn't been verified yet. Please check your inbox for the verification email.\",\n 'EMAIL_NOT_VERIFIED'\n );\n }\n\n await setSessionUser(session.authToken, userDoc._id);\n\n if (res) {\n setAuthTokenCookie(res, session.authToken);\n }\n\n getAuthConfig().onAfterLogin?.({\n provider: 'email',\n user: userDoc,\n session,\n connectionInfo,\n });\n getAuthConfig().login?.onSuccess?.(userDoc);\n\n return {\n user: serializeUserForClient(userDoc),\n session: { authToken: session.authToken },\n };\n } catch (error) {\n if (error instanceof Error) {\n getAuthConfig().onLoginError?.({\n provider: 'email',\n error,\n session,\n connectionInfo,\n });\n getAuthConfig().login?.onError?.(error);\n }\n throw error;\n }\n}\n\nexport async function handleLogout(args: Args, { session, res }: Context) {\n if (!session) {\n throw new Error('Session is not initialized');\n }\n\n await clearSessionUser(session.authToken);\n\n if (res) {\n clearAuthTokenCookie(res);\n }\n}\n\n/*\n It is important to return the same exact error both in case the email\n or password is incorrect so that the client cannot tell the difference,\n otherwise it would allow for an enumeration attack.\n*/\nfunction incorrectCredentialsError() {\n return new Error('Incorrect email/password combination');\n}\n","import { Store } from '../data/store';\nimport { schema } from '../data/types';\n\n/**\n * Two-bucket sliding window approximation to track rate limits.\n * This is a trade-off between storage and accuracy.\n * We're not keeping exact event logs, so we can't track the exact number of events in a window.\n * Instead, we're adding a weighted average from the previous window to the current window.\n */\nexport const dbRateLimits = new Store('_modelenceRateLimits', {\n schema: {\n bucket: schema.string(),\n type: schema.enum(['ip', 'user', 'email']),\n value: schema.string(),\n windowMs: schema.number(),\n\n windowStart: schema.date(),\n windowCount: schema.number(),\n prevWindowCount: schema.number(),\n\n expiresAt: schema.date(),\n },\n indexes: [\n { key: { bucket: 1, type: 1, value: 1, windowMs: 1 }, unique: true },\n { key: { expiresAt: 1 }, expireAfterSeconds: 0 },\n ],\n});\n","import { RateLimitRule, RateLimitType } from './types';\nimport { dbRateLimits } from './db';\nimport { RateLimitError } from '../error';\n\nlet allRules: Array<RateLimitRule> = [];\n\nexport function initRateLimits(rateLimits: RateLimitRule[]) {\n if (allRules.length > 0) {\n throw new Error('Duplicate call to initRateLimits - already initialized');\n }\n\n allRules = rateLimits;\n}\n\n/**\n * This function will check all rate limit rules on the specified bucket and type,\n * throw an error if any of them are exceeded and increase the count of the rate limit record.\n *\n * @category Rate Limits\n *\n * @example\n * ```ts\n * await consumeRateLimit({ bucket: 'api', type: 'ip', value: '127.0.0.1' });\n * ```\n * @param options.bucket - The bucket for the rate limit.\n * @param options.type - The type of the rate limit.\n * @param options.value - The value for the rate limit.\n * @param options.message - Optional custom error message when the rate limit is exceeded.\n */\nexport async function consumeRateLimit(options: {\n bucket: string;\n type: RateLimitType;\n value: string;\n message?: string;\n}) {\n const { bucket, type, value, message } = options;\n const rules = allRules.filter((rule) => rule.bucket === bucket && rule.type === type);\n const createError = message ? () => new RateLimitError(message) : undefined;\n\n for (const rule of rules) {\n await checkRateLimitRule(rule, value, createError);\n }\n}\n\n// Two-bucket sliding window approximation to track rate limits.\nasync function checkRateLimitRule(rule: RateLimitRule, value: string, createError?: () => Error) {\n const createRateLimitError = () => {\n return createError\n ? createError()\n : new RateLimitError(`Rate limit exceeded for ${rule.bucket}`);\n };\n\n const filter = {\n bucket: rule.bucket,\n type: rule.type,\n value,\n windowMs: rule.window,\n };\n\n const record = await dbRateLimits.findOne(filter);\n\n const now = Date.now();\n const currentWindowStart = Math.floor(now / rule.window) * rule.window;\n\n const { count, modifier } = record\n ? getCount(record, currentWindowStart, now)\n : {\n count: 0,\n modifier: {\n $setOnInsert: {\n windowStart: new Date(currentWindowStart),\n windowCount: 1,\n prevWindowCount: 0,\n expiresAt: new Date(currentWindowStart + rule.window + rule.window),\n },\n },\n };\n\n if (count >= rule.limit) {\n throw createRateLimitError();\n }\n\n /*\n Always use upsert, because there is a small chance the document might be auto-removed\n based on the expiration TTL index in between the check and the update\n */\n await dbRateLimits.upsertOne(filter, modifier);\n}\n\nfunction getCount(record: (typeof dbRateLimits)['Doc'], currentWindowStart: number, now: number) {\n const prevWindowStart = currentWindowStart - record.windowMs;\n\n if (record.windowStart.getTime() === currentWindowStart) {\n const currentWindowCount = record.windowCount;\n const prevWindowCount = record.prevWindowCount;\n const prevWindowWeight = 1 - (now - currentWindowStart) / record.windowMs;\n return {\n count: Math.round(currentWindowCount + prevWindowCount * prevWindowWeight),\n modifier: {\n $inc: { windowCount: 1 },\n $setOnInsert: {\n windowStart: new Date(currentWindowStart),\n prevWindowCount: 0,\n expiresAt: new Date(currentWindowStart + record.windowMs + record.windowMs),\n },\n },\n };\n }\n\n if (record.windowStart.getTime() === prevWindowStart) {\n const weight = 1 - (now - currentWindowStart) / record.windowMs;\n return {\n count: Math.round(record.windowCount * weight),\n modifier: {\n $set: {\n windowStart: new Date(currentWindowStart),\n windowCount: 1,\n prevWindowCount: record.windowCount,\n expiresAt: new Date(currentWindowStart + record.windowMs + record.windowMs),\n },\n },\n };\n }\n\n return {\n count: 0,\n modifier: {\n $set: {\n windowStart: new Date(currentWindowStart),\n windowCount: 1,\n prevWindowCount: 0,\n expiresAt: new Date(currentWindowStart + record.windowMs + record.windowMs),\n },\n },\n };\n}\n","import { usersCollection } from './db';\nimport { Args, Context, UpdateProfileProps } from '../methods/types';\nimport { serializeUserForClient } from './utils';\nimport { validateProfileFields } from './validators';\nimport { getAuthConfig } from '@/app/authConfig';\nimport { consumeRateLimit } from '../rate-limit/rules';\nimport { AuthError, ValidationError } from '../error';\n\nexport async function getOwnProfile(props: Args, { user }: Context) {\n if (!user) {\n throw new AuthError('Not authenticated');\n }\n\n const profile = await usersCollection.requireById(user.id);\n\n return {\n handle: profile.handle,\n emails: profile.emails,\n authMethods: Object.keys(profile.authMethods || {}),\n firstName: profile.firstName ?? undefined,\n lastName: profile.lastName ?? undefined,\n avatarUrl: profile.avatarUrl ?? undefined,\n };\n}\n\nexport async function handleUpdateProfile(props: Args, { user }: Context) {\n if (!user) {\n throw new AuthError('Not authenticated');\n }\n\n let profile = await usersCollection.requireById(user.id);\n\n const rawProps = props as UpdateProfileProps;\n const isHandleUnchanged =\n typeof rawProps.handle === 'string' && rawProps.handle.trim() === profile.handle;\n\n const { handle: _ignoredHandle, ...propsWithoutHandle } = rawProps;\n const update = validateProfileFields(\n isHandleUnchanged ? (propsWithoutHandle as UpdateProfileProps) : rawProps\n );\n\n await getAuthConfig().validateProfileUpdate?.(update);\n\n //Check if handle is already taken\n if ('handle' in update && update.handle !== undefined) {\n const existing = await usersCollection.findOne(\n {\n handle: update.handle,\n _id: { $ne: profile._id }, // excludes the current user\n },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (existing) {\n throw new ValidationError('Handle already taken.');\n }\n }\n\n //Update profile\n if (Object.keys(update).length > 0) {\n await consumeRateLimit({ bucket: 'updateProfile', type: 'user', value: user.id });\n\n const setFields: Record<string, unknown> = {}; //Used to set the fields\n const unsetFields: Record<string, ''> = {}; //Used to clear the fields the mongodb, because mongodb rejects undefined values when updating fields\n\n for (const [key, value] of Object.entries(update)) {\n if (value === undefined) {\n unsetFields[key] = '';\n } else {\n setFields[key] = value;\n }\n }\n\n const mongoUpdate: Record<string, unknown> = {};\n if (Object.keys(setFields).length > 0) mongoUpdate.$set = setFields;\n if (Object.keys(unsetFields).length > 0) mongoUpdate.$unset = unsetFields;\n\n try {\n await usersCollection.updateOne({ _id: profile._id }, mongoUpdate);\n\n //Also pass Cleared Fields as UNDEFINED in profile\n const clearedFields = Object.fromEntries(\n Object.keys(unsetFields).map((key) => [key, undefined])\n );\n profile = { ...profile, ...setFields, ...clearedFields } as typeof profile;\n } catch (error) {\n if (error instanceof Error && 'code' in error && (error as { code: number }).code === 11000) {\n throw new ValidationError('Handle already taken.');\n }\n throw error;\n }\n }\n\n return {\n user: serializeUserForClient(profile),\n };\n}\n","import { Document, ObjectId } from 'mongodb';\nimport { ConnectionInfo } from '@/methods/types';\n\nexport interface UserEmail {\n address: string;\n verified: boolean;\n}\n\nexport interface User extends Document {\n _id: ObjectId;\n\n handle: string;\n\n emails?: UserEmail[];\n\n status?: 'active' | 'disabled' | 'deleted';\n\n firstName?: string;\n lastName?: string;\n avatarUrl?: string;\n\n createdAt: Date;\n disabledAt?: Date;\n deletedAt?: Date;\n\n roles?: string[];\n\n authMethods: {\n password?: {\n hash: string;\n };\n google?: {\n id: string;\n };\n github?: {\n id: string;\n };\n };\n}\n\nexport type UserInfo = {\n /** The user's unique identifier. */\n id: string;\n /** The user's display handle. */\n handle: string;\n /** The role strings assigned to this user in the database. */\n roles: string[];\n /** Returns `true` if the user has the given role. */\n hasRole: (role: string) => boolean;\n /** Throws an error if the user does not have the given role. */\n requireRole: (role: string) => void;\n firstName?: string;\n lastName?: string;\n avatarUrl?: string;\n};\n\nexport type Role = string;\n\nexport type DefaultRoles = Record<'authenticated' | 'unauthenticated', Role | null>;\n\nexport type Session = {\n authToken: string;\n expiresAt: Date;\n userId: ObjectId | null;\n};\n\nexport type Permission = string;\n\n/**\n * Defines a role that can be assigned to users.\n *\n * Roles are registered via the `roles` option in {@link AppOptions} and\n * are synced to the Modelence Cloud dashboard for user management.\n *\n * @example\n * ```typescript\n * import { startApp } from 'modelence/server';\n *\n * startApp({\n * roles: {\n * admin: { description: 'Full access to all features' },\n * editor: { description: 'Can edit content' },\n * viewer: {},\n * },\n * });\n * ```\n */\nexport type RoleDefinition = {\n /** Human-readable description of the role, shown in the Modelence Cloud dashboard. */\n description?: string;\n /** @internal */\n permissions?: Permission[];\n};\n\nexport const SUPPORTED_OAUTH_PROVIDERS = ['google', 'github'] as const;\nexport type OAuthProvider = (typeof SUPPORTED_OAUTH_PROVIDERS)[number];\n\nexport type AuthProvider = OAuthProvider | 'email' | 'magicLink';\n\nexport type AuthSuccessProps = {\n provider: AuthProvider;\n user: User;\n session: Session | null;\n connectionInfo: ConnectionInfo;\n};\n\nexport type AuthErrorProps = {\n provider: AuthProvider;\n error: Error;\n session: Session | null;\n connectionInfo: ConnectionInfo;\n};\n\nexport type OAuthErrorInfo = {\n error: string;\n statusCode: number;\n};\n","import { usersCollection } from './db';\nimport { Args, Context } from '@/methods/types';\nimport { OAuthProvider, SUPPORTED_OAUTH_PROVIDERS } from './types';\n\nexport async function handleUnlinkOAuthProvider({ provider }: Args, { user }: Context) {\n if (!user) {\n throw new Error('You must be signed in to unlink a provider.');\n }\n\n if (\n typeof provider !== 'string' ||\n !SUPPORTED_OAUTH_PROVIDERS.includes(provider as OAuthProvider)\n ) {\n throw new Error(\n `Invalid provider. Supported providers are: ${SUPPORTED_OAUTH_PROVIDERS.join(', ')}.`\n );\n }\n\n // requireById throws if user is not found, so no null check needed\n const userDoc = await usersCollection.requireById(user.id);\n\n const methods = userDoc.authMethods ?? {};\n const providerKey = provider as keyof typeof methods;\n\n if (!methods[providerKey]) {\n throw new Error(`${provider} is not linked to your account.`);\n }\n\n const activeMethodsCount = Object.values(methods).filter(Boolean).length;\n if (activeMethodsCount <= 1) {\n throw new Error(\n 'Cannot unlink your only authentication method. Please add another method first.'\n );\n }\n\n // Determine the OTHER auth method fields that must still exist for lockout prevention.\n // This atomic filter ensures that between our read and write, at least one other method\n // hasn't been concurrently removed (prevents two simultaneous unlinks from both succeeding).\n const otherMethods = Object.keys(methods).filter(\n (key) => key !== provider && methods[key as keyof typeof methods]\n );\n const otherMethodGuard =\n otherMethods.length > 0\n ? { $or: otherMethods.map((key) => ({ [`authMethods.${key}`]: { $exists: true } })) }\n : {};\n\n const result = await usersCollection.updateOne(\n { _id: userDoc._id, ...otherMethodGuard },\n { $unset: { [`authMethods.${provider}`]: '' } }\n );\n\n if (result.matchedCount === 0) {\n throw new Error(\n 'Cannot unlink your only authentication method. Please add another method first.'\n );\n }\n}\n","export function emailVerificationTemplate({\n name,\n email,\n verificationUrl,\n}: {\n name?: string;\n email: string;\n verificationUrl: string;\n}) {\n return `\n <p>Hi${name ? ` ${name}` : ''},</p>\n <p>Please verify your email address ${email} by clicking the link below:</p>\n <p><a href=\"${verificationUrl}\">${verificationUrl}</a></p>\n <p>If you did not request this, please ignore this email.</p>\n `;\n}\n","import { z } from 'zod';\n\nimport { usersCollection, emailVerificationTokensCollection } from './db';\nimport { ObjectId, RouteParams, RouteResponse } from '@/server';\nimport { getEmailConfig } from '@/app/emailConfig';\nimport { randomBytes } from 'crypto';\nimport { time } from '@/time';\nimport { htmlToText } from '@/utils';\nimport { emailVerificationTemplate } from './templates/emailVerficationTemplate';\nimport { getAuthConfig } from '@/app/authConfig';\nimport { User } from './types';\nimport { Args, Context } from '@/methods/types';\nimport { validateEmail } from './validators';\nimport { consumeRateLimit } from '@/rate-limit/rules';\nimport { getConfig } from '@/config/server';\nimport { createSession, setAuthTokenCookie } from './session';\n\nconst USER_COLLATION = { locale: 'en', strength: 2 } as const;\n\nasync function verifyEmailToken(token: string) {\n const tokenDoc = await emailVerificationTokensCollection.findOne({\n token,\n expiresAt: { $gt: new Date() },\n });\n\n if (!tokenDoc) {\n throw new Error('Invalid or expired verification token');\n }\n\n const userDoc = await usersCollection.findOne({\n _id: tokenDoc.userId,\n status: { $nin: ['deleted', 'disabled'] },\n });\n\n if (!userDoc) {\n throw new Error('User not found');\n }\n\n const email = tokenDoc.email;\n\n if (!email) {\n throw new Error('Email not found in token');\n }\n\n // Mark the specific email as verified atomically, returning the updated doc.\n // `$elemMatch` keeps both conditions bound to a single array element: with two\n // independent dotted predicates, `'emails.verified': { $ne: true }` means \"no\n // element is verified\", so any account already holding one verified address\n // could never verify another, and the positional `$` had no guarantee of\n // binding to the element carrying `address`.\n // Same strength-2 collation as the lookups: the token always carries a\n // lowercased address (validateEmail) while the stored address may keep its\n // original casing (e.g. OAuth-created accounts), or the match silently fails.\n const updatedUserDoc = await usersCollection.findOneAndUpdate(\n {\n _id: tokenDoc.userId,\n status: { $nin: ['deleted', 'disabled'] },\n emails: { $elemMatch: { address: email, verified: { $ne: true } } },\n },\n { $set: { 'emails.$.verified': true } },\n { collation: USER_COLLATION, returnDocument: 'after' }\n );\n\n // Consume the token regardless of the update outcome: it has now been\n // presented, and leaving it spendable until its 24h expiry lets a token that\n // always fails be replayed.\n await emailVerificationTokensCollection.deleteOne({ _id: tokenDoc._id });\n\n if (!updatedUserDoc) {\n // Distinguish \"already verified\" from \"not this user's address\" by checking\n // the specific element, mirroring handleResendEmailVerification. The prior\n // check only matched on address, so an address that failed to verify for any\n // other reason was still reported as already verified.\n const existingEmailDoc = userDoc.emails?.find((e) => e.address.toLowerCase() === email);\n\n if (!existingEmailDoc) {\n throw new Error('Email address not found for this user');\n }\n\n if (existingEmailDoc.verified) {\n throw new Error('Email is already verified');\n }\n\n throw new Error('Unable to verify email address');\n }\n\n return { userDoc: updatedUserDoc, email };\n}\n\nexport async function handleVerifyEmail(params: RouteParams): Promise<RouteResponse> {\n const baseUrl = getConfig('_system.site.url') as string | undefined;\n const emailVerifiedRedirectUrl =\n getEmailConfig().verification?.redirectUrl ||\n getEmailConfig().emailVerifiedRedirectUrl ||\n baseUrl ||\n '/';\n try {\n const token = z.string().parse(params.query.token);\n const { userDoc } = await verifyEmailToken(token);\n\n const authConfig = getAuthConfig();\n authConfig.onAfterEmailVerification?.({\n provider: 'email',\n user: userDoc as User,\n session: null,\n connectionInfo: {\n baseUrl,\n ip: params.req.ip || params.req.socket.remoteAddress,\n userAgent: params.headers['user-agent'],\n acceptLanguage: params.headers['accept-language'],\n referrer: params.headers['referer'],\n },\n });\n\n const { authToken } = await createSession(userDoc._id);\n\n setAuthTokenCookie(params.res, authToken);\n\n return {\n status: 301,\n // Suppress the Referer so the token-bearing verification URL never leaks.\n headers: { 'Referrer-Policy': 'no-referrer' },\n redirect: `${emailVerifiedRedirectUrl}?status=verified`,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'An unexpected error occurred';\n if (error instanceof Error) {\n const authConfig = getAuthConfig();\n authConfig.onEmailVerificationError?.({\n provider: 'email',\n error,\n session: null,\n connectionInfo: {\n baseUrl,\n ip: params.req.ip || params.req.socket.remoteAddress,\n userAgent: params.headers['user-agent'],\n acceptLanguage: params.headers['accept-language'],\n referrer: params.headers['referer'],\n },\n });\n console.error('Error verifying email:', error);\n }\n\n return {\n status: 301,\n headers: { 'Referrer-Policy': 'no-referrer' },\n redirect: `${emailVerifiedRedirectUrl}?status=error&message=${encodeURIComponent(message)}`,\n };\n }\n}\n\nexport async function sendVerificationEmail({\n userId,\n email,\n baseUrl: requestBaseUrl,\n}: {\n userId: ObjectId;\n email: string;\n baseUrl?: string;\n}) {\n const baseUrl = (getConfig('_system.site.url') as string | undefined) || requestBaseUrl;\n\n if (getEmailConfig().provider) {\n const emailProvider = getEmailConfig().provider;\n\n // Generate verification token\n const verificationToken = randomBytes(32).toString('hex');\n const expiresAt = new Date(Date.now() + time.hours(24));\n\n // Store token in database\n await emailVerificationTokensCollection.insertOne({\n userId,\n email,\n token: verificationToken,\n createdAt: new Date(),\n expiresAt,\n });\n\n const verificationUrl = `${baseUrl}/api/_internal/auth/verify-email?token=${verificationToken}`;\n\n const template = getEmailConfig()?.verification?.template || emailVerificationTemplate;\n // TODO: we should have also the name on this step\n const htmlTemplate = template({ name: '', email, verificationUrl });\n const textContent = htmlToText(htmlTemplate);\n\n await emailProvider?.sendEmail({\n to: email,\n from: getEmailConfig()?.from || 'noreply@modelence.com',\n subject: getEmailConfig()?.verification?.subject || 'Verify your email address',\n text: textContent,\n html: htmlTemplate,\n });\n }\n}\n\nconst resendVerificationResponse = {\n success: true,\n message: 'If that email is registered and not yet verified, a verification email has been sent',\n};\n\nexport async function handleResendEmailVerification(args: Args, { connectionInfo }: Context) {\n const email = validateEmail(args.email as string);\n\n // Find user by email, excluding deleted/disabled accounts\n const userDoc = await usersCollection.findOne(\n { 'emails.address': email, status: { $nin: ['deleted', 'disabled'] } },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n // Return the same generic response whether the email is unknown,\n // already verified, or successfully sent — to prevent user enumeration.\n if (!userDoc) {\n return resendVerificationResponse;\n }\n\n const emailDoc = userDoc.emails?.find((e) => e.address.toLowerCase() === email);\n\n if (!emailDoc || emailDoc.verified) {\n return resendVerificationResponse;\n }\n\n if (!getEmailConfig().provider) {\n throw new Error('Email provider is not configured');\n }\n\n await consumeRateLimit({\n bucket: 'verification',\n type: 'user',\n value: userDoc._id.toString(),\n message: 'Please wait at least 60 seconds before requesting another verification email',\n });\n\n await sendVerificationEmail({\n userId: userDoc._id,\n email,\n baseUrl: connectionInfo?.baseUrl,\n });\n\n return resendVerificationResponse;\n}\n","import bcrypt from 'bcrypt';\nimport { SignupProps, Context, Args } from '../methods/types';\nimport { usersCollection } from './db';\nimport { isDisposableEmail } from './disposableEmails';\nimport { consumeRateLimit } from '../rate-limit/rules';\nimport { sendVerificationEmail } from './verification';\nimport { validateEmail, validatePassword, validateProfileFields } from './validators';\nimport { getAuthConfig } from '@/app/authConfig';\nimport { resolveUniqueHandle, isDuplicateEmailError } from './utils';\nimport { ValidationError } from '../error';\n\nexport async function handleSignupWithPassword(\n props: Args,\n { user, session, connectionInfo }: Context\n) {\n const authConfig = getAuthConfig();\n try {\n if (user) {\n throw new ValidationError('User is already authenticated', 'ALREADY_AUTHENTICATED');\n }\n\n // Narrow once at the boundary\n const signupProps = props as SignupProps;\n const { firstName, lastName, avatarUrl, handle } = signupProps;\n\n const email = validateEmail(signupProps.email);\n const password = validatePassword(signupProps.password);\n\n const ip = connectionInfo?.ip;\n if (ip) {\n await consumeRateLimit({\n bucket: 'signupAttempt',\n type: 'ip',\n value: ip,\n });\n }\n\n if (!authConfig.allowDisposableEmails && (await isDisposableEmail(email))) {\n throw new Error('Please use a permanent email address');\n }\n\n // TODO: captcha check\n\n const existingUser = await usersCollection.findOne(\n { 'emails.address': email },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (existingUser) {\n throw new Error('Unable to create account');\n }\n\n await authConfig.onBeforeSignup?.({\n email,\n firstName,\n lastName,\n handle,\n provider: 'email',\n connectionInfo,\n });\n\n if (ip) {\n await consumeRateLimit({\n bucket: 'signup',\n type: 'ip',\n value: ip,\n });\n }\n\n // Validate optional profile fields (firstName, lastName, avatarUrl, handle)\n const profileFields = validateProfileFields({\n firstName,\n lastName,\n avatarUrl,\n handle,\n });\n\n await authConfig.validateSignup?.({\n email,\n password,\n ...profileFields,\n });\n\n // Runs on every password-setting path (see `resetPassword`), so a policy\n // configured here can't be bypassed by resetting to a weaker password.\n await authConfig.validatePassword?.({\n password,\n email,\n context: 'signup',\n });\n\n // Resolve unique handle\n let resolvedHandle: string;\n\n if (profileFields.handle) {\n resolvedHandle = await resolveUniqueHandle(profileFields.handle, email);\n } else if (authConfig.generateHandle) {\n const generated = await authConfig.generateHandle({\n email,\n firstName: profileFields.firstName,\n lastName: profileFields.lastName,\n });\n\n resolvedHandle = await resolveUniqueHandle(generated, email, {\n throwOnConflict: false,\n });\n } else {\n resolvedHandle = await resolveUniqueHandle(undefined, email);\n }\n\n // Hash password with bcrypt (salt is automatically generated)\n const hash = await bcrypt.hash(password, 10);\n\n let result;\n try {\n result = await usersCollection.insertOne({\n handle: resolvedHandle,\n status: 'active',\n emails: [\n {\n address: email,\n verified: false,\n },\n ],\n createdAt: new Date(),\n authMethods: {\n password: {\n hash,\n },\n },\n ...(profileFields.firstName !== undefined && { firstName: profileFields.firstName }),\n ...(profileFields.lastName !== undefined && { lastName: profileFields.lastName }),\n ...(profileFields.avatarUrl !== undefined && { avatarUrl: profileFields.avatarUrl }),\n });\n } catch (error) {\n if (isDuplicateEmailError(error)) {\n // A concurrent signup won the race between the findOne check above and\n // this insert; the unique emails.address index rejected the duplicate.\n // Surface the same message the pre-check would have.\n throw new Error('Unable to create account');\n }\n throw error;\n }\n\n const userDocument = await usersCollection.findOne(\n { _id: result.insertedId },\n { readPreference: 'primary' }\n );\n\n if (!userDocument) {\n throw new Error('User not found');\n }\n\n await sendVerificationEmail({\n userId: result?.insertedId,\n email,\n baseUrl: connectionInfo?.baseUrl,\n });\n\n authConfig.onAfterSignup?.({\n provider: 'email',\n user: userDocument,\n session,\n connectionInfo,\n });\n\n authConfig.signup?.onSuccess?.(userDocument);\n\n return result.insertedId;\n } catch (error) {\n if (error instanceof Error) {\n authConfig.onSignupError?.({\n provider: 'email',\n error,\n session,\n connectionInfo,\n });\n\n authConfig.signup?.onError?.(error);\n }\n throw error;\n }\n}\n","import { z } from 'zod';\nimport bcrypt from 'bcrypt';\nimport { randomBytes } from 'crypto';\n\nimport { Args, Context } from '@/methods/types';\nimport { ObjectId, RouteParams, RouteResponse } from '@/server';\nimport { usersCollection, resetPasswordTokensCollection, magicLinkTokensCollection } from './db';\nimport { getEmailConfig } from '@/app/emailConfig';\nimport { getAuthConfig } from '@/app/authConfig';\nimport { time } from '@/time';\nimport { htmlToText } from '@/utils';\nimport { validateEmail, validatePassword } from './validators';\nimport { consumeRateLimit } from '@/server';\nimport { getConfig } from '@/config/server';\nimport { invalidateAllUserSessions } from './session';\nimport { hashToken } from './tokenHash';\nimport { resolveUrl } from './utils';\n\n// Short-lived httpOnly cookie carrying the reset token from the landing route to\n// the `resetPassword` mutation, so it never appears on a client-rendered URL.\nconst RESET_PASSWORD_COOKIE = 'resetPasswordToken';\n\n// Scope the cookie to the API surface so it's only sent on the resetPassword request.\nconst RESET_PASSWORD_COOKIE_PATH = '/api/_internal/';\n\n// Attributes shared by the set and clear calls. Browsers only remove a cookie\n// when the clearing Set-Cookie matches the same path/secure/sameSite/httpOnly,\n// so both sites must use these identical flags or the httpOnly cookie can linger.\nconst RESET_PASSWORD_COOKIE_OPTIONS = {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n // `lax` so the cookie survives the email-link navigation; `strict` can drop\n // it when the SPA is reached cross-site (some webviews).\n sameSite: 'lax',\n path: RESET_PASSWORD_COOKIE_PATH,\n} as const;\n\n/**\n * Looks up a reset token without consuming it. Only matches the hashed form:\n * tokens are stored as `hashToken(rawToken)`, and the caller must present the\n * raw token. There is deliberately no plaintext fallback — matching the raw\n * value against the stored column would let a leaked SHA-256 digest be replayed\n * as a bearer token (submit the digest, it equals the stored value), defeating\n * the point of hashing at rest. Tokens expire within 1 hour (TTL index), so no\n * migration fallback is needed for tokens issued before hashing was deployed.\n */\nasync function findResetTokenDoc(rawToken: string) {\n return resetPasswordTokensCollection.findOne({ token: hashToken(rawToken) });\n}\n\n/**\n * Atomically claims a reset token by `_id` — the single-use enforcement point.\n * Of concurrent requests holding the same token, exactly one gets the doc back;\n * the rest get null. Returns the deleted doc, or null if already claimed.\n */\nasync function claimResetTokenById(id: ObjectId) {\n return resetPasswordTokensCollection.findOneAndDelete({ _id: id });\n}\n\nfunction defaultPasswordResetTemplate({ email, resetUrl }: { email: string; resetUrl: string }) {\n return `\n <p>Hi,</p>\n <p>We received a request to reset your password for ${email}.</p>\n <p>Click the link below to reset your password:</p>\n <p><a href=\"${resetUrl}\">${resetUrl}</a></p>\n <p>This link will expire in 1 hour.</p>\n <p>If you did not request this password reset, please ignore this email.</p>\n `;\n}\n\nconst passwordResetSent = {\n success: true,\n message: 'If an account with that email exists, a password reset link has been sent',\n};\n\nexport async function handleSendResetPasswordToken(args: Args, { connectionInfo }: Context) {\n const email = validateEmail(args.email as string);\n const ip = connectionInfo?.ip;\n\n if (ip) {\n await consumeRateLimit({\n bucket: 'passwordReset',\n type: 'ip',\n value: ip,\n });\n }\n\n await consumeRateLimit({\n bucket: 'passwordReset',\n type: 'email',\n value: email,\n });\n\n // Find user by email\n const userDoc = await usersCollection.findOne(\n { 'emails.address': email, status: { $nin: ['deleted', 'disabled'] } },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (!userDoc) {\n // For security, don't reveal if email exists or not\n return passwordResetSent;\n }\n\n // Intentionally NOT gated on an existing password: this is a set-OR-reset\n // flow. Passwordless accounts (magic link, OAuth-only) are the documented\n // way to *add* a password — `handleResetPassword` sets the hash whether or\n // not one existed. Requiring `authMethods.password` here would silently dead-\n // end those users (generic success, no email) despite the promised flow.\n // Ownership is still proven by receiving the emailed token, and the response\n // stays generic either way, so this doesn't enable account enumeration.\n\n const emailProvider = getEmailConfig().provider;\n if (!emailProvider) {\n throw new Error('Email provider is not configured');\n }\n\n // Generate reset token\n const resetToken = randomBytes(32).toString('hex');\n const now = Date.now();\n const createdAt = new Date(now);\n const expiresAt = new Date(now + time.hours(1)); // 1 hour expiry\n\n // Store only the hash, never the raw token (defense against db leaks).\n await resetPasswordTokensCollection.insertOne({\n userId: userDoc._id,\n email,\n token: hashToken(resetToken),\n createdAt,\n expiresAt,\n });\n\n // Point the email at the server landing route, not the SPA page, so the raw\n // token never reaches a client-rendered URL.\n const baseUrl = (getConfig('_system.site.url') as string | undefined) || connectionInfo?.baseUrl;\n if (!baseUrl) {\n // Without a base URL we'd email a broken `undefined/...` link — fail loudly.\n throw new Error(\n 'Unable to build password reset link: set _system.site.url (MODELENCE_SITE_URL)'\n );\n }\n const resetUrl = `${baseUrl}/api/_internal/auth/reset-password?token=${resetToken}`;\n\n // Send email\n const template = getEmailConfig()?.passwordReset?.template || defaultPasswordResetTemplate;\n const htmlTemplate = template({ email, resetUrl, name: '' });\n const textContent = htmlToText(htmlTemplate);\n\n await emailProvider.sendEmail({\n to: email,\n from: getEmailConfig()?.from || 'noreply@modelence.com',\n subject: getEmailConfig()?.passwordReset?.subject || 'Reset your password',\n text: textContent,\n html: htmlTemplate,\n });\n\n return passwordResetSent;\n}\n\n/**\n * Server landing route for password reset. The email links here instead of the\n * SPA page: we validate the token, stash it in a short-lived httpOnly cookie, and\n * 302-redirect to the tokenless SPA page — so the token never reaches client JS.\n */\nexport async function handleResetPasswordLanding(params: RouteParams): Promise<RouteResponse> {\n const baseUrl =\n (getConfig('_system.site.url') as string | undefined) ||\n `${params.req.protocol}://${params.req.get('host')}`;\n const resetPasswordUrl = resolveUrl(baseUrl, getEmailConfig().passwordReset?.redirectUrl);\n\n try {\n const token = z.string().parse(params.query.token);\n\n const resetTokenDoc = await findResetTokenDoc(token);\n if (!resetTokenDoc || resetTokenDoc.expiresAt < new Date()) {\n throw new Error('This password reset link is invalid or has expired.');\n }\n\n params.res.cookie(RESET_PASSWORD_COOKIE, token, {\n ...RESET_PASSWORD_COOKIE_OPTIONS,\n maxAge: time.hours(1),\n });\n\n return {\n status: 302,\n // Suppress the Referer so the token-bearing landing URL never leaks.\n headers: { 'Referrer-Policy': 'no-referrer' },\n redirect: resetPasswordUrl,\n };\n } catch (error) {\n // Surface a fixed, friendly message; never forward the raw error (ZodError or\n // DB error) into the redirect URL. Log the real cause server-side instead.\n console.error('Error handling password reset landing:', error);\n const message = 'This password reset link is invalid or has expired.';\n return {\n status: 302,\n headers: { 'Referrer-Policy': 'no-referrer' },\n redirect: `${resetPasswordUrl}?status=error&message=${encodeURIComponent(message)}`,\n };\n }\n}\n\nexport async function handleResetPassword(args: Args, context: Context) {\n // Prefer the token from the httpOnly cookie set by the landing route.\n // `req` is null for in-process invocations (no cookie to read).\n const cookieToken = context.req?.cookies?.[RESET_PASSWORD_COOKIE];\n\n // DEPRECATED fallback: token from request args, for older clients that submit it\n // directly. Reintroduces the leak risk the cookie exchange closes — rollout only.\n // TODO(reset-token-arg-fallback): remove the `args.token` path.\n if (!cookieToken && args.token) {\n console.warn(\n '[modelence] resetPassword received a token via request args instead of the httpOnly ' +\n 'cookie. This path is deprecated and will be removed; ensure password reset emails link ' +\n 'to /api/_internal/auth/reset-password so the token is exchanged server-side.'\n );\n }\n\n const token = z.string().parse(cookieToken ?? args.token);\n const password = validatePassword(args.password as string);\n\n const clearCookie = () => {\n // `res` is null for in-process invocations (no response to mutate).\n // Clear with the same flags used to set it; browsers ignore a clear whose\n // attributes don't match, which would leave the httpOnly cookie behind.\n context.res?.clearCookie(RESET_PASSWORD_COOKIE, RESET_PASSWORD_COOKIE_OPTIONS);\n };\n\n // Look up WITHOUT consuming: if validation/hashing fails (or the user is\n // missing), the link stays valid so the user can retry the same link.\n const resetTokenDoc = await findResetTokenDoc(token);\n if (!resetTokenDoc) {\n clearCookie();\n throw new Error('Invalid or expired reset token');\n }\n\n if (resetTokenDoc.expiresAt < new Date()) {\n // Expired: remove it and reject.\n await claimResetTokenById(resetTokenDoc._id as ObjectId);\n clearCookie();\n throw new Error('Reset token has expired');\n }\n\n // Find the user\n const userDoc = await usersCollection.findOne({ _id: resetTokenDoc.userId });\n if (!userDoc) {\n throw new Error('User not found');\n }\n\n // App-level password policy. Runs before the token is claimed below, so a\n // rejected password leaves the link valid and the user can retry with a\n // compliant one — same rationale as the deferred token lookup above.\n //\n // Legacy tokens predate the `email` field (see the guard on the verify write\n // below), so fall back to the user's first stored address. The hook still\n // runs either way: skipping the policy for those tokens would reopen the very\n // bypass it exists to close.\n await getAuthConfig().validatePassword?.({\n password,\n email: resetTokenDoc.email ?? userDoc.emails?.[0]?.address?.toLowerCase() ?? '',\n context: 'reset',\n });\n\n // Hash the new password\n const hash = await bcrypt.hash(password, 10);\n\n // Commit point: atomically claim the token. If a concurrent request already\n // consumed it, abort WITHOUT touching the password (single-use, no double-spend).\n const claimedToken = await claimResetTokenById(resetTokenDoc._id as ObjectId);\n if (!claimedToken) {\n clearCookie();\n throw new Error('Invalid or expired reset token');\n }\n\n // Update user's password\n await usersCollection.updateOne(\n { _id: userDoc._id },\n { $set: { 'authMethods.password.hash': hash } }\n );\n\n // Mark the email as verified since the user proved ownership via the reset token.\n // Match with strength-2 collation: `resetTokenDoc.email` is lowercased, but the\n // stored address may keep its original casing (e.g. OAuth-created accounts), and\n // without the collation the positional `$` update would match nothing — leaving\n // the email unverified after a successful reset and blocking password login when\n // verification is required.\n if (resetTokenDoc.email) {\n await usersCollection.updateOne(\n { _id: userDoc._id, 'emails.address': resetTokenDoc.email },\n { $set: { 'emails.$.verified': true } },\n { collation: { locale: 'en', strength: 2 } }\n );\n }\n\n // Invalidate all existing sessions for this user so that other browsers/devices\n // are forced to re-authenticate with the new password\n await invalidateAllUserSessions(userDoc._id);\n\n // Revoke every outstanding magic link token and one-time code for this user's\n // email addresses. A reset is meant to lock out anyone who had access; without\n // this, a magic link or code issued before the reset (valid for up to 15\n // minutes) would still log an attacker back in — bypassing the lockout.\n // Magic link tokens are always stored with a `validateEmail`-lowercased email,\n // while a user's stored addresses may keep their original casing (e.g. OAuth-\n // created accounts), so match on the lowercased form to line up with the\n // stored keys — covering every address, not just the one that was reset.\n const userEmails = Array.from(\n new Set(\n (userDoc.emails ?? [])\n .map((e) => e.address?.toLowerCase())\n .filter((address): address is string => Boolean(address))\n )\n );\n if (userEmails.length > 0) {\n await magicLinkTokensCollection.deleteMany({ email: { $in: userEmails } });\n }\n\n // Token already consumed at the commit point; just drop the cookie.\n clearCookie();\n\n return { success: true, message: 'Password has been reset successfully' };\n}\n","export function magicLinkTemplate({\n name,\n email,\n magicLinkUrl,\n code,\n}: {\n name?: string;\n email: string;\n magicLinkUrl: string;\n code: string;\n}) {\n return `\n <p>Hi${name ? ` ${name}` : ''},</p>\n <p>Click the link below to sign in as ${email}:</p>\n <p><a href=\"${magicLinkUrl}\">${magicLinkUrl}</a></p>\n <p>Or enter this one-time code in the app:</p>\n <p><strong style=\"font-size: 24px; letter-spacing: 4px;\">${code}</strong></p>\n <p>The link and code can only be used once and will expire in 15 minutes.</p>\n <p>If you did not request this, please ignore this email.</p>\n `;\n}\n","import { z } from 'zod';\nimport { randomBytes, randomInt } from 'crypto';\n\nimport { Args, ConnectionInfo, Context } from '@/methods/types';\nimport { ObjectId, RouteParams, RouteResponse } from '@/server';\nimport { usersCollection, magicLinkTokensCollection } from './db';\nimport { getEmailConfig } from '@/app/emailConfig';\nimport { getAuthConfig } from '@/app/authConfig';\nimport { time } from '@/time';\nimport { htmlToText } from '@/utils';\nimport { validateEmail } from './validators';\nimport { consumeRateLimit } from '@/server';\nimport { getConfig } from '@/config/server';\nimport { isDisposableEmail } from './disposableEmails';\nimport { setAuthTokenCookie, setSessionUser } from './session';\nimport { hashToken } from './tokenHash';\nimport { magicLinkTemplate } from './templates/magicLinkTemplate';\nimport {\n resolveUrl,\n resolveUniqueHandle,\n serializeUserForClient,\n isDuplicateEmailError,\n} from './utils';\nimport { Session, User } from './types';\n\n// Short-lived httpOnly cookie carrying the magic link token from the landing\n// route to the `loginWithMagicLink` mutation, so it never appears on a\n// client-rendered URL.\nconst MAGIC_LINK_COOKIE = 'magicLinkToken';\n\n// Scope the cookie to the API surface so it's only sent on the loginWithMagicLink request.\nconst MAGIC_LINK_COOKIE_PATH = '/api/_internal/';\n\n// Attributes shared by the set and clear calls. Browsers only remove a cookie\n// when the clearing Set-Cookie matches the same path/secure/sameSite/httpOnly,\n// so both sites must use these identical flags or the httpOnly cookie can linger.\nconst MAGIC_LINK_COOKIE_OPTIONS = {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n // `lax` so the cookie survives the email-link navigation; `strict` can drop\n // it when the SPA is reached cross-site (some webviews).\n sameSite: 'lax',\n path: MAGIC_LINK_COOKIE_PATH,\n} as const;\n\nconst MAGIC_LINK_EXPIRY_MINUTES = 15;\n\nconst ONE_TIME_CODE_LENGTH = 6;\n\n// A 6-digit code is guessable in a way a 32-byte token is not, so each doc\n// tolerates only a few wrong guesses before it can no longer be used.\nconst MAX_CODE_ATTEMPTS = 5;\n\n/**\n * Invokes a notify-only auth hook (onAfterLogin, onLoginError, ...) without\n * letting it affect the auth flow. These hooks are typed `() => void`, but a\n * user can still pass an async function — a rejection from it, never awaited,\n * would be an unhandled promise rejection (which can crash the process). A sync\n * throw is contained the same way. Deliberately NOT awaited: a failing\n * analytics hook must not fail an otherwise-successful login, and in error\n * paths it must not mask the real error being propagated.\n */\nfunction fireAuthHook(name: string, invoke: (() => void | Promise<void>) | undefined) {\n try {\n Promise.resolve(invoke?.()).catch((hookError: unknown) => {\n console.error(`Error in ${name} hook:`, hookError);\n });\n } catch (hookError) {\n console.error(`Error in ${name} hook:`, hookError);\n }\n}\n\nfunction isMagicLinkEnabled(): boolean {\n return Boolean(getAuthConfig().magicLink?.enabled);\n}\n\nfunction isMagicLinkSignupEnabled(): boolean {\n return Boolean(getAuthConfig().magicLink?.allowSignup);\n}\n\n/**\n * Looks up a magic link token without consuming it. Only matches the hashed\n * form: tokens are stored as `hashToken(rawToken)`, and the caller must present\n * the raw token. There is deliberately no plaintext fallback — matching the raw\n * value against the stored column would let a leaked SHA-256 digest be replayed\n * as a bearer token, defeating the point of hashing at rest.\n */\nasync function findMagicLinkTokenDoc(rawToken: string) {\n return magicLinkTokensCollection.findOne({ token: hashToken(rawToken) });\n}\n\n/**\n * Atomically claims a magic link token by `_id` — the single-use enforcement\n * point. Of concurrent requests holding the same token, exactly one gets the\n * doc back; the rest get null. Returns the deleted doc, or null if already claimed.\n */\nasync function claimMagicLinkTokenById(id: ObjectId) {\n return magicLinkTokensCollection.findOneAndDelete({ _id: id });\n}\n\n/**\n * Best-effort undo of a token claim: re-inserts the exact claimed doc (same\n * `_id`, hashes, and expiry) so the single-use link/code works again on retry.\n * Used when the work the claim was committing to (the signup pre-gate or the\n * account insert) failed without producing an account — otherwise the failure would permanently burn\n * the user's link. Never throws: the caller is already propagating the real\n * error, and a failed restore must not mask it.\n */\nasync function restoreClaimedMagicLinkToken(\n claimedToken: NonNullable<Awaited<ReturnType<typeof claimMagicLinkTokenById>>>\n) {\n try {\n await magicLinkTokensCollection.insertOne(claimedToken);\n } catch (restoreError) {\n console.error('Failed to restore a magic link token after a failed signup:', restoreError);\n }\n}\n\n// The same generic response is returned whether or not an account exists for\n// the email, to prevent user enumeration.\nconst magicLinkSent = {\n success: true,\n message: 'If this email can be used to sign in, a link has been sent',\n};\n\nexport async function handleSendMagicLink(args: Args, { connectionInfo }: Context) {\n if (!isMagicLinkEnabled()) {\n throw new Error('Magic link authentication is not enabled');\n }\n\n const email = validateEmail(args.email as string);\n const ip = connectionInfo?.ip;\n\n if (ip) {\n await consumeRateLimit({\n bucket: 'magicLink',\n type: 'ip',\n value: ip,\n });\n }\n\n await consumeRateLimit({\n bucket: 'magicLink',\n type: 'email',\n value: email,\n });\n\n // Checked up front (unlike password reset): with `allowSignup` on, unknown\n // emails also get a real email, so there is no guaranteed early-out path\n // that could otherwise skip this.\n const emailProvider = getEmailConfig().provider;\n if (!emailProvider) {\n throw new Error('Email provider is not configured');\n }\n\n // Same preflight reasoning: no fallback sender. A default like\n // `noreply@modelence.com` would send customer auth email from our domain —\n // and fail SPF/DKIM on their provider anyway — so fail loudly instead.\n const fromAddress = getEmailConfig().from;\n if (!fromAddress) {\n throw new Error('Email `from` address is not configured');\n }\n\n // Also a preflight: resolved before the token is stored, so a misconfigured\n // server fails loudly without leaving behind an orphaned (unsendable) token.\n // Config ONLY — no fallback to the request's Host-derived base URL: the\n // emailed link IS the credential, and a request-derived base would let a\n // spoofed Host header poison the link's destination (login-link poisoning).\n const baseUrl = getConfig('_system.site.url') as string | undefined;\n if (!baseUrl) {\n throw new Error('Unable to build magic link: set _system.site.url (MODELENCE_SITE_URL)');\n }\n\n // Runs identically for known and unknown emails, so the thrown error reveals\n // only a property of the email domain, never account existence.\n if (!getAuthConfig().allowDisposableEmails && (await isDisposableEmail(email))) {\n throw new Error('Please use a permanent email address');\n }\n\n const userDoc = await usersCollection.findOne(\n { 'emails.address': email },\n { collation: { locale: 'en', strength: 2 } }\n );\n\n if (userDoc && (userDoc.status === 'disabled' || userDoc.status === 'deleted')) {\n // For security, don't reveal the account state — skip the email silently.\n return magicLinkSent;\n }\n\n if (!userDoc && !isMagicLinkSignupEnabled()) {\n // Signup is not allowed, so a link for an unknown email could never be\n // used — skip it silently (same generic response) so account existence\n // is not revealed.\n return magicLinkSent;\n }\n\n // Generate the two credentials backed by the same doc: a long token for the\n // clickable link and a short code the user can type (e.g. on mobile, or when\n // reading the email on a different device).\n const magicLinkToken = randomBytes(32).toString('hex');\n const oneTimeCode = randomInt(0, 10 ** ONE_TIME_CODE_LENGTH)\n .toString()\n .padStart(ONE_TIME_CODE_LENGTH, '0');\n const now = Date.now();\n const createdAt = new Date(now);\n const expiresAt = new Date(now + time.minutes(MAGIC_LINK_EXPIRY_MINUTES));\n\n // Store only the hashes, never the raw values (defense against db leaks;\n // for the short code the attempt cap and expiry are the primary defense,\n // since its keyspace is small enough to brute-force a leaked hash offline).\n await magicLinkTokensCollection.insertOne({\n email,\n token: hashToken(magicLinkToken),\n code: hashToken(oneTimeCode),\n attempts: 0,\n createdAt,\n expiresAt,\n });\n\n // Point the email at the server landing route, not the SPA page, so the raw\n // token never reaches a client-rendered URL.\n const magicLinkUrl = `${baseUrl}/api/_internal/auth/magic-link?token=${magicLinkToken}`;\n\n // Send email\n const template = getEmailConfig()?.magicLink?.template || magicLinkTemplate;\n const htmlTemplate = template({ email, magicLinkUrl, code: oneTimeCode, name: '' });\n const textContent = htmlToText(htmlTemplate);\n\n await emailProvider.sendEmail({\n to: email,\n from: fromAddress,\n subject: getEmailConfig()?.magicLink?.subject || 'Your sign-in link',\n text: textContent,\n html: htmlTemplate,\n });\n\n return magicLinkSent;\n}\n\n/**\n * Server landing route for magic link sign-in. The email links here instead of\n * the SPA page: we validate the token, stash it in a short-lived httpOnly\n * cookie, and 302-redirect to the tokenless SPA page — so the token never\n * reaches client JS. The token is deliberately NOT consumed here: email\n * security scanners prefetch GET links, and consuming on GET would burn the\n * single-use token before the user ever clicks it. Consumption happens in the\n * `loginWithMagicLink` mutation triggered by the user's browser.\n */\nexport async function handleMagicLinkLanding(params: RouteParams): Promise<RouteResponse> {\n // Config ONLY — no fallback to the request's Host header, which is\n // attacker-controlled and must never decide where an auth flow redirects.\n // A link can only have been emailed with `_system.site.url` set, so this is\n // normally present; if it was unset since, fall back to a same-origin\n // relative redirect rather than trusting the request.\n const baseUrl = (getConfig('_system.site.url') as string | undefined) || '';\n const magicLinkPageUrl = resolveUrl(baseUrl, getEmailConfig().magicLink?.redirectUrl) || '/';\n\n try {\n const token = z.string().parse(params.query.token);\n\n const tokenDoc = await findMagicLinkTokenDoc(token);\n if (!tokenDoc || tokenDoc.expiresAt < new Date()) {\n throw new Error('This sign-in link is invalid or has expired.');\n }\n\n params.res.cookie(MAGIC_LINK_COOKIE, token, {\n ...MAGIC_LINK_COOKIE_OPTIONS,\n maxAge: time.minutes(MAGIC_LINK_EXPIRY_MINUTES),\n });\n\n return {\n status: 302,\n // Suppress the Referer so the token-bearing landing URL never leaks.\n headers: { 'Referrer-Policy': 'no-referrer' },\n redirect: magicLinkPageUrl,\n };\n } catch (error) {\n // Surface a fixed, friendly message; never forward the raw error (ZodError or\n // DB error) into the redirect URL. Log the real cause server-side instead.\n console.error('Error handling magic link landing:', error);\n const message = 'This sign-in link is invalid or has expired.';\n return {\n status: 302,\n headers: { 'Referrer-Policy': 'no-referrer' },\n redirect: `${magicLinkPageUrl}?status=error&message=${encodeURIComponent(message)}`,\n };\n }\n}\n\ntype MagicLinkAuthParams = {\n tokenDoc: { _id: ObjectId; email: string };\n session: Session;\n connectionInfo: ConnectionInfo;\n res: Context['res'];\n clearCookie: () => void;\n};\n\n// A magic link / one-time code proves ownership of the email, so authentication\n// is a find-or-create: an existing account logs in, an unknown email signs up\n// (when enabled). Mongo does both in one atomic upsert, so there is no\n// claim→insert→catch-duplicate→recover saga to compensate — see the retry note\n// on the E11000 branch for the one race the atomic op can't rule out by itself.\n\nconst USER_COLLATION = { locale: 'en', strength: 2 } as const;\n\n/**\n * The account-touching core of both entrypoints, given an already-validated\n * token doc. Claims the token (single-use commit point), then find-or-creates\n * the user atomically, verifies the clicked email if it wasn't already, binds\n * the session, and fires the create-path or login-path hooks accordingly.\n */\nasync function authenticateMagicLinkUser(params: MagicLinkAuthParams) {\n const { tokenDoc, session, connectionInfo, res, clearCookie } = params;\n const email = tokenDoc.email;\n const authConfig = getAuthConfig();\n const signupEnabled = isMagicLinkSignupEnabled();\n\n // Tracks whether this request is creating a new account, so a failure routes\n // to onSignupError vs onLoginError. Set in the pre-gate below the moment we\n // know the email is unknown (a signup), and kept in sync if the atomic\n // find-or-create later reveals a concurrent account (demoting to a login).\n let isSignupAttempt = false;\n\n try {\n // Commit point: atomically claim the single-use token. If a concurrent\n // request already consumed it, abort WITHOUT authenticating (no double-spend).\n // Claimed BEFORE the signup pre-gate below so that of concurrent requests\n // holding the same token doc (link + code used together, or a double-submitted\n // login) only the winner runs the signup side effects — a loser aborting here\n // has not re-run onBeforeSignup, burned a signup rate-limit slot, or promoted\n // itself to a signup attempt, so it routes to onLoginError instead of firing\n // onSignupError alongside the winner's onAfterSignup.\n const claimedToken = await claimMagicLinkTokenById(tokenDoc._id);\n if (!claimedToken) {\n clearCookie();\n throw new Error('Invalid or expired code or magic link');\n }\n\n // Signup-only side effects run BEFORE the account can exist, preserving\n // onBeforeSignup's veto contract and the pre-insert signup rate limit. The\n // upsert can't tell us \"is this a signup?\" until after it inserts, so we\n // detect the unknown-email case with a lookup here. A concurrent request\n // may still create the account between this read and the upsert — that race\n // resolves as a login (isNew=false) below, which is the correct outcome.\n let resolvedHandle: string | undefined;\n if (signupEnabled) {\n try {\n const existing = await usersCollection.findOne(\n { 'emails.address': email },\n { collation: USER_COLLATION }\n );\n if (!existing) {\n isSignupAttempt = true;\n await authConfig.onBeforeSignup?.({ email, provider: 'magicLink', connectionInfo });\n\n const ip = connectionInfo?.ip;\n if (ip) {\n await consumeRateLimit({ bucket: 'signup', type: 'ip', value: ip });\n }\n\n resolvedHandle = authConfig.generateHandle\n ? await resolveUniqueHandle(await authConfig.generateHandle({ email }), email, {\n throwOnConflict: false,\n })\n : await resolveUniqueHandle(undefined, email);\n }\n } catch (error) {\n // The pre-gate rejected (onBeforeSignup veto, rate limit) or failed\n // transiently — either way no account was created. The token was claimed\n // above; put it back so the failure doesn't permanently burn the link.\n await restoreClaimedMagicLinkToken(claimedToken);\n throw error;\n }\n }\n\n let upsertResult;\n try {\n upsertResult = await upsertMagicLinkUser(email, resolvedHandle, signupEnabled);\n } catch (error) {\n // The find-or-create failed transiently (e.g. a DB error) without\n // authenticating anyone. The token was claimed above — put it back so a\n // failure that wasn't the link's use doesn't permanently burn it.\n await restoreClaimedMagicLinkToken(claimedToken);\n throw error;\n }\n const { userDoc, isNew } = upsertResult;\n\n if (!userDoc) {\n // Reachable only when the op ran as a pure find (no insert) yet matched\n // nothing. Two cases, distinguished by whether the pre-gate resolved a\n // handle (i.e. whether this request was a signup):\n await restoreClaimedMagicLinkToken(claimedToken);\n clearCookie();\n if (signupEnabled && !isSignupAttempt) {\n // Signup is on and the pre-gate saw an existing account, so no handle\n // was resolved — but that account's emails were cleared (hard-delete)\n // before the find, so the match missed. Refusing to insert a handle-less\n // doc left userDoc null. This was a login whose account vanished\n // mid-flight; nothing was created, so let the user retry.\n throw new Error('User account is not active');\n }\n // Signup disabled and no account exists (with a permitted insert the op\n // always returns a doc). Defense in depth: the send path already refuses\n // unknown emails when signup is off, but a token can outlive its account\n // or the flag can flip between send and click. Nothing was created, so\n // restore the token rather than burning a link on a state the user can't\n // act on. This is a signup attempt the config forbids, so it routes to\n // onSignupError (as the old signup path).\n isSignupAttempt = true;\n throw new Error('Sign up with magic link is not enabled');\n }\n\n // The upsert is authoritative: a concurrent account means this was a login,\n // not the signup the pre-gate anticipated (demotes onSignupError→onLoginError).\n isSignupAttempt = isNew;\n\n if (userDoc.status === 'disabled' || userDoc.status === 'deleted') {\n // The link proved email possession but the account cannot be used. Leave\n // the token burned so it can't be retried against a re-enabled account.\n clearCookie();\n throw new Error('User account is not active');\n }\n\n let currentUserDoc = userDoc;\n // Clicking the emailed link proves ownership. On the insert path the email\n // is already stored verified; on the login path a concurrent password\n // signup may have left it unverified — this link is the proof that verifies\n // it. Skip the redundant write when already verified.\n const emailDoc = userDoc.emails?.find((e) => e.address.toLowerCase() === email);\n const wasUnverified = !isNew && !emailDoc?.verified;\n if (wasUnverified) {\n // Same strength-2 collation as the lookups: the stored address may keep\n // its original casing (e.g. OAuth-created accounts) while `email` is\n // lowercased, or the positional update silently matches nothing. Return\n // the updated doc so hooks and the response see `verified: true`.\n const verifiedDoc = await usersCollection.findOneAndUpdate(\n { _id: userDoc._id, 'emails.address': email },\n { $set: { 'emails.$.verified': true } },\n { collation: USER_COLLATION, returnDocument: 'after' }\n );\n currentUserDoc = verifiedDoc ?? userDoc;\n }\n\n await setSessionUser(session.authToken, currentUserDoc._id);\n\n if (res) {\n setAuthTokenCookie(res, session.authToken);\n }\n\n if (isNew) {\n fireAuthHook('onAfterSignup', () =>\n authConfig.onAfterSignup?.({\n provider: 'magicLink',\n user: currentUserDoc,\n session,\n connectionInfo,\n })\n );\n } else {\n if (wasUnverified) {\n fireAuthHook('onAfterEmailVerification', () =>\n authConfig.onAfterEmailVerification?.({\n provider: 'magicLink',\n user: currentUserDoc,\n session,\n connectionInfo,\n })\n );\n }\n\n fireAuthHook('onAfterLogin', () =>\n authConfig.onAfterLogin?.({\n provider: 'magicLink',\n user: currentUserDoc,\n session,\n connectionInfo,\n })\n );\n }\n\n clearCookie();\n\n return {\n user: serializeUserForClient(currentUserDoc),\n session: { authToken: session.authToken },\n };\n } catch (error) {\n // Route the failure to the hook matching what this request was attempting.\n // `isSignupAttempt` is true once the pre-gate sees an unknown email (so a\n // vetoing onBeforeSignup or a signup rate-limit rejection fires\n // onSignupError, matching the old signup path), and stays true only if the\n // account was actually created — the unknown→login race demotes it.\n if (error instanceof Error) {\n const hookName = isSignupAttempt ? 'onSignupError' : 'onLoginError';\n const hook = isSignupAttempt ? authConfig.onSignupError : authConfig.onLoginError;\n fireAuthHook(hookName, () =>\n hook?.({ provider: 'magicLink', error, session, connectionInfo })\n );\n }\n throw error;\n }\n}\n\n/**\n * Atomic find-or-create for the magic-link user. Returns the user (post-op)\n * and whether this call created it. `upsert` is gated on `signupEnabled`: when\n * signup is off this is a pure find (unknown email → `{ userDoc: null }`), so\n * no account is ever created for an email that could never sign up — no\n * insert-then-delete compensation.\n *\n * The `$elemMatch` selector (rather than `'emails.address'`) avoids the upsert\n * field-path conflict with `$setOnInsert: { emails: [...] }`. Concurrent upserts\n * can still race to insert the same email (a known Mongo caveat the unique\n * index turns into an E11000); a single retry re-runs the op, which now finds\n * the winner's doc and returns it as a login (`isNew: false`).\n *\n * The insert branch is additionally gated on a resolved handle: the pre-gate\n * only computes `resolvedHandle` when it sees an unknown email, so if a\n * concurrent hard-delete clears the existing account's emails between that read\n * and this op, the `$elemMatch` would miss and insert a doc with no handle\n * (violating the non-optional `handle` schema and its unique index). Refusing\n * the insert without a handle turns that race into a pure find returning\n * `null`, which the caller handles like signup-disabled — restore the token,\n * don't burn a link on a handle-less account.\n */\nasync function upsertMagicLinkUser(\n email: string,\n resolvedHandle: string | undefined,\n signupEnabled: boolean\n): Promise<{ userDoc: User | null; isNew: boolean }> {\n const canInsert = signupEnabled && resolvedHandle !== undefined;\n const runUpsert = () =>\n usersCollection.findOneAndUpsert(\n { emails: { $elemMatch: { address: email } } },\n {\n $setOnInsert: {\n handle: resolvedHandle as string,\n status: 'active',\n // Clicking the emailed link proves ownership of the address.\n emails: [{ address: email, verified: true }],\n createdAt: new Date(),\n // Magic link is proof of email possession — there is no per-user\n // credential to store, so no authMethods entry is added.\n authMethods: {},\n },\n },\n { upsert: canInsert, collation: USER_COLLATION }\n );\n\n try {\n const { doc, isNew } = await runUpsert();\n return { userDoc: doc as User | null, isNew };\n } catch (error) {\n if (!isDuplicateEmailError(error)) {\n throw error;\n }\n // Concurrent insert won the race; re-run to read the winner's doc. This can\n // only resolve as a match now (the account exists), so isNew is false.\n const { doc } = await runUpsert();\n return { userDoc: doc as User | null, isNew: false };\n }\n}\n\n/**\n * Consumes the magic link token stashed in the httpOnly cookie by the landing\n * route, logging in the matching user — or, when `magicLink.allowSignup` is\n * enabled, creating the account first when the email is unknown (combined\n * sign-in/sign-up, like OAuth).\n */\nexport async function handleLoginWithMagicLink(args: Args, context: Context) {\n const { session, connectionInfo, res, req } = context;\n\n if (!session) {\n throw new Error('Session is not initialized');\n }\n\n if (!isMagicLinkEnabled()) {\n throw new Error('Magic link authentication is not enabled');\n }\n\n // The token travels only in the httpOnly cookie set by the landing route —\n // there is deliberately no args fallback (new API, no legacy clients).\n const token = z.string().parse(req?.cookies?.[MAGIC_LINK_COOKIE]);\n\n const clearCookie = () => {\n // `res` is null for in-process invocations (no response to mutate).\n // Clear with the same flags used to set it; browsers ignore a clear whose\n // attributes don't match, which would leave the httpOnly cookie behind.\n res?.clearCookie(MAGIC_LINK_COOKIE, MAGIC_LINK_COOKIE_OPTIONS);\n };\n\n // Look up WITHOUT consuming: if a later step fails transiently, the link\n // stays valid so the user can retry it.\n const tokenDoc = await findMagicLinkTokenDoc(token);\n if (!tokenDoc) {\n clearCookie();\n throw new Error('Invalid or expired code or magic link');\n }\n\n if (tokenDoc.expiresAt < new Date()) {\n // Expired: remove it and reject.\n await claimMagicLinkTokenById(tokenDoc._id as ObjectId);\n clearCookie();\n throw new Error('Code or magic link has expired');\n }\n\n return authenticateMagicLinkUser({\n tokenDoc: { _id: tokenDoc._id as ObjectId, email: tokenDoc.email },\n session,\n connectionInfo,\n res,\n clearCookie,\n });\n}\n\n/**\n * Consumes the one-time code from the magic link email, logging in the\n * matching user — or, when `magicLink.allowSignup` is enabled, creating the\n * account first when the email is unknown. Same semantics as the link: the\n * code proves possession of the address.\n *\n * The typed-code path exists for contexts where the link chain breaks: native\n * apps without deep links set up, or reading the email on a different device\n * than the one signing in.\n */\nexport async function handleLoginWithOneTimeCode(args: Args, context: Context) {\n const { session, connectionInfo, res } = context;\n\n if (!session) {\n throw new Error('Session is not initialized');\n }\n\n if (!isMagicLinkEnabled()) {\n throw new Error('Magic link authentication is not enabled');\n }\n\n const email = validateEmail(args.email as string);\n // Tolerate the formatting users copy from the email (\"482 193\", \"482-193\").\n const code = z.string().parse(args.code).replace(/[\\s-]/g, '');\n\n const ip = connectionInfo?.ip;\n if (ip) {\n await consumeRateLimit({\n bucket: 'oneTimeCode',\n type: 'ip',\n value: ip,\n });\n }\n\n await consumeRateLimit({\n bucket: 'oneTimeCode',\n type: 'email',\n value: email,\n });\n\n // Look up WITHOUT consuming, keyed by email + hashed code. Docs that already\n // burned their guess allowance are excluded, so a capped code can never be\n // used even if eventually guessed right.\n const tokenDoc = await magicLinkTokensCollection.findOne({\n email,\n code: hashToken(code),\n attempts: { $lt: MAX_CODE_ATTEMPTS },\n });\n\n if (!tokenDoc) {\n // Count the failed guess against every outstanding code for this email —\n // attempts are tracked on the docs themselves so concurrent guesses are\n // counted atomically.\n await magicLinkTokensCollection.updateMany({ email }, { $inc: { attempts: 1 } });\n // Same message whether the code is wrong, capped, or the email unknown —\n // never reveal account existence.\n throw new Error('Invalid or expired code or magic link');\n }\n\n if (tokenDoc.expiresAt < new Date()) {\n // Expired: remove it and reject.\n await claimMagicLinkTokenById(tokenDoc._id as ObjectId);\n throw new Error('Code or magic link has expired');\n }\n\n return authenticateMagicLinkUser({\n tokenDoc: { _id: tokenDoc._id as ObjectId, email: tokenDoc.email },\n session,\n connectionInfo,\n res,\n // The code travels as a mutation argument — there is no cookie to clear.\n clearCookie: () => {},\n });\n}\n","import { ObjectId } from 'mongodb';\nimport { z } from 'zod';\n\nimport { getAuthConfig } from '@/app/authConfig';\nimport { consumeRateLimit } from '@/server';\nimport { AuthError, ValidationError } from '../error';\nimport { Args, Context } from '../methods/types';\nimport { usersCollection } from './db';\nimport { consumeOAuthExchangeCode, setSessionUser } from './session';\nimport type { OAuthProvider } from './types';\nimport { serializeUserForClient } from './utils';\n\n/**\n * Same message whether the code is unknown, already redeemed, expired, or bound\n * to an account that is no longer active — the client has no legitimate use for\n * the distinction, and telling them apart would let a caller probe for valid codes.\n */\nfunction invalidCodeError() {\n return new AuthError('Invalid or expired sign-in code', 'INVALID_OAUTH_CODE');\n}\n\n/**\n * Completes a native OAuth sign-in by exchanging the single-use code the\n * callback deep-linked back to the app for a real session.\n *\n * This is the second half of the mobile flow: the OAuth callback authenticates\n * the user through the ordinary find-or-create path and mints a code, and the\n * app redeems it here over TLS. The session is created at this point rather\n * than in the callback, so a code that is intercepted but never redeemed never\n * corresponds to a live session.\n */\nexport async function handleLoginWithOAuth(args: Args, { user, session, connectionInfo }: Context) {\n // Known only after the code is redeemed; until then a failure can't be\n // attributed to a specific provider.\n let provider: OAuthProvider | undefined;\n\n try {\n if (!session) {\n throw new Error('Session is not initialized');\n }\n\n // Matches handleLoginWithPassword: an authenticated session must not be\n // silently rebound to another account by a code arriving from anywhere.\n if (user) {\n throw new ValidationError('User is already authenticated', 'ALREADY_AUTHENTICATED');\n }\n\n const ip = connectionInfo?.ip;\n if (ip) {\n await consumeRateLimit({\n bucket: 'oauthExchange',\n type: 'ip',\n value: ip,\n });\n }\n\n const code = z.string().min(1).parse(args.code);\n\n // Optional at the schema level so a code minted by a previous-version\n // instance mid-deploy is still redeemable; whether it is actually required\n // is decided by the challenge stored with the code.\n const codeVerifier = z\n .string()\n .min(1)\n .optional()\n .parse(args.codeVerifier ?? undefined);\n\n // Commit point: single-use, so concurrent redemptions resolve to one winner.\n // Also where the device binding is enforced — a code minted against another\n // device's challenge does not survive this call.\n const claimed = await consumeOAuthExchangeCode(code, codeVerifier);\n if (!claimed || !ObjectId.isValid(claimed.userId)) {\n throw invalidCodeError();\n }\n provider = claimed.provider;\n\n // Re-check status here: the account may have been disabled between the\n // callback minting this code and the app redeeming it.\n const userDoc = await usersCollection.findOne({\n _id: new ObjectId(claimed.userId),\n status: { $nin: ['deleted', 'disabled'] },\n });\n\n if (!userDoc) {\n throw invalidCodeError();\n }\n\n await setSessionUser(session.authToken, userDoc._id);\n\n getAuthConfig().onAfterLogin?.({\n provider,\n user: userDoc,\n session,\n connectionInfo,\n });\n getAuthConfig().login?.onSuccess?.(userDoc);\n\n return {\n user: serializeUserForClient(userDoc),\n session: { authToken: session.authToken },\n };\n } catch (error) {\n if (error instanceof Error && provider) {\n getAuthConfig().onLoginError?.({\n provider,\n error,\n session,\n connectionInfo,\n });\n getAuthConfig().login?.onError?.(error);\n }\n throw error;\n }\n}\n","import { randomBytes } from 'crypto';\n\nimport type { AuthRateLimitsConfig } from '../app/authConfig';\nimport { Module } from '../app/module';\nimport { RateLimitRule } from '../rate-limit/types';\nimport { time } from '../time';\nimport {\n dbDisposableEmailDomains,\n emailVerificationTokensCollection,\n magicLinkTokensCollection,\n resetPasswordTokensCollection,\n usersCollection,\n} from './db';\nimport { updateDisposableEmailListCron } from './disposableEmails';\nimport { handleLoginWithPassword, handleLogout } from './login';\nimport { getOwnProfile, handleUpdateProfile } from './profile';\nimport { handleUnlinkOAuthProvider } from './unlinkOAuthProvider';\nimport { handleSignupWithPassword } from './signup';\nimport { handleVerifyEmail, handleResendEmailVerification } from './verification';\nimport {\n handleResetPassword,\n handleResetPasswordLanding,\n handleSendResetPasswordToken,\n} from './resetPassword';\nimport {\n handleLoginWithMagicLink,\n handleLoginWithOneTimeCode,\n handleMagicLinkLanding,\n handleSendMagicLink,\n} from './magicLink';\nimport { handleLoginWithOAuth } from './loginWithOAuth';\n\nfunction ruleKey(rule: Pick<RateLimitRule, 'bucket' | 'type' | 'window'>): string {\n return `${rule.bucket}\\n${rule.type}\\n${rule.window}`;\n}\n\nfunction defaultAuthRateLimits(): RateLimitRule[] {\n return [\n { bucket: 'signup', type: 'ip', window: time.minutes(15), limit: 20 },\n { bucket: 'signup', type: 'ip', window: time.days(1), limit: 200 },\n { bucket: 'signupAttempt', type: 'ip', window: time.minutes(15), limit: 50 },\n { bucket: 'signupAttempt', type: 'ip', window: time.days(1), limit: 500 },\n { bucket: 'signin', type: 'ip', window: time.minutes(15), limit: 50 },\n { bucket: 'signin', type: 'ip', window: time.days(1), limit: 500 },\n { bucket: 'verification', type: 'user', window: time.seconds(60), limit: 1 },\n { bucket: 'verification', type: 'user', window: time.days(1), limit: 10 },\n { bucket: 'passwordReset', type: 'ip', window: time.minutes(15), limit: 10 },\n { bucket: 'passwordReset', type: 'ip', window: time.days(1), limit: 100 },\n { bucket: 'passwordReset', type: 'email', window: time.hours(1), limit: 5 },\n { bucket: 'passwordReset', type: 'email', window: time.days(1), limit: 10 },\n { bucket: 'magicLink', type: 'ip', window: time.minutes(15), limit: 10 },\n { bucket: 'magicLink', type: 'ip', window: time.days(1), limit: 100 },\n { bucket: 'magicLink', type: 'email', window: time.hours(1), limit: 5 },\n { bucket: 'magicLink', type: 'email', window: time.days(1), limit: 10 },\n { bucket: 'oneTimeCode', type: 'ip', window: time.minutes(15), limit: 20 },\n { bucket: 'oneTimeCode', type: 'ip', window: time.days(1), limit: 100 },\n { bucket: 'oneTimeCode', type: 'email', window: time.hours(1), limit: 10 },\n { bucket: 'oneTimeCode', type: 'email', window: time.days(1), limit: 20 },\n // Redeeming a mobile OAuth exchange code. Codes are 256-bit and live for a\n // minute, so this guards against bulk guessing rather than a targeted one.\n { bucket: 'oauthExchange', type: 'ip', window: time.minutes(15), limit: 30 },\n { bucket: 'oauthExchange', type: 'ip', window: time.days(1), limit: 300 },\n { bucket: 'updateProfile', type: 'user', window: time.minutes(15), limit: 30 },\n { bucket: 'updateProfile', type: 'user', window: time.days(1), limit: 200 },\n ];\n}\n\nfunction collectOverrides(config: AuthRateLimitsConfig): RateLimitRule[] {\n const overrides: RateLimitRule[] = [];\n\n const buckets: Array<keyof AuthRateLimitsConfig> = [\n 'signup',\n 'signupAttempt',\n 'signin',\n 'verification',\n 'passwordReset',\n 'magicLink',\n 'oneTimeCode',\n 'oauthExchange',\n 'updateProfile',\n ];\n\n for (const bucket of buckets) {\n const rules = config[bucket];\n if (rules === undefined) continue;\n for (const rule of rules) {\n overrides.push({ bucket, ...rule });\n }\n }\n\n return overrides;\n}\n\n/**\n * Builds the rate limit rules for all authentication endpoints by merging\n * caller-supplied overrides into the built-in defaults.\n *\n * Merge semantics:\n * - Each override rule replaces the default rule with the same\n * (bucket, type, window) tuple.\n * - Defaults whose tuple is not overridden are preserved.\n * - Override rules with no matching default are added.\n *\n * This means `signup: [{ type: 'ip', window: time.minutes(15), limit: 10 }]`\n * tightens the 15-minute signup cap without dropping the per-day cap.\n */\nexport function buildAuthRateLimits(config: AuthRateLimitsConfig = {}): RateLimitRule[] {\n const defaults = defaultAuthRateLimits();\n const overrides = collectOverrides(config);\n\n const overrideByKey = new Map<string, RateLimitRule>();\n for (const rule of overrides) {\n overrideByKey.set(ruleKey(rule), rule);\n }\n\n const merged: RateLimitRule[] = [];\n const consumed = new Set<string>();\n for (const def of defaults) {\n const key = ruleKey(def);\n const override = overrideByKey.get(key);\n if (override !== undefined) {\n merged.push(override);\n consumed.add(key);\n } else {\n merged.push(def);\n }\n }\n for (const [key, rule] of overrideByKey) {\n if (!consumed.has(key)) {\n merged.push(rule);\n }\n }\n return merged;\n}\n\nexport async function createGuestUser() {\n // TODO: add rate-limiting and captcha handling\n\n const guestId = randomBytes(9)\n .toString('base64')\n .replace(/[+/]/g, (c) => (c === '+' ? 'a' : 'b'));\n\n const handle = `guest_${guestId}`;\n // TODO: re-try on handle collision\n\n const result = await usersCollection.insertOne({\n handle,\n status: 'active',\n createdAt: new Date(),\n authMethods: {},\n });\n\n return result.insertedId;\n}\n\nexport default new Module('_system.user', {\n stores: [\n usersCollection,\n dbDisposableEmailDomains,\n emailVerificationTokensCollection,\n resetPasswordTokensCollection,\n magicLinkTokensCollection,\n ],\n queries: {\n getOwnProfile,\n },\n mutations: {\n signupWithPassword: handleSignupWithPassword,\n loginWithPassword: handleLoginWithPassword,\n logout: handleLogout,\n resendEmailVerification: handleResendEmailVerification,\n sendResetPasswordToken: handleSendResetPasswordToken,\n resetPassword: handleResetPassword,\n sendMagicLink: handleSendMagicLink,\n loginWithMagicLink: handleLoginWithMagicLink,\n loginWithOneTimeCode: handleLoginWithOneTimeCode,\n loginWithOAuth: handleLoginWithOAuth,\n updateProfile: handleUpdateProfile,\n unlinkOAuthProvider: handleUnlinkOAuthProvider,\n },\n cronJobs: {\n updateDisposableEmailList: updateDisposableEmailListCron,\n },\n rateLimits: buildAuthRateLimits(),\n configSchema: {\n 'auth.email.enabled': {\n type: 'boolean',\n isPublic: true,\n default: true,\n },\n 'auth.email.from': {\n type: 'string',\n isPublic: false,\n default: '',\n },\n 'auth.email.verification': {\n type: 'boolean',\n isPublic: true,\n default: true,\n },\n 'auth.google.enabled': {\n type: 'boolean',\n isPublic: true,\n default: false,\n },\n 'auth.google.clientId': {\n type: 'string',\n isPublic: false,\n default: '',\n },\n 'auth.google.clientSecret': {\n type: 'secret',\n isPublic: false,\n default: '',\n },\n 'auth.github.enabled': {\n type: 'boolean',\n isPublic: true,\n default: false,\n },\n 'auth.github.clientId': {\n type: 'string',\n isPublic: false,\n default: '',\n },\n 'auth.github.clientSecret': {\n type: 'secret',\n isPublic: false,\n default: '',\n },\n // Deep links the mobile OAuth callback may redirect to, e.g. \"myapp://auth\",\n // separated by commas or newlines. Merged with `AuthConfig.mobile.redirectUrls`.\n // Not public: the client names its own redirect target, and publishing the\n // allowlist would only advertise what an attacker needs to guess.\n 'auth.mobile.redirectUrls': {\n type: 'text',\n isPublic: false,\n default: '',\n },\n },\n routes: [\n {\n path: '/api/_internal/auth/verify-email',\n handlers: {\n get: handleVerifyEmail,\n },\n },\n {\n // Server landing for password reset (see handleResetPasswordLanding).\n // Runs before the SPA catch-all.\n path: '/api/_internal/auth/reset-password',\n handlers: {\n get: handleResetPasswordLanding,\n },\n },\n {\n // Server landing for magic link sign-in (see handleMagicLinkLanding).\n // Runs before the SPA catch-all.\n path: '/api/_internal/auth/magic-link',\n handlers: {\n get: handleMagicLinkLanding,\n },\n },\n ],\n});\n","import { AppConfig, ConfigSchema, ConfigType } from './types';\n\ntype LocalConfigVariant = 'withRemoteServer' | 'withoutRemoteServer';\n\nconst localConfigMap = {\n withoutRemoteServer: {\n MONGODB_URI: '_system.mongodbUri',\n MONGODB_POOL_SIZE: '_system.mongodbPoolSize',\n MODELENCE_AUTH_GOOGLE_ENABLED: '_system.user.auth.google.enabled',\n MODELENCE_AUTH_GOOGLE_CLIENT_ID: '_system.user.auth.google.clientId',\n MODELENCE_AUTH_GOOGLE_CLIENT_SECRET: '_system.user.auth.google.clientSecret',\n MODELENCE_AUTH_GITHUB_ENABLED: '_system.user.auth.github.enabled',\n MODELENCE_AUTH_GITHUB_CLIENT_ID: '_system.user.auth.github.clientId',\n MODELENCE_AUTH_GITHUB_CLIENT_SECRET: '_system.user.auth.github.clientSecret',\n MODELENCE_AUTH_GITHUB_CLIENT_SCOPES: '_system.user.auth.github.scopes',\n MODELENCE_AUTH_MOBILE_REDIRECT_URLS: '_system.user.auth.mobile.redirectUrls',\n MODELENCE_EMAIL_RESEND_API_KEY: '_system.email.resend.apiKey',\n MODELENCE_EMAIL_AWS_SES_REGION: '_system.email.awsSes.region',\n MODELENCE_EMAIL_AWS_SES_ACCESS_KEY_ID: '_system.email.awsSes.accessKeyId',\n MODELENCE_EMAIL_AWS_SES_SECRET_ACCESS_KEY: '_system.email.awsSes.secretAccessKey',\n MODELENCE_EMAIL_SMTP_HOST: '_system.email.smtp.host',\n MODELENCE_EMAIL_SMTP_PORT: '_system.email.smtp.port',\n MODELENCE_EMAIL_SMTP_USER: '_system.email.smtp.user',\n MODELENCE_EMAIL_SMTP_PASS: '_system.email.smtp.pass',\n MODELENCE_SITE_URL: '_system.site.url',\n MODELENCE_ENV_TYPE: '_system.env.type',\n MODELENCE_MULTI_INSTANCE: '_system.multiInstance',\n // deprecated\n MODELENCE_ENV: '_system.env',\n GOOGLE_AUTH_ENABLED: '_system.user.auth.google.enabled',\n GOOGLE_AUTH_CLIENT_ID: '_system.user.auth.google.clientId',\n GOOGLE_AUTH_CLIENT_SECRET: '_system.user.auth.google.clientSecret',\n },\n withRemoteServer: {\n MODELENCE_SITE_URL: '_system.site.url',\n },\n} as const;\n\nfunction formatLocalConfigValue(value: string, type: ConfigType): string | number | boolean {\n if (type === 'number') {\n const numValue = Number(value);\n if (isNaN(numValue)) {\n throw new Error(`Invalid number value for config: ${value}`);\n }\n return numValue;\n }\n if (type === 'boolean') {\n if (value.toLowerCase() === 'true') {\n return true;\n }\n if (value.toLowerCase() === 'false') {\n return false;\n }\n throw new Error(`Invalid boolean value for config: ${value}`);\n }\n return value;\n}\n\nfunction getConfigsFromEnvMap(\n configMap: Record<string, string>,\n configSchema: ConfigSchema\n): AppConfig[] {\n const configs: AppConfig[] = [];\n\n for (const [envVar, configKey] of Object.entries(configMap)) {\n const value = process.env[envVar];\n const configSchemaEntry = configSchema[configKey];\n if (value) {\n const type = configSchemaEntry?.type ?? 'string';\n configs.push({\n key: configKey,\n type: type,\n value: formatLocalConfigValue(value, type),\n });\n }\n }\n\n return configs;\n}\n\n// Where an attached local dev server actually serves: the default for\n// `_system.site.url`, and what Studio shows when it has to tell someone the\n// environment is running on a developer's machine instead of in the cloud.\nexport function getLocalSiteUrl(): string {\n const port = process.env.MODELENCE_PORT || process.env.PORT || 3000;\n // Reads the env var directly on purpose: this is what *produces* the\n // `_system.site.url` config below, so getConfig would be circular.\n // eslint-disable-next-line no-restricted-syntax\n return process.env.MODELENCE_SITE_URL || `http://localhost:${port}`;\n}\n\nexport function getLocalConfigs(\n configSchema: ConfigSchema,\n variant: LocalConfigVariant = 'withoutRemoteServer'\n): AppConfig[] {\n const configMap = localConfigMap[variant];\n const configs = getConfigsFromEnvMap(configMap, configSchema);\n\n // Attached local dev (`modelence setup` writes the marker; Studio sandboxes\n // pre-set \"sandbox\"): default the site URL to this process instead of the\n // environment URL from the cloud config, so OAuth callbacks and email links\n // point at the server actually running. An explicit MODELENCE_SITE_URL wins.\n if (\n variant === 'withRemoteServer' &&\n process.env.MODELENCE_RUNTIME === 'local' &&\n !configs.some(({ key }) => key === '_system.site.url')\n ) {\n configs.push({\n key: '_system.site.url',\n type: 'string',\n value: getLocalSiteUrl(),\n });\n }\n\n return configs;\n}\n","import { createHash, randomUUID } from 'crypto';\nimport fs from 'fs';\nimport os from 'os';\nimport path from 'path';\n\nlet instanceId: string | undefined;\n\n// Random once-per-machine ID persisted in the user's home dir — outside any\n// repo so it can never be committed and cloned. Containers without a writable\n// home fall back to hostname, which is unique per container.\nfunction getMachineKey(): string {\n try {\n const dir = path.join(os.homedir(), '.modelence');\n const file = path.join(dir, 'machine-id');\n try {\n const existing = fs.readFileSync(file, 'utf8').trim();\n if (existing) {\n return existing;\n }\n } catch {\n // Missing or unreadable — generate below.\n }\n const generated = randomUUID();\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(file, generated + '\\n');\n return generated;\n } catch {\n return os.hostname();\n }\n}\n\n// Stable identity of \"this working copy connected to this environment\": the\n// same directory reconnecting (e.g. a dev-server restart) presents the same\n// ID, while the same directory pointed at a different environment gets a new\n// one — containers.id is unique across environments in Studio.\nexport function getInstanceId(): string {\n if (!instanceId) {\n instanceId = createHash('sha256')\n .update(`${getMachineKey()}\\n${process.cwd()}\\n${process.env.MODELENCE_ENVIRONMENT_ID || ''}`)\n .digest('hex')\n .slice(0, 32);\n }\n return instanceId;\n}\n\n// A dev-time instance — a developer machine or a Studio sandbox, as marked by\n// `modelence setup` — as opposed to a deployed cloud container.\nexport function isDevRuntime(): boolean {\n const runtime = process.env.MODELENCE_RUNTIME;\n return runtime === 'local' || runtime === 'sandbox';\n}\n\n// A developer's own machine — the only runtime that may force-take an\n// environment lease held by another instance.\nexport function isLocalRuntime(): boolean {\n return process.env.MODELENCE_RUNTIME === 'local';\n}\n\n// For dev runtimes the container ID is the instance ID; cloud containers keep\n// the Studio-issued MODELENCE_CONTAINER_ID.\nexport function getContainerId(): string | undefined {\n return isDevRuntime() ? getInstanceId() : process.env.MODELENCE_CONTAINER_ID;\n}\n","import os from 'os';\nimport { getContainerId, isDevRuntime, isLocalRuntime } from './instance';\nimport { getLocalSiteUrl } from '../config/local';\nimport { ConfigSchema } from '../config/types';\nimport { CronJobMetadata } from '../cron/types';\nimport { RoleDefinition } from '../auth/types';\nimport { Store } from '../data/store';\nimport { AppConfig } from '../config/types';\nimport { ModelSchema } from '../data/types';\n\ntype CloudBackendConnectOkResponse = {\n status: 'ok';\n configs: AppConfig[];\n environmentId: string;\n appAlias: string;\n environmentAlias: string;\n telemetry: {\n isEnabled: boolean;\n serviceName: string;\n };\n};\n\ntype CloudBackendConnectErrorResponse = {\n status: 'error';\n error: string;\n};\n\nexport type CloudBackendConnectResponse =\n | CloudBackendConnectOkResponse\n | CloudBackendConnectErrorResponse;\n\nexport async function connectCloudBackend({\n configSchema,\n cronJobsMetadata,\n stores,\n roles,\n}: {\n configSchema?: ConfigSchema;\n cronJobsMetadata?: CronJobMetadata[];\n stores?: Store<ModelSchema, Record<string, never>>[];\n roles?: Record<string, RoleDefinition>;\n}): Promise<CloudBackendConnectOkResponse> {\n const containerId = getContainerId();\n if (!containerId) {\n throw new Error('Unable to connect to Modelence Cloud: MODELENCE_CONTAINER_ID is not set');\n }\n\n // Set by `modelence dev --takeover`: Studio disconnects whoever holds the\n // environment (pausing a sandbox, or detaching another dev instance) and\n // hands over the lease in the same connect request. Only the `local`\n // runtime may take over — Studio enforces the same rule server-side.\n const takeover = isLocalRuntime() && process.env.MODELENCE_TAKEOVER === '1';\n\n try {\n const dataStores = (stores ?? []).map((store) => ({\n name: store.getName(),\n schema: store.getSerializedSchema(),\n collections: [store.getName()],\n version: 2,\n indexes: store.getIndexes(),\n searchIndexes: store.getSearchIndexes(),\n indexCreationMode: store.getIndexCreationMode(),\n }));\n\n const data = await callCloudApi<CloudBackendConnectResponse>('/api/connect', 'POST', {\n hostname: os.hostname(),\n runtime: process.env.MODELENCE_RUNTIME,\n containerId,\n ...(isLocalRuntime() ? { localSiteUrl: getLocalSiteUrl() } : {}),\n ...(takeover ? { force: true } : {}),\n dataModels: dataStores,\n configSchema,\n cronJobsMetadata,\n roles,\n });\n\n if (data.status === 'error') {\n throw Object.assign(new Error(`Unable to connect to Modelence Cloud: ${data.error}`), {\n responseBody: data,\n });\n }\n\n console.log('Successfully connected to Modelence Cloud');\n\n return data;\n } catch (error) {\n const cloudError = error as { message?: string; status?: number; responseBody?: unknown };\n\n // /api/connect's only 409 is \"another instance already holds this\n // environment\" — point a developer at the takeover flag.\n if (isLocalRuntime() && cloudError?.status === 409 && !takeover) {\n const detail =\n (cloudError.responseBody as { error?: string } | undefined)?.error ?? cloudError.message;\n console.error(\n `${detail}\\nRe-run with the --takeover flag (e.g. npm run dev -- --takeover) to ` +\n 'disconnect it and connect this instance instead.'\n );\n process.exit(1);\n }\n\n // A refusal the server explained (e.g. another instance already holds\n // this environment) is an expected condition on a dev machine: print the\n // message alone and exit instead of an uncaught stack dump.\n if (isDevRuntime() && (cloudError?.status ?? cloudError?.responseBody) !== undefined) {\n console.error(cloudError.message);\n process.exit(1);\n }\n console.error('Unable to connect to Modelence Cloud:', error);\n throw error;\n }\n}\n\nexport async function fetchConfigs() {\n return callCloudApi<{ configs: AppConfig[] }>('/api/configs', 'GET');\n}\n\nexport type CloudSyncResponse = { status: 'ok' } | { status: 'detached'; message?: string };\n\nexport async function syncStatus() {\n const data = await callCloudApi<CloudSyncResponse>('/api/sync', 'POST', {\n containerId: getContainerId(),\n });\n return data;\n}\n\nexport async function callCloudApi<T = unknown>(\n endpoint: string,\n method: string,\n payload?: object\n): Promise<T> {\n const { MODELENCE_SERVICE_ENDPOINT, MODELENCE_SERVICE_TOKEN } = process.env;\n\n if (!MODELENCE_SERVICE_ENDPOINT) {\n throw new Error('Unable to connect to Modelence Cloud: MODELENCE_SERVICE_ENDPOINT is not set');\n }\n\n const response = await fetch(`${MODELENCE_SERVICE_ENDPOINT}${endpoint}`, {\n method,\n headers: {\n Authorization: `Bearer ${MODELENCE_SERVICE_TOKEN}`,\n ...(payload ? { 'Content-Type': 'application/json' } : {}),\n },\n body: payload ? JSON.stringify(payload) : undefined,\n });\n\n if (!response.ok) {\n const data = await response.text();\n let parsed: unknown;\n let messageDetail: string = data;\n try {\n parsed = JSON.parse(data);\n const errorField = (parsed as { error?: unknown }).error;\n if (typeof errorField === 'string') {\n messageDetail = errorField;\n } else if (errorField && typeof errorField === 'object') {\n // Structured error envelope from newer endpoints — keep the body on\n // the thrown Error so callers can pull a code if they need to.\n const message = (errorField as { message?: unknown }).message;\n if (typeof message === 'string') {\n messageDetail = message;\n }\n }\n } catch {\n /* response was not JSON — fall back to raw text */\n }\n const error = new Error(\n `Unable to connect to Modelence Cloud: HTTP status: ${response.status}, ${messageDetail}`\n );\n if (parsed !== undefined) {\n (error as Error & { responseBody?: unknown }).responseBody = parsed;\n }\n (error as Error & { status?: number }).status = response.status;\n throw error;\n }\n\n if (response.status === 204 || response.headers?.get('content-length') === '0') {\n return undefined as T;\n }\n\n return (await response.json()) as T;\n}\n","import { time } from '../time';\nimport { fetchConfigs, syncStatus } from '../app/backendApi';\nimport { isDevRuntime } from '../app/instance';\nimport { getLocalConfigs } from './local';\nimport { loadConfigs, getSchema } from './server';\nimport { AppConfig } from './types';\n\nlet isSyncing = false;\n\nconst SYNC_INTERVAL = time.seconds(10);\n\nexport function startConfigSync() {\n setInterval(async () => {\n if (isSyncing) {\n return;\n }\n\n isSyncing = true;\n\n // TODO: move this sync outside of config\n try {\n const sync = await syncStatus();\n // Another instance took over this environment. The dev-runtime guard\n // makes sure a Studio bug can never take down cloud containers.\n if (sync?.status === 'detached' && isDevRuntime()) {\n console.error(\n `Detached from Modelence Cloud: ${sync.message ?? 'this environment is now connected from another instance.'}`\n );\n process.exit(1);\n }\n } catch (error) {\n console.error('Error syncing status', error);\n }\n\n try {\n await syncConfig();\n } catch (error) {\n console.error('Error syncing config', error);\n }\n\n isSyncing = false;\n }, SYNC_INTERVAL);\n}\n\nexport function loadRemoteConfigs(configs: AppConfig[]) {\n loadConfigs(configs);\n loadConfigs(getLocalConfigs(getSchema(), 'withRemoteServer'));\n}\n\nasync function syncConfig() {\n const { configs } = await fetchConfigs();\n loadRemoteConfigs(configs);\n}\n","import { schema } from '../data/types';\nimport { Store } from '../data/store';\n\n/**\n * Database collection for storing distributed locks.\n *\n * Locks are used to ensure that only one instance of the application can perform\n * a specific action at a time, such as running cron jobs or migrations\n */\nexport const locksCollection = new Store('_modelenceLocks', {\n schema: {\n _id: schema.string(), // unique identifier for the lock, used as the primary key\n instanceId: schema.string(),\n acquiredAt: schema.date(),\n\n resource: schema.string(), // deprecated, will be dropped in v1.0.0 (use _id instead)\n },\n indexes: [\n {\n // TODO(v1.0.0): remove after dropping legacy `resource` compatibility.\n key: { resource: 1 },\n unique: true,\n },\n {\n // TODO(v1.0.0): remove after dropping legacy `resource` compatibility.\n key: { resource: 1, instanceId: 1 },\n },\n {\n // TODO(v1.0.0): remove after dropping legacy `resource` compatibility.\n key: { resource: 1, acquiredAt: 1 },\n },\n ],\n indexCreationMode: 'blocking',\n});\n","import { randomBytes } from 'crypto';\nimport { MongoError } from 'mongodb';\nimport { locksCollection } from './db';\nimport { logDebug } from '../telemetry';\nimport { time } from '@/time';\n\n/**\n * In-memory cache to avoid frequent lock acquisition attempts\n * for the same resource within a short time frame.\n */\nconst lockCache: Record<\n string,\n {\n value: boolean;\n expiresAt: number;\n }\n> = {};\n\n/**\n * Time duration to consider a cached lock acquisition valid\n */\nconst DEFAULT_CACHE_DURATION = time.seconds(10);\n\n/**\n * Unique identifier for this application instance.\n * Generated once per application instance to track which container owns which locks.\n */\nconst INSTANCE_ID = randomBytes(32).toString('base64url');\n\n/**\n * Time after which a lock is expired\n */\nconst DEFAULT_LOCK_DURATION = time.seconds(30);\n\ntype LockOptions = {\n lockDuration?: number;\n successfulLockCacheDuration?: number;\n failedLockCacheDuration?: number;\n heartbeat?: boolean;\n bypassCache?: boolean;\n instanceId?: string;\n};\n\ntype LockHeartbeat = {\n timer: ReturnType<typeof setTimeout> | null;\n stopRequested: boolean;\n lockDuration: number;\n heartbeatInterval: number;\n};\n\nconst lockHeartbeats = new Map<string, LockHeartbeat>();\n\ntype DuplicateKeyMongoError = MongoError & {\n code?: number;\n keyPattern?: Record<string, unknown>;\n};\n\nconst isDuplicateKeyError = (error: unknown): error is DuplicateKeyMongoError =>\n error instanceof MongoError && error.code === 11000;\n\nconst hasKeyPatternField = (error: DuplicateKeyMongoError, field: string): boolean =>\n typeof error.keyPattern === 'object' &&\n error.keyPattern !== null &&\n Object.prototype.hasOwnProperty.call(error.keyPattern, field);\n\n/**\n * TODO(v1.0.0): Remove legacy `resource` fallback support.\n * This helper only exists for pre-1.0.0 lock documents where `_id !== resource`.\n * Delete this function and its call sites when releasing 1.0.0.\n */\nconst shouldFallbackToLegacyStrategy = async ({\n error,\n resource,\n}: {\n error: DuplicateKeyMongoError;\n resource: string;\n}): Promise<boolean> => {\n if (hasKeyPatternField(error, 'resource')) {\n return true;\n }\n\n if (hasKeyPatternField(error, '_id')) {\n return false;\n }\n\n const existingLock = (await locksCollection.findOne({ resource })) as {\n _id?: unknown;\n } | null;\n\n return !!existingLock && existingLock._id !== resource;\n};\n\nconst tryAcquireLockById = async ({\n resource,\n staleThresholdDate,\n instanceId,\n}: {\n resource: string;\n staleThresholdDate: Date;\n instanceId: string;\n}): Promise<boolean> => {\n const result = await locksCollection.upsertOne(\n {\n _id: resource,\n $or: [{ instanceId }, { acquiredAt: { $lt: staleThresholdDate } }],\n },\n {\n $set: {\n resource,\n instanceId,\n acquiredAt: new Date(),\n },\n $setOnInsert: {\n _id: resource,\n },\n }\n );\n\n return result.upsertedCount > 0 || result.modifiedCount > 0;\n};\n\nconst deleteLegacyLock = async ({\n resource,\n instanceId,\n staleThresholdDate,\n}: {\n resource: string;\n instanceId: string;\n staleThresholdDate?: Date;\n}): Promise<boolean> => {\n const selector = staleThresholdDate\n ? {\n resource,\n _id: { $ne: resource },\n $or: [{ instanceId }, { acquiredAt: { $lt: staleThresholdDate } }],\n }\n : {\n resource,\n instanceId,\n };\n\n const legacyDeleteResult = await locksCollection.deleteOne(selector);\n\n return legacyDeleteResult.deletedCount > 0;\n};\n\nconst stopLockHeartbeat = (resource: string) => {\n const heartbeatKey = resource;\n const heartbeat = lockHeartbeats.get(heartbeatKey);\n if (!heartbeat) {\n return;\n }\n\n heartbeat.stopRequested = true;\n if (heartbeat.timer) {\n clearTimeout(heartbeat.timer);\n heartbeat.timer = null;\n }\n\n lockHeartbeats.delete(heartbeatKey);\n};\n\nconst startLockHeartbeat = ({\n resource,\n lockDuration,\n instanceId,\n}: {\n resource: string;\n lockDuration: number;\n instanceId: string;\n}) => {\n const heartbeatInterval = Math.floor(lockDuration / 3);\n const heartbeatKey = resource;\n const existingHeartbeat = lockHeartbeats.get(heartbeatKey);\n\n if (\n existingHeartbeat &&\n !existingHeartbeat.stopRequested &&\n existingHeartbeat.heartbeatInterval === heartbeatInterval &&\n existingHeartbeat.lockDuration === lockDuration\n ) {\n return;\n }\n\n if (existingHeartbeat) {\n existingHeartbeat.stopRequested = true;\n if (existingHeartbeat.timer) {\n clearTimeout(existingHeartbeat.timer);\n existingHeartbeat.timer = null;\n }\n lockHeartbeats.delete(heartbeatKey);\n }\n\n const heartbeat: LockHeartbeat = {\n timer: null,\n stopRequested: false,\n lockDuration,\n heartbeatInterval,\n };\n\n const scheduleRefresh = () => {\n heartbeat.timer = setTimeout(() => {\n void acquireLock(resource, {\n lockDuration,\n bypassCache: true,\n instanceId,\n })\n .then((isLockAcquired) => {\n if (!isLockAcquired) {\n heartbeat.stopRequested = true;\n logDebug(`Lost lock while refreshing heartbeat: ${resource}`, {\n source: 'lock',\n resource,\n instanceId,\n });\n }\n })\n .finally(() => {\n if (heartbeat.stopRequested) {\n lockHeartbeats.delete(heartbeatKey);\n return;\n }\n scheduleRefresh();\n });\n }, heartbeatInterval);\n };\n\n lockHeartbeats.set(heartbeatKey, heartbeat);\n scheduleRefresh();\n};\n\n/**\n * Acquires a lock of the specified resource.\n * If the lock already exists and is owned by another container, this will fail.\n * If the lock is expired (no recent heartbeat), it will be taken over.\n *\n * @param resource - The type of lock to acquire\n * @param options.lockDuration - Time in ms after which a lock is considered expired (default: 30s)\n * @param options.successfulLockCacheDuration - Time in ms to cache successful lock acquisition\n * @param options.failedLockCacheDuration - Time in ms to cache failed lock acquisition\n * @param options.heartbeat - If true, auto-refreshes lock ownership at lockDuration/3 intervals\n * @param options.bypassCache - If true, skips the in-memory cache and always hits the DB\n * @param options.instanceId - The unique identifier for this application instance\n * @returns true if lock was acquired, false otherwise\n */\nexport async function acquireLock(\n resource: string,\n {\n lockDuration = DEFAULT_LOCK_DURATION,\n successfulLockCacheDuration = DEFAULT_CACHE_DURATION,\n failedLockCacheDuration = DEFAULT_CACHE_DURATION,\n heartbeat,\n bypassCache,\n instanceId = INSTANCE_ID,\n }: LockOptions = {}\n): Promise<boolean> {\n const now = Date.now();\n if (!bypassCache && lockCache[resource] && now < lockCache[resource].expiresAt) {\n if (lockCache[resource].value && heartbeat) {\n startLockHeartbeat({\n resource,\n lockDuration,\n instanceId,\n });\n }\n return lockCache[resource].value;\n }\n\n const staleThresholdDate = new Date(now - lockDuration);\n\n logDebug(`Attempting to acquire lock: ${resource}`, {\n source: 'lock',\n resource,\n instanceId,\n });\n\n try {\n const isLockAcquired = await acquireLockById({\n resource,\n staleThresholdDate,\n instanceId,\n });\n\n lockCache[resource] = {\n value: isLockAcquired,\n expiresAt: now + (isLockAcquired ? successfulLockCacheDuration : failedLockCacheDuration),\n };\n\n if (isLockAcquired) {\n if (heartbeat) {\n startLockHeartbeat({\n resource,\n lockDuration,\n instanceId,\n });\n }\n\n logDebug(`Lock acquired: ${resource}`, {\n source: 'lock',\n resource,\n instanceId,\n });\n } else {\n logDebug(`Failed to acquire lock (already held): ${resource}`, {\n source: 'lock',\n resource,\n instanceId,\n });\n }\n\n return isLockAcquired;\n } catch {\n lockCache[resource] = {\n value: false,\n expiresAt: now + failedLockCacheDuration,\n };\n logDebug(`Failed to acquire lock (already held): ${resource}`, {\n source: 'lock',\n resource,\n instanceId,\n });\n return false;\n }\n}\n\nconst acquireLockById = async ({\n resource,\n staleThresholdDate,\n instanceId,\n}: {\n resource: string;\n staleThresholdDate: Date;\n instanceId: string;\n}): Promise<boolean> => {\n try {\n return await tryAcquireLockById({ resource, staleThresholdDate, instanceId });\n } catch (error) {\n // TODO(v1.0.0): Remove the legacy fallback\n // Backward compatibility: if an old lock doc exists with a non-resource _id and\n // a unique `resource` index, upserting the new `_id = resource` shape can fail.\n // Migrate that legacy lock document to `_id = resource` and retry current strategy.\n if (isDuplicateKeyError(error) && (await shouldFallbackToLegacyStrategy({ error, resource }))) {\n const hasDeletedLegacyLock = await deleteLegacyLock({\n resource,\n staleThresholdDate,\n instanceId,\n });\n\n if (!hasDeletedLegacyLock) {\n return false;\n }\n\n try {\n return await tryAcquireLockById({ resource, staleThresholdDate, instanceId });\n } catch (retryError) {\n if (isDuplicateKeyError(retryError)) {\n return false;\n }\n throw retryError;\n }\n }\n\n // Duplicate _id means the lock exists but was not eligible for update (owned + fresh).\n if (isDuplicateKeyError(error)) {\n return false;\n }\n\n throw error;\n }\n};\n\n/**\n * Releases a lock of the specified resource owned by this container.\n *\n * @param resource - The resource to release the lock for\n * @param options.instanceId - The unique identifier for this application instance\n * @returns true if lock was released, false if lock wasn't owned by this container or release failed.\n */\nexport async function releaseLock(\n resource: string,\n {\n instanceId = INSTANCE_ID,\n }: {\n instanceId?: string;\n } = {}\n): Promise<boolean> {\n stopLockHeartbeat(resource);\n\n try {\n const result = await locksCollection.deleteOne({\n _id: resource,\n instanceId,\n });\n\n // TODO(v1.0.0): Remove the legacy fallback\n if (result.deletedCount === 0) {\n const hasDeletedLegacyLock = await deleteLegacyLock({\n resource,\n instanceId,\n });\n\n return hasDeletedLegacyLock;\n }\n\n return result.deletedCount > 0;\n } catch {\n return false;\n } finally {\n delete lockCache[resource];\n }\n}\n","// import { Worker, isMainThread, parentPort, workerData } from 'worker_threads';\n\nimport { time } from '../time';\nimport { CronJob, CronJobInputParams } from './types';\nimport { startTransaction, captureError } from '@/telemetry';\nimport { Module } from '../app/module';\nimport { schema } from '../data/types';\nimport { Store } from '../data/store';\nimport { acquireLock } from '../lock/helpers';\nimport { getMongodbUri } from '@/db/client';\n\nconst cronJobs: Record<string, CronJob> = {};\nlet cronJobsInterval: NodeJS.Timeout | null = null;\n\nconst cronJobsCollection = new Store('_modelenceCronJobs', {\n schema: {\n alias: schema.string(),\n lastStartDate: schema.date().optional(),\n },\n indexes: [{ key: { alias: 1 }, unique: true, background: true }],\n});\n\n// TODO: allow changing interval and timeout with cron jobconfigs\nexport function defineCronJob(\n alias: CronJob['alias'],\n {\n description = '',\n interval,\n timeout = Math.min(Math.max(interval, time.minutes(1)), time.days(1)),\n handler,\n }: CronJobInputParams\n) {\n if (cronJobs[alias]) {\n throw new Error(`Duplicate cron job declaration: '${alias}' already exists`);\n }\n\n if (cronJobsInterval) {\n throw new Error(\n `Unable to add a cron job - cron jobs have already been initialized: [${alias}]`\n );\n }\n\n if (interval < time.seconds(5)) {\n throw new Error(`Cron job interval should not be less than 5 second [${alias}]`);\n }\n\n if (timeout > time.days(1)) {\n throw new Error(`Cron job timeout should not be longer than 1 day [${alias}]`);\n }\n\n cronJobs[alias] = {\n alias,\n params: { description, interval, timeout },\n handler,\n state: {\n isRunning: false,\n },\n };\n}\n\nexport async function startCronJobs() {\n if (cronJobsInterval) {\n throw new Error('Cron jobs already started');\n }\n\n const aliasList = Object.keys(cronJobs);\n if (aliasList.length > 0) {\n if (!getMongodbUri()) {\n console.log('MongoDB URI is not configured. Skipping cron jobs.');\n return;\n }\n\n const aliasSelector = { alias: { $in: aliasList } };\n\n const cronJobRecords = await cronJobsCollection.fetch(aliasSelector);\n const now = Date.now();\n cronJobRecords.forEach((record) => {\n const job = cronJobs[record.alias];\n if (!job) {\n return;\n }\n job.state.scheduledRunTs = record.lastStartDate\n ? record.lastStartDate.getTime() + job.params.interval\n : now;\n });\n\n Object.values(cronJobs).forEach((job) => {\n if (!job.state.scheduledRunTs) {\n job.state.scheduledRunTs = now;\n }\n });\n\n cronJobsInterval = setInterval(tickCronJobs, time.seconds(1));\n }\n}\n\nasync function tickCronJobs() {\n const now = Date.now();\n\n const ownsLock = await acquireLock('cron', {\n successfulLockCacheDuration: time.seconds(10),\n failedLockCacheDuration: time.seconds(30),\n });\n\n if (!ownsLock) {\n return;\n }\n\n for (const job of Object.values(cronJobs)) {\n const { alias, params, state } = job;\n if (state.isRunning) {\n if (state.startTs && state.startTs + params.timeout < now) {\n const timeoutError = new Error(`Cron job '${alias}' timed out after ${params.timeout}ms`);\n captureError(timeoutError);\n state.isRunning = false;\n }\n continue;\n }\n\n // TODO: limit the number of jobs running concurrently\n\n if (state.scheduledRunTs && state.scheduledRunTs <= now) {\n void runCronJob(job);\n }\n }\n}\n\nasync function runCronJob(job: CronJob) {\n const { alias, params, handler, state } = job;\n state.isRunning = true;\n state.startTs = Date.now();\n\n const transaction = startTransaction('cron', `cron:${alias}`);\n // TODO: enforce job timeout\n try {\n await cronJobsCollection.upsertOne(\n { alias },\n {\n $set: { lastStartDate: new Date(state.startTs) },\n $setOnInsert: { alias },\n }\n );\n await handler();\n handleCronJobCompletion(state, params);\n transaction.end('success');\n } catch (err) {\n handleCronJobCompletion(state, params);\n const error = err instanceof Error ? err : new Error(String(err));\n captureError(error);\n transaction.end('error');\n console.error(`Error in cron job '${alias}':`, err);\n }\n}\n\nfunction handleCronJobCompletion(state: CronJob['state'], params: CronJob['params']) {\n state.scheduledRunTs = state.startTs ? state.startTs + params.interval : Date.now();\n state.startTs = undefined;\n state.isRunning = false;\n}\n\nexport function getCronJobsMetadata() {\n return Object.values(cronJobs).map(({ alias, params }) => ({\n alias,\n description: params.description,\n interval: params.interval,\n timeout: params.timeout,\n }));\n}\n\nexport default new Module('_system.cron', {\n stores: [cronJobsCollection],\n});\n\n// const runCronJob = () => {\n// const worker = new Worker(filePath, {\n// workerData: {},\n// execArgv: ['--loader', 'tsx'],\n// });\n\n// const timeoutId = setTimeout(() => {\n// worker.terminate();\n// console.error(`Cron job '${alias}' timed out after ${timeout}ms`);\n// }, timeout);\n\n// worker.on('message', (message) => {\n// if (message === 'done') {\n// clearTimeout(timeoutId);\n// worker.terminate();\n// }\n// });\n\n// worker.on('error', (err) => {\n// clearTimeout(timeoutId);\n// console.error(`Error in cron job '${alias}':`, err);\n// });\n\n// worker.on('exit', (code) => {\n// console.error(`Cron job '${alias}' exited with code ${code}`);\n// setTimeout(runCronJob, interval);\n// });\n// };\n","import { Store } from './store';\nimport { ModelSchema } from './types';\n\ntype NormalizedStore = Store<ModelSchema, Record<string, never>>;\n\nexport type ResolvedStores = {\n storesToInit: NormalizedStore[];\n effectiveStores: NormalizedStore[];\n};\n\n/**\n * Resolves module stores into two sets:\n *\n * - `storesToInit`: unique runtime instances that must be initialized.\n * - `effectiveStores`: the latest tail of each extension chain, used for\n * index reconciliation and cloud metadata.\n *\n * Throws if two unrelated chains share the same collection name.\n *\n * @internal\n */\nexport function resolveStores(stores: NormalizedStore[]): ResolvedStores {\n const uniqueStores = [...new Set(stores)];\n\n // Collect the chain tail for each unique root\n const rootToTail = new Map<NormalizedStore, NormalizedStore>();\n for (const store of uniqueStores) {\n const root = store.getChainRoot();\n rootToTail.set(root, root.getChainTail());\n }\n\n const effectiveStores = [...new Set(rootToTail.values())] as NormalizedStore[];\n\n // Detect collisions: different chains with the same collection name\n const nameToRoot = new Map<string, NormalizedStore>();\n for (const [root, tail] of rootToTail) {\n const name = tail.getName();\n const existing = nameToRoot.get(name);\n if (existing !== undefined && existing !== root) {\n throw new Error(\n `Store collision: multiple unrelated stores use collection name '${name}'. ` +\n `Use .extend() to create a single extension chain instead of independent stores.`\n );\n }\n nameToRoot.set(name, root);\n }\n\n return { storesToInit: uniqueStores, effectiveStores };\n}\n","import { Module } from '../app/module';\nimport { locksCollection } from './db';\n\n/**\n * Lock module for distributed locking across multiple instances.\n */\nexport default new Module('_system.lock', {\n stores: [locksCollection],\n});\n","import { Store } from '../data/store';\nimport { schema } from '../data/types';\n\nexport const dbMigrations = new Store('_modelenceMigrations', {\n schema: {\n version: schema.number(),\n status: schema.enum(['completed', 'failed']),\n description: schema.string().optional(),\n output: schema.string().optional(),\n appliedAt: schema.date(),\n },\n indexes: [{ key: { version: 1 }, unique: true }, { key: { version: 1, status: 1 } }],\n});\n","import { acquireLock, releaseLock } from '@/lock';\nimport { Module } from '../app/module';\nimport { dbMigrations } from './db';\nimport { logInfo } from '../telemetry';\n\nexport type MigrationScript = {\n version: number;\n description: string;\n handler: () => Promise<string | void>;\n};\n\ntype MigrationRunOptions = {\n lockMode?: 'acquire' | 'skip';\n};\n\nexport async function runMigrations(\n migrations: MigrationScript[],\n { lockMode = 'acquire' }: MigrationRunOptions = {}\n) {\n if (migrations.length === 0) {\n return;\n }\n\n if (lockMode === 'acquire') {\n const hasLock = await acquireLock('migrations');\n\n if (!hasLock) {\n logInfo('Another instance is running migrations. Skipping migration run.', {\n source: 'migrations',\n });\n return;\n }\n }\n\n try {\n const versions = migrations.map(({ version }) => version);\n\n const existingVersions = await dbMigrations.fetch({\n version: { $in: versions },\n });\n const existingVersionSet = new Set(existingVersions.map(({ version }) => version));\n const pendingMigrations = migrations.filter(({ version }) => !existingVersionSet.has(version));\n\n if (pendingMigrations.length === 0) {\n return;\n }\n\n logInfo(`Running migrations (${pendingMigrations.length})...`, {\n source: 'migrations',\n });\n for (const { version, description, handler } of pendingMigrations) {\n logInfo(`Running migration v${version}: ${description}`, {\n source: 'migrations',\n });\n try {\n const output = await handler();\n const outputStr = (output || '').toString().trim();\n const maxSize = 15 * 1024 * 1024; // 15MB (leaving 1MB buffer for other fields and MongoDB overhead)\n const truncatedOutput =\n outputStr.length > maxSize\n ? outputStr.slice(0, maxSize) + '\\n[Output truncated - exceeded size limit]'\n : outputStr;\n await dbMigrations.upsertOne(\n {\n version,\n },\n {\n $set: {\n version,\n status: 'completed',\n description,\n output: truncatedOutput,\n appliedAt: new Date(),\n },\n }\n );\n logInfo(`Migration v${version} complete`, {\n source: 'migrations',\n });\n } catch (e) {\n if (e instanceof Error) {\n await dbMigrations.upsertOne(\n {\n version,\n },\n {\n $set: {\n version,\n status: 'failed',\n description,\n output: e.message || '',\n appliedAt: new Date(),\n },\n }\n );\n logInfo(`Migration v${version} is failed: ${e.message}`, {\n source: 'migrations',\n });\n }\n }\n }\n } finally {\n if (lockMode === 'acquire') {\n await releaseLock('migrations');\n }\n }\n}\n\nexport function startMigrations(migrations: MigrationScript[]) {\n setTimeout(() => {\n runMigrations(migrations).catch((err) => {\n console.error('Error running migrations:', err);\n });\n }, 0);\n}\n\nexport default new Module('_system.migration', {\n stores: [dbMigrations],\n});\n","import { Module } from '../app/module';\nimport { dbRateLimits } from './db';\n\nexport default new Module('_system.rateLimit', {\n stores: [dbRateLimits],\n});\n","import { Module } from '../app/module';\nimport { callCloudApi } from '../app/backendApi';\nexport type { FileVisibility, GetUploadUrlResult } from './types';\nimport type { FileVisibility, GetUploadUrlResult } from './types';\n\ntype DownloadFileResult = {\n downloadUrl: string;\n};\n\ntype GetFileUrlResult = {\n url: string;\n};\n\nexport async function getUploadUrl({\n filePath,\n contentType,\n visibility,\n}: {\n filePath: string;\n contentType: string;\n visibility: FileVisibility;\n}): Promise<GetUploadUrlResult> {\n return await callCloudApi<GetUploadUrlResult>('/api/files/upload', 'POST', {\n filePath,\n contentType,\n visibility,\n });\n}\n\nexport async function deleteFile(filePath: string): Promise<void> {\n await callCloudApi<void>('/api/files/delete', 'POST', { filePath });\n}\n\nexport async function downloadFile(filePath: string): Promise<DownloadFileResult> {\n return await callCloudApi<DownloadFileResult>('/api/files/download', 'POST', { filePath });\n}\n\nexport async function getFileUrl(filePath: string): Promise<GetFileUrlResult> {\n return await callCloudApi<GetFileUrlResult>('/api/files/url', 'POST', { filePath });\n}\n\n// The file operations above are server-only. They proxy to Modelence Cloud\n// using the app's service token and accept any `filePath`, so they must NOT be\n// callable directly from an untrusted client — exposing them would let any\n// caller mint signed URLs for, download, or delete arbitrary paths. Apps reach\n// these through their own queries/mutations, which enforce authorization. The\n// module therefore registers no client-callable methods.\nexport default new Module('_system.files', {});\n","/**\n * Escape a JSON payload for safe embedding inside a `<script>` tag:\n * `</script>`, HTML chars, and U+2028/U+2029 (illegal in JS source).\n * Single-pass replace avoids order-dependent double-escape pitfalls.\n */\nexport function escapeJsonForScript(json: string): string {\n return json.replace(\n /[<>&\\u2028\\u2029]/g,\n (ch) => `\\\\u${ch.charCodeAt(0).toString(16).padStart(4, '0')}`\n );\n}\n","import {\n createServer,\n defineConfig,\n ViteDevServer,\n loadConfigFromFile,\n UserConfig,\n mergeConfig,\n Plugin,\n PluginOption,\n} from 'vite';\nimport reactPlugin from '@vitejs/plugin-react';\nimport path from 'path';\nimport fs from 'fs';\nimport express from 'express';\nimport type { AppServer, AppServerInitOptions, ExpressMiddleware } from './types';\nimport { escapeJsonForScript } from './ssr/escapeJsonForScript';\n\nconst CLIENT_BUILD_DIR = './.modelence/build/client'.replace(/\\\\/g, '/');\nconst SSR_BUILD_DIR = './.modelence/build/ssr'.replace(/\\\\/g, '/');\n// Resolved relative to Vite's `root` (./src/client).\nconst SSR_ENTRY_VIRTUAL_PATH = '/index.tsx';\n// Match the empty root container regardless of attribute order/quoting/extra\n// attributes (e.g. `<div id='root' class=\"app\">`) and whitespace variants Vite\n// may emit. Capture group 1 is the opening tag so its attributes are preserved\n// when we stream our own content into it.\nconst ROOT_PLACEHOLDER_REGEX = /(<div\\b[^>]*\\bid\\s*=\\s*[\"']root[\"'][^>]*>)\\s*<\\/div>/i;\nconst HEAD_CLOSE_TAG = '</head>';\n\nclass ViteServer implements AppServer {\n private viteServer?: ViteDevServer;\n private config?: UserConfig;\n private ssrEnabled = false;\n private ssrTransportInstalled = false;\n private prodEntryLoaded = false;\n // Manifest and template are immutable after build; cache to avoid sync fs\n // reads per request. Dev mode recomputes because HMR can shift the graph.\n private prodCssAssetsCache?: import('./ssr/collectCss').CssAssets;\n private prodTemplateCache?: string;\n\n enableSsr() {\n this.ssrEnabled = true;\n }\n\n async init({ httpServer }: AppServerInitOptions) {\n this.config = await getConfig(this.isDev() ? httpServer : undefined, { ssr: this.ssrEnabled });\n if (this.isDev()) {\n console.log('Starting Vite dev server...');\n this.viteServer = await createServer(this.config);\n } else if (this.ssrEnabled) {\n // `postBuildCommand` skips the Vite SSR build, so check upfront instead\n // of crashing on the first document request with a \"Cannot find module\".\n const ssrEntryPath = path.resolve(process.cwd(), SSR_BUILD_DIR, 'index.mjs');\n if (!fs.existsSync(ssrEntryPath)) {\n throw new Error(\n `Modelence: SSR is enabled (startApp({ ssr: true })) but the SSR ` +\n `bundle is missing at ${ssrEntryPath}.\\n\\n` +\n `This usually means \\`postBuildCommand\\` is set in modelence.config.ts, ` +\n `which replaces the default Vite client build (and the SSR build along ` +\n `with it). Either:\\n` +\n ` • remove \\`postBuildCommand\\` so Modelence builds the SSR bundle, or\\n` +\n ` • remove \\`ssr: true\\` from startApp() if your custom toolchain ` +\n `handles SSR itself.`\n );\n }\n }\n\n if (this.ssrEnabled && !this.ssrTransportInstalled) {\n const { installSsrCallMethodTransport } = await import('./ssr/transport');\n installSsrCallMethodTransport();\n this.ssrTransportInstalled = true;\n }\n }\n\n middlewares(): ExpressMiddleware[] {\n if (this.isDev()) {\n return (this.viteServer?.middlewares ?? []) as ExpressMiddleware[];\n }\n\n // `index: false` stops express.static from auto-serving `index.html`\n // for `/`, which would shadow the SSR handler. Asset requests still serve.\n const staticOptions = this.ssrEnabled ? { index: false } : undefined;\n const staticFolders = [express.static(CLIENT_BUILD_DIR, staticOptions)];\n if (this.config?.publicDir) {\n staticFolders.push(express.static(this.config.publicDir, staticOptions));\n }\n return staticFolders;\n }\n\n async handler(req: express.Request, res: express.Response) {\n if (this.ssrEnabled && isDocumentRequest(req)) {\n // Skip the SSR pipeline for HEAD — Node strips the body anyway.\n if (req.method === 'HEAD') {\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.setHeader('Cache-Control', 'no-store');\n res.status(200).end();\n return;\n }\n\n try {\n await this.handleSsr(req, res);\n } catch (error) {\n if (this.isDev() && this.viteServer && error instanceof Error) {\n this.viteServer.ssrFixStacktrace(error);\n }\n console.error('SSR render error:', {\n url: req.originalUrl,\n method: req.method,\n userAgent: req.get('user-agent'),\n error,\n });\n // Mid-stream: can't recover; terminate with a truncated page.\n if (res.headersSent) {\n res.end();\n return;\n }\n this.serveStaticShell(res);\n }\n return;\n }\n\n if (this.ssrEnabled) {\n res.status(404).end();\n return;\n }\n\n this.serveStaticShell(res);\n }\n\n private async handleSsr(req: express.Request, res: express.Response) {\n const template = await this.getTemplate(req.originalUrl);\n const snapshot = await this.captureSsrSnapshot();\n if (!snapshot) {\n throw new Error(\n 'Modelence SSR is enabled but no SSR snapshot was captured. ' +\n \"Make sure 'src/client/index.tsx' calls renderApp(...) from 'modelence/client'.\"\n );\n }\n\n // Lazy: keep react-dom/server off the boot path for non-SSR requests.\n const [{ renderSsrTreeStream }, { getCallContext }, cssModule] = await Promise.all([\n import('./ssr/render'),\n import('./app/server'),\n import('./ssr/collectCss'),\n ]);\n\n const callContext = await getCallContext(req, res);\n\n const cssAssets = this.collectCssAssets(cssModule);\n\n // 103 Early Hints so the browser prefetches CSS during render. No-op on\n // older Node / proxies that strip 1xx.\n sendEarlyHints(res, cssModule.buildEarlyHintsLink(cssAssets));\n\n const { sessionState, pipe, getQueryState } = await renderSsrTreeStream({\n callContext,\n loadingElement: snapshot.loadingElement,\n routesElement: snapshot.routesElement,\n router: snapshot.router,\n location: req.originalUrl,\n });\n\n const {\n prelude: rawPrelude,\n rootOpenTag,\n epilogue: rawEpilogue,\n } = splitTemplateAtRoot(template);\n const prelude = injectStylesheets(rawPrelude, cssModule.renderStylesheetLinks(cssAssets));\n\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.setHeader('Cache-Control', 'no-store');\n res.status(200);\n\n // Flush head + session state up front; session is resolved before render.\n res.write(prelude);\n res.write(\n `<script id=\"__MODELENCE_STATE__\" type=\"application/json\">${escapeJsonForScript(\n sessionState\n )}</script>`\n );\n res.write(rootOpenTag);\n\n await pipe(res as unknown as import('node:stream').Writable);\n\n res.write('</div>');\n res.write(\n `<script id=\"__MODELENCE_QUERY_STATE__\" type=\"application/json\">${escapeJsonForScript(\n getQueryState()\n )}</script>`\n );\n res.end(rawEpilogue);\n }\n\n private collectCssAssets(\n cssModule: typeof import('./ssr/collectCss')\n ): import('./ssr/collectCss').CssAssets {\n if (this.isDev()) {\n if (!this.viteServer) {\n return { hrefs: [], source: 'dev' };\n }\n return cssModule.collectDevCssAssets(this.viteServer, SSR_ENTRY_VIRTUAL_PATH);\n }\n\n if (!this.prodCssAssetsCache) {\n this.prodCssAssetsCache = cssModule.loadProdCssAssets(CLIENT_BUILD_DIR);\n }\n return this.prodCssAssetsCache;\n }\n\n private async getTemplate(url: string): Promise<string> {\n if (this.isDev()) {\n const templatePath = path.resolve(process.cwd(), 'src/client/index.html');\n let template = fs.readFileSync(templatePath, 'utf-8');\n if (this.viteServer) {\n template = await this.viteServer.transformIndexHtml(url, template);\n }\n return template;\n }\n\n if (!this.prodTemplateCache) {\n const templatePath = path.resolve(process.cwd(), CLIENT_BUILD_DIR, 'index.html');\n this.prodTemplateCache = fs.readFileSync(templatePath, 'utf-8');\n }\n return this.prodTemplateCache;\n }\n\n private async captureSsrSnapshot(): Promise<\n import('./client/renderApp').RenderAppOptions | null\n > {\n // Evaluating the entry runs `renderApp(...)`, which stores the snapshot\n // on globalThis. Read it immediately after evaluation.\n const { _getSsrSnapshot } = await import('./client/renderApp');\n\n if (this.isDev()) {\n // Re-evaluate per request so HMR edits apply.\n if (!this.viteServer) {\n throw new Error('Vite dev server not initialized');\n }\n await this.viteServer.ssrLoadModule(SSR_ENTRY_VIRTUAL_PATH);\n return _getSsrSnapshot();\n }\n\n if (!this.prodEntryLoaded) {\n await import(path.resolve(process.cwd(), SSR_BUILD_DIR, 'index.mjs'));\n this.prodEntryLoaded = true;\n }\n return _getSsrSnapshot();\n }\n\n private serveStaticShell(res: express.Response) {\n if (this.isDev()) {\n try {\n // Prevent browser from caching the HTML entrypoint in dev mode.\n // Vite's transformMiddleware uses no-cache + ETag for .ts/.tsx modules,\n // which revalidates correctly. But the HTML served by Express's sendFile\n // can be cached by the browser (e.g. bfcache on back/forward navigation).\n // Without HMR WebSocket, stale HTML leads to dynamic import() URLs that\n // reference modules the current Vite instance doesn't recognize.\n res.setHeader('Cache-Control', 'no-store');\n res.sendFile('index.html', { root: './src/client' });\n } catch (e) {\n console.error('Error serving index.html:', e);\n res.status(500).send('Internal Server Error');\n }\n } else {\n res.sendFile('index.html', { root: CLIENT_BUILD_DIR });\n }\n }\n\n private isDev() {\n return process.env.NODE_ENV !== 'production';\n }\n}\n\nfunction isDocumentRequest(req: express.Request): boolean {\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n return false;\n }\n\n // Treat as a document unless the client explicitly asked for something\n // other than HTML. `*/*` and missing Accept (curl, health checks) count\n // as document-ok; path-based filters below catch the genuinely non-doc cases.\n const accept = req.get('accept') ?? '';\n if (accept && !accept.includes('text/html') && !accept.includes('*/*')) {\n return false;\n }\n\n const pathname = (req.path ?? req.url ?? '').split('?')[0];\n\n if (pathname.startsWith('/api/')) {\n return false;\n }\n\n // Non-HTML extensions: favicons, sourcemaps, asset probes.\n const lastSegment = pathname.split('/').pop() ?? '';\n const dotIndex = lastSegment.lastIndexOf('.');\n if (dotIndex > 0) {\n const ext = lastSegment.slice(dotIndex).toLowerCase();\n if (ext !== '.html' && ext !== '.htm') {\n return false;\n }\n }\n\n return true;\n}\n\n/** @internal Exported for testing. */\nexport function splitTemplateAtRoot(template: string): {\n prelude: string;\n rootOpenTag: string;\n epilogue: string;\n} {\n const match = template.match(ROOT_PLACEHOLDER_REGEX);\n if (!match || match.index === undefined) {\n throw new Error('SSR template is missing the expected `<div id=\"root\"></div>` placeholder.');\n }\n const prelude = template.slice(0, match.index);\n const epilogue = template.slice(match.index + match[0].length);\n // Preserve the template's own opening tag (with any attributes) so the\n // streamed root matches what the client hydrates against.\n return { prelude, rootOpenTag: match[1], epilogue };\n}\n\n// Insert CSS <link>s before </head> so styles ship with the first chunk.\nfunction injectStylesheets(prelude: string, linksHtml: string): string {\n if (!linksHtml) {\n return prelude;\n }\n const headCloseIndex = prelude.lastIndexOf(HEAD_CLOSE_TAG);\n if (headCloseIndex === -1) {\n return linksHtml + prelude;\n }\n return prelude.slice(0, headCloseIndex) + linksHtml + prelude.slice(headCloseIndex);\n}\n\ntype EarlyHintsCapable = {\n writeEarlyHints?: (hints: { link?: string | string[] }) => void;\n};\n\n// 103 Early Hints (Node 18.11+); no-op when unsupported or proxies strip 1xx.\nfunction sendEarlyHints(res: express.Response, links: string[]) {\n if (links.length === 0) {\n return;\n }\n const target = res as unknown as EarlyHintsCapable;\n if (typeof target.writeEarlyHints !== 'function') {\n return;\n }\n try {\n target.writeEarlyHints({ link: links });\n } catch (error) {\n // Best-effort: some proxies reject 1xx after headers are touched.\n if (process.env.NODE_ENV !== 'production') {\n console.warn('Modelence SSR: writeEarlyHints failed', error);\n }\n }\n}\n\nasync function loadUserViteConfig() {\n const appDir = process.cwd();\n\n try {\n const result = await loadConfigFromFile(\n { command: 'serve', mode: 'development' },\n undefined,\n appDir\n );\n return result?.config || {};\n } catch (error) {\n console.warn(`Could not load vite config:`, error);\n return {};\n }\n}\n\nfunction safelyMergeConfig(baseConfig: UserConfig, userConfig: UserConfig) {\n const mergedConfig = mergeConfig(baseConfig, userConfig);\n\n // Deduplicate plugins by name, keeping user plugins over framework plugins\n if (mergedConfig.plugins && Array.isArray(mergedConfig.plugins)) {\n const seenPlugins = new Set<string>();\n mergedConfig.plugins = mergedConfig.plugins\n .flat()\n .filter((plugin: PluginOption) => {\n if (!plugin || typeof plugin !== 'object' || Array.isArray(plugin)) {\n return true;\n }\n const pluginName = (plugin as Plugin).name;\n if (!pluginName || seenPlugins.has(pluginName)) {\n return false;\n }\n seenPlugins.add(pluginName);\n return true;\n })\n .reverse(); // Reverse to prioritize user plugins over framework plugins\n mergedConfig.plugins.reverse(); // Reverse back to maintain original order\n }\n\n return mergedConfig;\n}\n\nasync function getConfig(httpServer?: import('http').Server, options: { ssr?: boolean } = {}) {\n const appDir = process.cwd();\n const userConfig = await loadUserViteConfig();\n\n const eslintConfigFile = [\n '.eslintrc.js',\n '.eslintrc.json',\n '.eslintrc',\n 'eslint.config.js',\n '.eslintrc.yml',\n '.eslintrc.yaml',\n ].find((file) => fs.existsSync(path.join(appDir, file)));\n\n const plugins = [reactPlugin(), modelenceAssetPlugin()];\n\n if (eslintConfigFile) {\n const eslintPlugin = (await import('vite-plugin-eslint')).default;\n plugins.push(\n eslintPlugin({\n failOnError: false,\n include: ['src/**/*.js', 'src/**/*.jsx', 'src/**/*.ts', 'src/**/*.tsx'],\n cwd: appDir,\n overrideConfigFile: path.resolve(appDir, eslintConfigFile),\n })\n );\n }\n\n const baseConfig = defineConfig({\n plugins,\n build: {\n outDir: CLIENT_BUILD_DIR,\n emptyOutDir: true,\n },\n server: {\n middlewareMode: true,\n hmr: httpServer ? { server: httpServer } : undefined,\n },\n appType: options.ssr ? 'custom' : 'spa',\n root: './src/client',\n resolve: {\n alias: {\n '@': path.resolve(appDir, 'src').replace(/\\\\/g, '/'),\n },\n },\n });\n\n return safelyMergeConfig(baseConfig, userConfig);\n}\n\nfunction modelenceAssetPlugin(): Plugin {\n return {\n name: 'modelence-asset-handler',\n async transform(code: string, id: string) {\n const assetRegex = /\\.(png|jpe?g|gif|svg|mpwebm|ogg|mp3|wav|flac|aac)$/;\n if (assetRegex.test(id)) {\n if (process.env.NODE_ENV === 'development') {\n return code;\n }\n // TODO: Upload to CDN\n // return `export default \"${cdnUrl}\"`;\n return code;\n }\n },\n };\n}\n\nexport const viteServer = new ViteServer();\n","import type { EmailPayload, EmailProvider } from '../types';\nimport { callCloudApi } from './backendApi';\n\ninterface ManagedEmailSendResponse {\n messageId: string;\n isOverage: boolean;\n acceptedRecipients: string[];\n}\n\ninterface ManagedEmailErrorBody {\n error?: { code?: string; message?: string };\n}\n\n// Studio's /api/email/send only accepts a curated subset of fields. The\n// sender domain is enforced server-side (locked to a Modelence-owned\n// subdomain in v1), so we drop the address half of `from` and forward only\n// the friendly name. cc/bcc/attachments/headers are not yet supported by the\n// relay — surfaced as a clear error before the network call.\nfunction extractFromName(from: string | undefined): string | undefined {\n if (!from) return undefined;\n const match = from.match(/^\\s*\"?([^\"<]+?)\"?\\s*<[^>]+>\\s*$/);\n return match?.[1]?.trim();\n}\n\nfunction normalizeAddressList(value: string | string[] | undefined): string[] | undefined {\n if (!value) return undefined;\n return Array.isArray(value) ? value : [value];\n}\n\nfunction buildRequestBody(payload: EmailPayload) {\n return {\n to: Array.isArray(payload.to) ? payload.to : [payload.to],\n subject: payload.subject,\n html: payload.html,\n text: payload.text,\n fromName: extractFromName(payload.from),\n replyTo: normalizeAddressList(payload.replyTo),\n };\n}\n\nfunction describeManagedEmailError(error: unknown): Error {\n if (!(error instanceof Error)) {\n return new Error('Managed email send failed');\n }\n const body = (error as Error & { responseBody?: ManagedEmailErrorBody }).responseBody;\n const code = body?.error?.code;\n const message = body?.error?.message;\n if (code && message) {\n return new Error(`Managed email rejected (${code}): ${message}`);\n }\n return error;\n}\n\nexport const managedEmailProvider: EmailProvider = {\n async sendEmail(payload: EmailPayload): Promise<void> {\n if (payload.cc || payload.bcc || payload.attachments || payload.headers) {\n throw new Error(\n 'Modelence managed email does not support cc, bcc, attachments, or custom headers in v1. ' +\n 'Configure your own provider (Resend, SES, SMTP) to use these features. ' +\n 'See https://docs.modelence.com/email/managed.'\n );\n }\n\n try {\n await callCloudApi<ManagedEmailSendResponse>(\n '/api/email/send',\n 'POST',\n buildRequestBody(payload)\n );\n } catch (error) {\n throw describeManagedEmailError(error);\n }\n },\n};\n","/**\n * Security configuration for the application\n *\n * By default, the app is protected against clickjacking by setting\n * `Content-Security-Policy: frame-ancestors 'self'` and `X-Frame-Options: SAMEORIGIN`\n * on all responses, preventing the app from being embedded in iframes on other domains.\n *\n * @example\n * ```typescript\n * import { startApp } from 'modelence/server';\n *\n * // Allow embedding in iframes on specific domains\n * startApp({\n * security: {\n * frameAncestors: ['https://modelence.com', 'https://app.example.com'],\n * trustedProxies: ['loopback', 'linklocal', 'uniquelocal'],\n * clientIpHeader: 'cf-connecting-ip',\n * },\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Allow a browser client on another origin to call this app's API (e.g. Expo\n * // Web, which Metro serves on :8081 while the API runs on :3000). Applies to\n * // module routes and framework API routes, not to SSR pages or static assets.\n * startApp({\n * security: {\n * allowedOrigins: ['http://localhost:8081'],\n * },\n * });\n * ```\n */\nexport type SecurityConfig = {\n /**\n * Additional origins allowed to embed this app in an iframe.\n * The app's own origin (`'self'`) is always included automatically.\n *\n * When not set, only same-origin framing is allowed.\n * When set, `X-Frame-Options` is omitted since it cannot express multiple origins.\n */\n frameAncestors?: string[];\n /**\n * Origins allowed to read this app's responses from a browser (CORS).\n *\n * Browsers block a cross-origin `fetch` unless the response carries a\n * matching `Access-Control-Allow-Origin`. The common case is Expo Web, which\n * Metro serves on a different port from the API — a different port is a\n * different origin, so every method call is blocked without this.\n *\n * Scope: this covers your module routes and the framework's own API routes\n * (method calls and the OAuth endpoints). SSR pages and static assets are\n * excluded, so a listed origin can call your API but cannot read your rendered\n * pages with credentials.\n *\n * The scope is derived from the routes actually registered, not matched by\n * path prefix: module routes carry no framework-imposed prefix (the docs'\n * example mounts `/todos` at the root), so a prefix rule would silently drop\n * CORS from the user-defined routes that most need it.\n *\n * Each entry must be an exact origin (`scheme://host[:port]`); patterns and\n * wildcards are not supported, since the response header carries one concrete\n * origin. Entries are normalized (trimmed, lowercased, default port and\n * trailing slash dropped) to match what browsers send, and anything that is\n * not a valid origin throws at startup rather than silently never matching.\n *\n * The matched origin is echoed back rather than `*`, and\n * `Access-Control-Allow-Credentials` is sent, so the browser will expose a\n * credentialed response to JS. Note this only covers same-site requests: the\n * auth cookie is `SameSite=Lax`, so a genuinely cross-site caller\n * (`app.example.com` → `api.other.com`) never has the cookie attached in the\n * first place, regardless of this setting. The Expo Web case works because\n * `localhost:8081` and `localhost:3000` differ only by port, and port is not\n * part of a site.\n *\n * Opt-in by design: when unset, no CORS headers are sent at all. Deployments\n * that already add CORS at a proxy or router therefore stay untouched — a\n * duplicated `Access-Control-Allow-Origin` is invalid and would break them.\n *\n * Native iOS/Android do not enforce CORS and never need this.\n */\n allowedOrigins?: string[];\n\n /**\n * IP addresses or CIDR ranges of reverse proxies that are allowed to supply\n * the client IP through `X-Forwarded-For`. This uses Express's `trust proxy`\n * address syntax, which also supports the named ranges `loopback`,\n * `linklocal`, and `uniquelocal`.\n *\n * For backward compatibility, all proxy addresses are trusted when neither\n * this option nor `MODELENCE_TRUSTED_PROXIES` is set. Configure one of them in\n * production so only addresses that cannot be reached directly by untrusted\n * clients are trusted. Once configured,\n * `connectionInfo.ip` is resolved by walking the proxy chain from the app\n * toward the client and stopping at the first untrusted address. This keeps a\n * caller from choosing its rate-limit identity by prepending a forged\n * `X-Forwarded-For` value.\n *\n * @example\n * ```typescript\n * trustedProxies: ['loopback', '10.0.0.0/8']\n * ```\n */\n trustedProxies?: string | string[];\n\n /**\n * Name of a single-value header the trusted proxy sets to the originating\n * client IP, used instead of walking the `X-Forwarded-For` chain.\n *\n * Cloudflare recommends reading `CF-Connecting-IP` (or `True-Client-IP` on\n * Enterprise plans) rather than `X-Forwarded-For`, because Cloudflare\n * *appends* to an inbound `X-Forwarded-For` instead of overwriting it, while\n * these headers always carry exactly one address.\n *\n * This header is only read when the immediate peer is a trusted proxy, so\n * `trustedProxies` (or `MODELENCE_TRUSTED_PROXIES`) must also be configured\n * with the proxy's addresses. Without that, a direct caller could set the\n * header themselves and choose their own rate-limit identity. When the peer\n * is untrusted or the header is absent, the IP falls back to the normal\n * `trust proxy` resolution.\n *\n * @example\n * ```typescript\n * // Behind Cloudflare, with Cloudflare's published ranges trusted:\n * clientIpHeader: 'cf-connecting-ip'\n * ```\n */\n clientIpHeader?: string;\n};\n\nlet securityConfig: SecurityConfig = Object.freeze({});\n\n/**\n * Canonicalizes a configured origin into the form browsers actually send in the\n * `Origin` header, so the middleware can match it with a plain string compare.\n *\n * Browsers lowercase the scheme and host, omit the default port, and never\n * include a path — so `http://LOCALHOST:8081` and `http://localhost:8081/` must\n * both collapse to `http://localhost:8081` or they would silently never match.\n *\n * Throws rather than skipping a bad entry: a typo here surfaces to the developer\n * as an opaque CORS failure in the browser with nothing logged server-side, so\n * failing at startup is the only place it is cheap to diagnose.\n */\nfunction normalizeOrigin(entry: string): string {\n if (typeof entry !== 'string' || entry.trim() === '') {\n throw new Error(\n `Invalid security.allowedOrigins entry: expected a non-empty string, received ${JSON.stringify(entry)}.`\n );\n }\n\n const trimmed = entry.trim();\n\n // Called out separately from the generic parse failure below: '*' is a\n // plausible guess that would parse as neither an origin nor a URL, and the\n // reason it is unsupported (credentialed requests) is worth stating.\n if (trimmed === '*') {\n throw new Error(\n \"Invalid security.allowedOrigins entry '*': wildcards are not supported, since the \" +\n 'response echoes one concrete origin to keep credentialed requests working. ' +\n 'List each allowed origin explicitly.'\n );\n }\n\n let url: URL;\n try {\n url = new URL(trimmed);\n } catch {\n throw new Error(\n `Invalid security.allowedOrigins entry ${JSON.stringify(entry)}: expected an origin of the ` +\n \"form 'scheme://host[:port]' (for example 'http://localhost:8081').\"\n );\n }\n\n // URL keeps userinfo and a query/fragment out of `origin`, so they would be\n // silently dropped rather than rejected. Reject them: they signal the author\n // meant something the allowlist cannot express.\n if (url.username || url.password) {\n throw new Error(\n `Invalid security.allowedOrigins entry ${JSON.stringify(entry)}: an origin must not carry credentials.`\n );\n }\n if (url.search || url.hash) {\n throw new Error(\n `Invalid security.allowedOrigins entry ${JSON.stringify(entry)}: an origin must not carry a query or fragment.`\n );\n }\n // Checked before the path rule below: for a non-special scheme the whole\n // opaque body lands in `pathname`, so the path rule would fire first and\n // suggest the useless 'null' origin. `localhost:8081` (no scheme) parses this\n // way too, which is exactly the typo worth naming clearly.\n //\n // Non-special schemes (and opaque ones like `data:`) serialize as 'null',\n // which would otherwise match the opaque origin sandboxed iframes send.\n if (url.origin === 'null') {\n throw new Error(\n `Invalid security.allowedOrigins entry ${JSON.stringify(entry)}: expected an origin of the ` +\n \"form 'scheme://host[:port]' (for example 'http://localhost:8081'). \" +\n `Scheme ${JSON.stringify(url.protocol.replace(':', ''))} does not form a comparable origin.`\n );\n }\n // `*` is a legal URL hostname character, so `https://*.example.com` parses and\n // would be stored as-is. Browsers never send a literal `*` in `Origin`, so the\n // entry could only ever fail to match — the same silent CORS failure the bare\n // '*' check above exists to prevent.\n if (url.hostname.includes('*')) {\n throw new Error(\n `Invalid security.allowedOrigins entry ${JSON.stringify(entry)}: wildcards are not supported, ` +\n 'since the response echoes one concrete origin to keep credentialed requests working. ' +\n 'List each allowed origin explicitly.'\n );\n }\n // A bare origin parses with pathname '/' — anything longer is a real path.\n if (url.pathname !== '/') {\n throw new Error(\n `Invalid security.allowedOrigins entry ${JSON.stringify(entry)}: an origin must not include a path. ` +\n `Use ${JSON.stringify(url.origin)} instead.`\n );\n }\n\n // `URL.origin` is exactly the serialization browsers send: lowercased, default\n // port dropped, no trailing slash.\n return url.origin;\n}\n\nfunction normalizeAllowedOrigins(allowedOrigins: string[]): string[] {\n if (!Array.isArray(allowedOrigins)) {\n throw new Error(\n `Invalid security.allowedOrigins: expected an array of origin strings, received ${JSON.stringify(allowedOrigins)}.`\n );\n }\n return allowedOrigins.map(normalizeOrigin);\n}\n\nexport function setSecurityConfig(newSecurityConfig: SecurityConfig) {\n const validated = newSecurityConfig.allowedOrigins\n ? {\n ...newSecurityConfig,\n allowedOrigins: normalizeAllowedOrigins(newSecurityConfig.allowedOrigins),\n }\n : newSecurityConfig;\n securityConfig = Object.freeze(Object.assign({}, securityConfig, validated));\n}\n\nexport function getSecurityConfig() {\n return securityConfig;\n}\n","import { WebsocketServerProvider } from '@/websocket/types';\n\nexport type WebsocketConfig = {\n provider?: WebsocketServerProvider;\n};\n\nlet websocketConfig: WebsocketConfig = Object.freeze({});\n\nexport function setWebsocketConfig(newWebsocketConfig: WebsocketConfig) {\n websocketConfig = Object.freeze(Object.assign({}, websocketConfig, newWebsocketConfig));\n}\n\nexport function getWebsocketConfig() {\n return websocketConfig;\n}\n","import dotenv from 'dotenv';\nimport fs from 'fs/promises';\nimport os from 'os';\nimport path from 'path';\n\nimport type { AppServer, ModelSchema } from '../types';\nimport socketioServer from '@/websocket/socketio/server';\nimport { initRoles } from '../auth/role';\nimport sessionModule from '../auth/session';\nimport { RoleDefinition } from '../auth/types';\nimport userModule from '../auth/user';\nimport { getLocalConfigs } from '../config/local';\nimport { loadConfigs, setSchema } from '../config/server';\nimport { startConfigSync, loadRemoteConfigs } from '../config/sync';\nimport { ConfigSchema } from '../config/types';\nimport cronModule, { defineCronJob, getCronJobsMetadata, startCronJobs } from '../cron/jobs';\nimport { type IndexReconcileMode, Store } from '../data/store';\nimport { resolveStores } from '../data/resolveStores';\nimport { connect, getClient, getMongodbUri } from '../db/client';\nimport { _createSystemMutation, _createSystemQuery, createMutation, createQuery } from '../methods';\nimport {\n MigrationScript,\n default as migrationModule,\n runMigrations,\n startMigrations,\n} from '../migration';\nimport rateLimitModule from '../rate-limit';\nimport { initRateLimits } from '../rate-limit/rules';\nimport systemModule from '../system';\nimport lockModule, { acquireLock, releaseLock } from '../lock';\nimport filesModule from '../files';\nimport { viteServer } from '../viteServer';\nimport { connectCloudBackend } from './backendApi';\nimport { initMetrics } from './metrics';\nimport { Module } from './module';\nimport { startServer } from './server';\nimport { markAppStarted, setMetadata } from './state';\nimport { time } from '@/time';\nimport { EmailConfig, setEmailConfig } from './emailConfig';\nimport { managedEmailProvider } from './managedEmailProvider';\nimport { AuthConfig, setAuthConfig } from './authConfig';\nimport { SecurityConfig, setSecurityConfig } from './securityConfig';\nimport { WebsocketConfig, setWebsocketConfig } from './websocketConfig';\nimport { buildAuthRateLimits } from '../auth/user';\n\nexport type AppOptions = {\n modules?: Module[];\n server?: AppServer;\n email?: EmailConfig;\n auth?: AuthConfig;\n /** Security settings such as clickjacking protection. See {@link SecurityConfig}. */\n security?: SecurityConfig;\n /**\n * Custom role definitions keyed by role name. Defined roles are synced to the\n * Modelence Cloud dashboard for user management. See {@link RoleDefinition}.\n *\n * @example\n * ```typescript\n * startApp({\n * roles: {\n * admin: { description: 'Full access to all features' },\n * editor: { description: 'Can edit content' },\n * viewer: {},\n * },\n * });\n * ```\n */\n roles?: Record<string, RoleDefinition>;\n /** @internal */\n defaultRoles?: Record<string, string>;\n migrations?: Array<MigrationScript>;\n websocket?: WebsocketConfig;\n /** Enable server-side rendering of the user's React tree. */\n ssr?: boolean;\n};\n\nexport async function startApp({\n modules = [],\n roles = {},\n defaultRoles = {},\n server = viteServer,\n migrations = [],\n email = {},\n auth = {},\n security = {},\n websocket = {},\n ssr = false,\n}: AppOptions) {\n if (ssr && server === viteServer) {\n viteServer.enableSsr();\n }\n dotenv.config();\n\n dotenv.config({ path: '.modelence.env' });\n\n const hasRemoteBackend = Boolean(process.env.MODELENCE_SERVICE_ENDPOINT);\n\n trackAppStart()\n .then(() => {\n // Do nothing\n })\n .catch(() => {\n // Silently ignore tracking errors to not disrupt app startup\n });\n\n for (const module of modules) {\n if (module.name.toLowerCase().startsWith('_system.')) {\n throw new Error(\n `Invalid module name: '${module.name}'\\n\\n` +\n `The '_system.' prefix is reserved for internal use and cannot be used in user-defined modules.\\n\\n` +\n `Rename your module to something that does not start with '_system.'`\n );\n }\n }\n const systemModules = [\n userModule,\n sessionModule,\n cronModule,\n migrationModule,\n rateLimitModule,\n systemModule,\n lockModule,\n filesModule,\n ];\n const combinedModules = [...systemModules, ...modules];\n\n markAppStarted();\n\n initSystemMethods(systemModules);\n initCustomMethods(modules);\n\n initRoles(roles, defaultRoles);\n\n const configSchema = getConfigSchema(combinedModules);\n setSchema(configSchema);\n const rawStores = getStores(combinedModules) as Store<ModelSchema, never>[];\n const channels = getChannels(combinedModules);\n\n defineCronJobs(combinedModules);\n\n // Apply user-provided auth rate limit overrides directly onto the user\n // module so its `rateLimits` array reflects the effective configuration\n // (the Modelence Cloud reads these from modules for display).\n userModule.rateLimits = buildAuthRateLimits(auth.rateLimits);\n\n const rateLimits = getRateLimits(combinedModules);\n initRateLimits(rateLimits);\n\n const { storesToInit, effectiveStores } = resolveStores(rawStores) as {\n storesToInit: Store<ModelSchema, never>[];\n effectiveStores: Store<ModelSchema, never>[];\n };\n\n if (hasRemoteBackend) {\n const { configs, environmentId, appAlias, environmentAlias, telemetry } =\n await connectCloudBackend({\n configSchema,\n cronJobsMetadata: getCronJobsMetadata(),\n stores: effectiveStores,\n roles,\n });\n loadRemoteConfigs(configs);\n setMetadata({ environmentId, appAlias, environmentAlias, telemetry });\n } else {\n loadConfigs(getLocalConfigs(configSchema));\n }\n\n // Default to Modelence's managed email provider when the app is connected\n // to Modelence Cloud and no provider was configured. This relays through\n // Studio's /api/email/send (which sends via SES on a Modelence-owned\n // domain). Locally — without a service endpoint — keep the historical\n // \"no provider configured\" behavior so misconfigurations still throw loudly.\n if (hasRemoteBackend && !email.provider) {\n setEmailConfig({ ...email, provider: managedEmailProvider });\n } else {\n setEmailConfig(email);\n }\n setAuthConfig(auth);\n setSecurityConfig(security);\n setWebsocketConfig({\n ...websocket,\n provider: websocket.provider || socketioServer,\n });\n\n const mongodbUri = getMongodbUri();\n if (mongodbUri) {\n await connect();\n const allStoresToInit = [...new Set([...storesToInit, ...effectiveStores])];\n initStores(allStoresToInit);\n await createIndexesAndMigrationsWithLock(effectiveStores, migrations);\n } else {\n startMigrations(migrations);\n }\n\n if (hasRemoteBackend) {\n await initMetrics();\n startConfigSync();\n }\n\n startCronJobs().catch(console.error);\n\n await startServer(server, { combinedModules, channels });\n}\n\nfunction initCustomMethods(modules: Module[]) {\n for (const module of modules) {\n for (const [key, handler] of Object.entries(module.queries)) {\n createQuery(`${module.name}.${key}`, handler);\n }\n for (const [key, handler] of Object.entries(module.mutations)) {\n createMutation(`${module.name}.${key}`, handler);\n }\n }\n}\n\nfunction initSystemMethods(modules: Module[]) {\n for (const module of modules) {\n for (const [key, handler] of Object.entries(module.queries)) {\n _createSystemQuery(`${module.name}.${key}`, handler);\n }\n for (const [key, handler] of Object.entries(module.mutations)) {\n _createSystemMutation(`${module.name}.${key}`, handler);\n }\n }\n}\n\nfunction getStores(modules: Module[]) {\n return modules.flatMap((module) => module.stores);\n}\n\nfunction getChannels(modules: Module[]) {\n return modules.flatMap((module) => module.channels);\n}\n\nfunction getRateLimits(modules: Module[]) {\n return modules.flatMap((module) => module.rateLimits);\n}\n\nfunction warnIndexCreationFailure(storeName: string, error: unknown) {\n console.warn(`Failed to create indexes for store '${storeName}'. Continuing startup.`, error);\n}\n\nconst MIGRATIONS_LOCK_RESOURCE = 'migrations';\n\nasync function createIndexesAndMigrationsWithLock(\n effectiveStores: Store<ModelSchema, never>[],\n migrations: MigrationScript[]\n) {\n const hasLock = await acquireLock(MIGRATIONS_LOCK_RESOURCE, {\n lockDuration: time.seconds(30),\n heartbeat: true,\n });\n if (!hasLock) {\n return;\n }\n\n let blockingStores: Store<ModelSchema, never>[];\n let backgroundStores: Store<ModelSchema, never>[];\n\n try {\n blockingStores = effectiveStores.filter((store) => store.getIndexCreationMode() === 'blocking');\n backgroundStores = effectiveStores.filter(\n (store) => store.getIndexCreationMode() === 'background'\n );\n\n for (const store of blockingStores) {\n await createStoreIndexes(store, 'full');\n }\n for (const store of backgroundStores) {\n await createStoreIndexes(store, 'drop-only');\n }\n } catch (error) {\n await releaseLock(MIGRATIONS_LOCK_RESOURCE);\n throw error;\n }\n\n const backgroundIndexCreationPromise = (async () => {\n for (const store of backgroundStores) {\n await createStoreIndexes(store, 'create-only');\n }\n })();\n const migrationPromise = runMigrations(migrations, { lockMode: 'skip' });\n\n void Promise.allSettled([backgroundIndexCreationPromise, migrationPromise])\n .then(([backgroundIndexesResult, migrationResult]) => {\n if (backgroundIndexesResult.status === 'rejected') {\n console.error('Error creating background indexes:', backgroundIndexesResult.reason);\n }\n\n if (migrationResult.status === 'rejected') {\n console.error('Error running migrations:', migrationResult.reason);\n }\n })\n .finally(async () => {\n await releaseLock(MIGRATIONS_LOCK_RESOURCE);\n });\n}\n\nasync function createStoreIndexes(\n store: Store<ModelSchema, never>,\n reconcileMode: IndexReconcileMode = 'full'\n) {\n const storeName = store.getName();\n\n try {\n await store.createIndexes(reconcileMode);\n } catch (error) {\n warnIndexCreationFailure(storeName, error);\n }\n}\n\nfunction getConfigSchema(modules: Module[]): ConfigSchema {\n const merged: ConfigSchema = {};\n\n for (const module of modules) {\n for (const [key, value] of Object.entries(module.configSchema)) {\n const absoluteKey = `${module.name}.${key}`;\n if (absoluteKey in merged) {\n throw new Error(`Duplicate config schema key: ${absoluteKey} (${module.name})`);\n }\n\n merged[absoluteKey] = value;\n }\n }\n\n return merged;\n}\n\nfunction defineCronJobs(modules: Module[]) {\n for (const module of modules) {\n for (const [cronAlias, cronJobParams] of Object.entries(module.cronJobs)) {\n defineCronJob(`${module.name}.${cronAlias}`, cronJobParams);\n }\n }\n}\n\nfunction initStores(stores: Store<ModelSchema, never>[]) {\n const client = getClient();\n if (!client) {\n throw new Error('Failed to initialize stores: MongoDB client not initialized');\n }\n\n for (const store of stores) {\n store.init(client);\n }\n}\n\nasync function trackAppStart() {\n const isTrackingEnabled = process.env.MODELENCE_TRACKING_ENABLED !== 'false';\n\n if (isTrackingEnabled) {\n const serviceEndpoint = process.env.MODELENCE_SERVICE_ENDPOINT ?? 'https://cloud.modelence.com';\n const environmentId = process.env.MODELENCE_ENVIRONMENT_ID;\n\n const appDetails = await getAppDetails();\n const modelencePackageJson = await import('../../package.json');\n\n await fetch(`${serviceEndpoint}/api/track/app-start`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n projectName: appDetails.name,\n version: modelencePackageJson.default.version,\n localHostname: os.hostname(),\n environmentId,\n }),\n });\n }\n}\n\nasync function getAppDetails() {\n try {\n const packageJsonPath = path.join(process.cwd(), 'package.json');\n const packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8');\n const packageJson = JSON.parse(packageJsonContent);\n\n return {\n name: packageJson.name || 'unknown',\n };\n } catch {\n return {\n name: 'unknown',\n };\n }\n}\n","import { ObjectId } from 'mongodb';\nimport { randomUUID } from 'crypto';\nimport {\n usersCollection,\n emailVerificationTokensCollection,\n resetPasswordTokensCollection,\n magicLinkTokensCollection,\n} from './db';\n\nexport async function clearTokens(userId: ObjectId) {\n await emailVerificationTokensCollection.deleteMany({\n userId,\n });\n\n await resetPasswordTokensCollection.deleteMany({\n userId,\n });\n\n // Magic link tokens and one-time codes are keyed by email, not userId, so they\n // must be revoked by the user's addresses. Without this, disabling a user leaves\n // any link issued before the disable able to authenticate once re-enabled, and\n // (with allowSignup) a deleted user's old link could create a fresh account for\n // the same email. Read the user before the caller wipes `emails`. Magic link\n // tokens are stored under the `validateEmail`-lowercased address while stored\n // addresses may keep original casing (e.g. OAuth accounts), so match lowercased.\n const userDoc = await usersCollection.findOne({ _id: userId });\n const userEmails = Array.from(\n new Set(\n (userDoc?.emails ?? [])\n .map((e) => e.address?.toLowerCase())\n .filter((address): address is string => Boolean(address))\n )\n );\n if (userEmails.length > 0) {\n await magicLinkTokensCollection.deleteMany({ email: { $in: userEmails } });\n }\n}\n\nexport async function disableUser(userId: ObjectId) {\n await clearTokens(userId);\n\n await usersCollection.updateOne(userId, {\n $set: {\n status: 'disabled',\n disabledAt: new Date(),\n },\n });\n}\n\nexport async function deleteUser(userId: ObjectId) {\n await clearTokens(userId);\n\n await usersCollection.updateOne(\n {\n _id: userId,\n },\n {\n $set: {\n handle: `deleted-${userId}-${randomUUID()}`,\n status: 'deleted',\n deletedAt: new Date(),\n authMethods: {},\n emails: [],\n },\n }\n );\n}\n","import { Session, User } from '@/auth/types';\nimport { getWebsocketConfig } from '@/app/websocketConfig';\nimport { logError } from '../telemetry';\n\ntype canAccessChannel = (props: {\n user: User | null;\n session: Session | null;\n roles: string[];\n}) => Promise<boolean>;\n\nexport class ServerChannel<T = unknown> {\n public readonly category: string;\n public readonly canAccessChannel: canAccessChannel | null;\n\n constructor(category: string, canAccessChannel?: canAccessChannel) {\n this.category = category;\n this.canAccessChannel = canAccessChannel || null;\n }\n\n broadcast(id: string, data: T) {\n const websocketProvider = getWebsocketConfig().provider;\n if (!websocketProvider) {\n logError('Websockets provider should be added to startApp', {});\n return;\n }\n\n websocketProvider.broadcast({\n category: this.category,\n id,\n data,\n });\n }\n}\n","import { getEmailConfig } from '@/app/emailConfig';\nimport { EmailPayload } from '../types';\n\nexport function sendEmail(payload: EmailPayload) {\n if (!getEmailConfig().provider) {\n throw new Error(\n 'Email provider is not configured, see https://docs.modelence.com/email for more details.'\n );\n }\n return getEmailConfig().provider?.sendEmail(payload);\n}\n","import { getAuthConfig } from '@/app/authConfig';\nimport { getConfig } from '@/config/server';\n\n/**\n * Schemes that must never be used as a redirect target, whatever the allowlist\n * says. These execute or read local content rather than navigating, so allowing\n * one would turn the OAuth callback into an XSS or file-disclosure vector.\n */\nconst FORBIDDEN_SCHEMES = new Set(['javascript:', 'data:', 'file:', 'vbscript:', 'blob:']);\n\nfunction parseRedirectUrl(url: string): URL | null {\n try {\n return new URL(url);\n } catch {\n return null;\n }\n}\n\n/**\n * Deep links the mobile OAuth flow may redirect back to, from both sources:\n * the `auth.mobile.redirectUrls` config value (Studio- and env-manageable) and\n * the typed `AuthConfig.mobile.redirectUrls` array.\n *\n * The config value is declared `type: 'text'`, so the Cloud dashboard renders a\n * textarea and one-URL-per-line is as natural to write as a comma-separated\n * list. Both separators are accepted: splitting on commas alone would read a\n * newline-delimited value as a single unparseable entry, silently emptying the\n * allowlist and rejecting every mobile sign-in.\n *\n * There is no default. An empty result disables mobile OAuth entirely rather\n * than falling back to something permissive.\n */\nexport function getAllowedMobileRedirectUrls(): string[] {\n const fromConfig = String(getConfig('_system.user.auth.mobile.redirectUrls') ?? '')\n .split(/[\\n,]/)\n .map((url) => url.trim())\n .filter(Boolean);\n\n const fromAuthConfig = (getAuthConfig().mobile?.redirectUrls ?? [])\n .map((url) => String(url).trim())\n .filter(Boolean);\n\n return [...new Set([...fromConfig, ...fromAuthConfig])];\n}\n\n/**\n * Whether `url` is an allowed deep-link target for the mobile OAuth flow.\n *\n * Matching compares parsed scheme, host (including port) and path — never raw\n * string prefixes, which would let `myapp://authorize.evil` pass against an\n * allowlisted `myapp://auth`. A candidate may add query parameters or a\n * fragment (the callback itself appends `?code=`), but may not change where it\n * points.\n *\n * Host and path are compared as separate fields, so the two ways of writing a\n * custom scheme are distinct targets rather than synonyms: `myapp://auth`\n * parses as host `auth` with an empty path, while `myapp:auth` parses as an\n * empty host with path `auth`. Nothing is normalized between the two forms —\n * write the allowlist entry exactly as the app registers its scheme, which for\n * React Native and Expo is the `myapp://auth` form used throughout the docs.\n */\nexport function isAllowedMobileRedirectUrl(url: string): boolean {\n if (!url || typeof url !== 'string') return false;\n\n const candidate = parseRedirectUrl(url);\n if (!candidate) return false;\n if (FORBIDDEN_SCHEMES.has(candidate.protocol.toLowerCase())) return false;\n if (hasUserinfo(candidate)) return false;\n\n return getAllowedMobileRedirectUrls().some((allowed) => {\n const entry = parseRedirectUrl(allowed);\n if (!entry) return false;\n if (FORBIDDEN_SCHEMES.has(entry.protocol.toLowerCase())) return false;\n if (hasUserinfo(entry)) return false;\n\n return (\n candidate.protocol.toLowerCase() === entry.protocol.toLowerCase() &&\n // `host` carries the port, so a differing port fails here too.\n candidate.host.toLowerCase() === entry.host.toLowerCase() &&\n normalizePath(candidate.pathname) === normalizePath(entry.pathname)\n );\n });\n}\n\n/**\n * Whether a URL carries a `user:password@` prefix.\n *\n * Userinfo is not part of the host comparison above — `myapp://evil@auth` and\n * `myapp://auth` share the host `auth` — but it survives into the redirect that\n * `buildMobileRedirect` emits. Rejecting it keeps the allowlisted target and\n * the emitted URL identical.\n */\nfunction hasUserinfo(url: URL): boolean {\n return url.username !== '' || url.password !== '';\n}\n\nfunction normalizePath(pathname: string): string {\n const trimmed = pathname.replace(/\\/+$/, '');\n return trimmed === '' ? '/' : trimmed;\n}\n\n/**\n * Query parameters this module owns on a deep link.\n *\n * The allowlist matches on scheme, host and path only, so a configured target\n * is free to carry query parameters of its own — including, accidentally or\n * deliberately, one of these. Any that the caller does not set are removed, so\n * the app reads the outcome of *this* flow rather than a value baked into the\n * allowlisted URL. Without that, `redirectUri=myapp://auth?error=nope` would\n * make a successful sign-in look failed.\n */\nconst OUTCOME_PARAMS = ['code', 'error', 'errorCode', 'linked'] as const;\n\n/**\n * Appends query parameters to an already-allowlisted deep link.\n *\n * Callers must have validated `redirectUri` with {@link isAllowedMobileRedirectUrl}\n * first; this only builds the final URL.\n */\nexport function buildMobileRedirect(redirectUri: string, params: Record<string, string>): string {\n const url = new URL(redirectUri);\n\n for (const key of OUTCOME_PARAMS) {\n url.searchParams.delete(key);\n }\n\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, value);\n }\n return url.toString();\n}\n","/**\n * Query params a failed web OAuth flow reports through. Stripped from the\n * configured target before the fresh values are set, so a stale\n * `?error=` left in `oauthErrorRedirectUrl` can never shadow the real one.\n * Mirrors the mobile deep-link contract (see `mobileRedirect.ts`).\n */\nconst ERROR_PARAMS = ['error', 'errorCode'] as const;\n\nexport type OAuthErrorRedirectParams = {\n /** Human-readable message for display. Not a stable contract. */\n error: string;\n /** Stable identifier the app can branch on. */\n errorCode: string;\n};\n\n/**\n * Builds the absolute URL a failed web OAuth flow redirects to.\n *\n * `target` is `authConfig.oauthErrorRedirectUrl`: a server-side, trusted value,\n * so it is not allowlisted the way a mobile `redirectUri` is. A path such as\n * `/login` is resolved against `siteUrl` (`_system.site.url`), the same base\n * the email landing routes use; an absolute URL is taken as is. Existing\n * unrelated query params and any hash fragment are preserved.\n */\nexport function buildOAuthErrorRedirect(\n siteUrl: string,\n target: string,\n params: OAuthErrorRedirectParams\n): string {\n const url = new URL(target, siteUrl);\n\n for (const key of ERROR_PARAMS) {\n url.searchParams.delete(key);\n }\n\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, value);\n }\n\n return url.toString();\n}\n","import { randomBytes } from 'crypto';\nimport { type Request, type Response } from 'express';\nimport { MongoServerError, ObjectId } from 'mongodb';\nimport { usersCollection } from '@/auth/db';\nimport {\n createSession,\n setAuthTokenCookie,\n consumeLinkNonce,\n issueOAuthExchangeCode,\n} from '@/auth/session';\nimport { getAuthConfig } from '@/app/authConfig';\nimport { getCallContext } from '@/app/server';\nimport { getConfig } from '@/config/server';\nimport { getLocalSiteUrl } from '@/config/local';\nimport { time } from '@/time';\nimport { resolveUniqueHandle } from '../utils';\nimport { User, Session, UserEmail, OAuthProvider } from '@/auth/types';\nimport { ConnectionInfo } from '@/methods/types';\nimport {\n isAllowedMobileRedirectUrl,\n buildMobileRedirect,\n getAllowedMobileRedirectUrls,\n} from './mobileRedirect';\nimport { buildOAuthErrorRedirect } from './oauthErrorRedirect';\n\n/** Which kind of client started an OAuth flow. */\nexport type OAuthPlatform = 'web' | 'mobile';\n\n/**\n * How a completed OAuth flow is handed back to the client.\n *\n * Web sets the session cookie and redirects to the site root, as it always has.\n * Mobile has no shared cookie jar, so it redirects to the app's deep link with a\n * single-use exchange code the app redeems for a session (see\n * `issueOAuthExchangeCode`).\n */\nexport type OAuthOutcome =\n | { platform: 'web' }\n | {\n platform: 'mobile';\n redirectUri: string;\n /**\n * PKCE-style challenge from the device that started the flow.\n *\n * Present for a sign-in, where it binds the exchange code to that device.\n * Absent for linking, which redirects with `linked=<provider>` and mints\n * no credential — so there is nothing to bind. `authenticateUser` is the\n * only caller that mints a code, and it requires\n * {@link BoundMobileOutcome}, so an unbound value cannot reach it.\n */\n codeChallenge?: string;\n };\n\n/**\n * A mobile outcome that is allowed to mint an exchange code.\n *\n * Narrower than {@link OAuthOutcome}: `codeChallenge` is required, so the\n * compiler — not a runtime check — rejects any attempt to mint a code for a\n * flow that carries no device binding.\n */\nexport type BoundMobileOutcome = {\n platform: 'mobile';\n redirectUri: string;\n codeChallenge: string;\n};\n\n/**\n * Where an *error* should be delivered.\n *\n * A failure carries no credential, so it needs a destination but no binding.\n *\n * `api` is for failures raised at the initiation endpoint, which a client calls\n * directly and can read a JSON body from. `web` is for the provider callback,\n * where the browser has landed on a URL outside any client bundle and a JSON\n * body would strand it.\n */\nexport type OAuthErrorTarget =\n | { platform: 'web' }\n | { platform: 'api' }\n | { platform: 'mobile'; redirectUri: string };\n\n/**\n * Resolves the outcome for a validated OAuth state.\n *\n * A mobile state stays mobile whether or not it carries a challenge: dropping to\n * web here would strand a native client in the device browser at `/` instead of\n * deep-linking back. Whether a code may be minted is decided at the point of\n * minting, by {@link isBoundMobileOutcome}.\n */\nexport function toOAuthOutcome(\n state: Pick<OAuthStateResult, 'platform' | 'redirectUri' | 'codeChallenge'>\n): OAuthOutcome {\n if (state.platform === 'mobile' && state.redirectUri) {\n return {\n platform: 'mobile',\n redirectUri: state.redirectUri,\n ...(state.codeChallenge ? { codeChallenge: state.codeChallenge } : {}),\n };\n }\n return { platform: 'web' };\n}\n\n/** Whether a mobile outcome carries the binding required to mint a code. */\nexport function isBoundMobileOutcome(outcome: OAuthOutcome): outcome is BoundMobileOutcome {\n return outcome.platform === 'mobile' && typeof outcome.codeChallenge === 'string';\n}\n\n/**\n * Whether the callback should fire the *login* hooks itself.\n *\n * On mobile it must not: this runs in the device browser against a throwaway\n * guest session, and the sign-in is only real once the app redeems the code.\n * `handleLoginWithOAuth` fires them there with the user's real session. Firing\n * in both places delivered every login hook twice, the first time with the\n * wrong session.\n *\n * Signup hooks are deliberately not gated: account creation genuinely happens\n * here, fires once, and redemption cannot tell a new account from a returning\n * one.\n */\nfunction shouldFireLoginHooks(outcome: OAuthOutcome): boolean {\n return outcome.platform !== 'mobile';\n}\n\nexport interface OAuthUserData {\n id: string;\n email: string;\n emailVerified: boolean;\n providerName: OAuthProvider;\n firstName?: string;\n lastName?: string;\n avatarUrl?: string;\n}\n/** Consumes a single-use link nonce; returns the bound userId string or null. */\nexport async function resolveUserIdFromLinkNonce(\n nonce: string | undefined\n): Promise<string | null> {\n if (!nonce || typeof nonce !== 'string') return null;\n return consumeLinkNonce(nonce);\n}\n\n/**\n * Machine-readable failure reasons handed to a native app alongside the\n * human-readable message, so the app can branch on the outcome without parsing\n * English prose.\n */\nexport type OAuthErrorCode =\n | 'missing_code'\n | 'invalid_state'\n | 'invalid_redirect'\n | 'invalid_link_nonce'\n | 'account_inactive'\n | 'email_exists'\n | 'email_required'\n | 'link_failed'\n | 'not_signed_in'\n | 'oauth_failed';\n\n/*\n * Sends OAuth error response.\n * If `oauthErrorRedirectUrl` is configured, redirects there with the failure\n * in the query string. Otherwise, if `errorComponent` is configured, renders\n * HTML. Otherwise falls back to JSON.\n *\n * On a mobile flow, an HTML or JSON body would strand the user in the device\n * browser with no route back into the app, so the error is delivered as a\n * redirect to the app's deep link instead. Errors raised before the state is\n * decoded have no known platform and keep the default behaviour.\n *\n * `oauthErrorRedirectUrl` covers the `web` platform only: it exists for the\n * provider callback, where the browser has landed outside any client bundle.\n * An initiation-time failure (`api`) is answered by a client that called the\n * endpoint directly and can read the JSON body, so it is never redirected.\n */\nexport function sendOAuthError(\n res: Response,\n statusCode: number,\n errorMessage: string,\n // Only a destination is needed to report a failure; an OAuthOutcome is\n // accepted too, since it is a strict subtype.\n outcome: OAuthErrorTarget = { platform: 'web' },\n errorCode: OAuthErrorCode = 'oauth_failed'\n) {\n if (outcome.platform === 'mobile') {\n // The human-readable message is for display; `errorCode` is what an app\n // should branch on, since the message text is not a stable contract.\n res.set('Referrer-Policy', 'no-referrer');\n return res.redirect(\n buildMobileRedirect(outcome.redirectUri, { error: errorMessage, errorCode })\n );\n }\n\n const authConfig = getAuthConfig();\n\n if (authConfig.oauthErrorRedirectUrl && outcome.platform === 'web') {\n // `_system.site.url` is set by the time a *callback* runs, since\n // `getRedirectUri` needs it to start the flow at all. It can still be\n // missing in local dev, so fall back to the same value that seeds it there.\n const siteUrl = (getConfig('_system.site.url') as string) || getLocalSiteUrl();\n let redirectUrl: string | null = null;\n try {\n redirectUrl = buildOAuthErrorRedirect(siteUrl, authConfig.oauthErrorRedirectUrl, {\n error: errorMessage,\n errorCode,\n });\n } catch (err) {\n // A relative `oauthErrorRedirectUrl` against an unusable base throws here.\n // Report the original failure below rather than turning it into a 500.\n console.error(\n `[modelence] Could not build the OAuth error redirect from ` +\n `${JSON.stringify(authConfig.oauthErrorRedirectUrl)} and site URL ` +\n `${JSON.stringify(siteUrl)}. Falling back to the default error response.`,\n err\n );\n }\n\n if (redirectUrl) {\n // Same shape as the mobile deep link, so a client can handle both alike.\n // The message is not a credential, but it is user-facing text about a\n // failed sign-in; keep it out of Referer like the mobile branch does.\n res.set('Referrer-Policy', 'no-referrer');\n return res.redirect(redirectUrl);\n }\n }\n\n const response = res.status(statusCode);\n if (authConfig.errorComponent) {\n try {\n const html = authConfig.errorComponent({ error: errorMessage, statusCode });\n if (html) return response.send(html);\n } catch (err) {\n console.error('Unhandled error in authConfig.errorComponent:', err);\n }\n }\n\n return response.json({ error: errorMessage });\n}\n\nexport async function authenticateUser(\n res: Response,\n userId: ObjectId,\n provider: OAuthProvider,\n outcome: OAuthOutcome = { platform: 'web' }\n) {\n if (outcome.platform === 'mobile') {\n // Fail closed rather than falling through to a cookie session: a mobile\n // sign-in always carries a challenge (initiation rejects it otherwise), so\n // reaching here without one means the state was tampered with or truncated.\n // Silently issuing a browser session instead would strand the app at `/`.\n if (!isBoundMobileOutcome(outcome)) {\n sendOAuthError(\n res,\n 400,\n 'This sign-in could not be completed. Please try signing in again.',\n outcome,\n 'invalid_state'\n );\n return;\n }\n\n // Deliberately no session and no cookie: the deep link is the weakest hop (a\n // custom scheme can be claimed by any installed app), so it carries only a\n // single-use code. The session is minted at redemption over TLS, so an\n // intercepted-but-unredeemed code never corresponds to a live session.\n const code = await issueOAuthExchangeCode(userId.toString(), provider, outcome.codeChallenge);\n\n // The deep link carries a credential; keep it out of any Referer header.\n res.set('Referrer-Policy', 'no-referrer');\n res.redirect(buildMobileRedirect(outcome.redirectUri, { code }));\n return;\n }\n\n const { authToken } = await createSession(userId);\n\n setAuthTokenCookie(res, authToken);\n res.redirect('/');\n}\n\nasync function handleExistingProviderLogin(\n res: Response,\n userData: OAuthUserData,\n existingUser: User,\n session: Session | null,\n connectionInfo: ConnectionInfo,\n outcome: OAuthOutcome\n) {\n const authConfig = getAuthConfig();\n\n try {\n if (existingUser.status === 'disabled' || existingUser.status === 'deleted') {\n sendOAuthError(res, 400, 'User account is not active.', outcome);\n return;\n }\n\n //Add User FirstName,LastName, AvatarURL if not exists\n const update: Partial<Pick<OAuthUserData, 'firstName' | 'lastName' | 'avatarUrl'>> = {};\n\n if (existingUser.firstName === undefined && userData.firstName) {\n update.firstName = userData.firstName;\n }\n if (existingUser.lastName === undefined && userData.lastName) {\n update.lastName = userData.lastName;\n }\n if (existingUser.avatarUrl === undefined && userData.avatarUrl) {\n update.avatarUrl = userData.avatarUrl;\n }\n\n let user = existingUser;\n\n if (Object.keys(update).length > 0) {\n await usersCollection.updateOne({ _id: existingUser._id }, { $set: update });\n user = { ...existingUser, ...update } as typeof existingUser;\n }\n\n await authenticateUser(res, existingUser._id, userData.providerName, outcome);\n\n if (shouldFireLoginHooks(outcome)) {\n authConfig.onAfterLogin?.({\n provider: userData.providerName,\n user,\n session,\n connectionInfo,\n });\n authConfig.login?.onSuccess?.(user);\n }\n } catch (error) {\n if (error instanceof Error) {\n authConfig.login?.onError?.(error);\n\n authConfig.onLoginError?.({\n provider: userData.providerName,\n error,\n session,\n connectionInfo,\n });\n }\n throw error;\n }\n}\n\nasync function handleExistingEmailLogin(\n res: Response,\n userData: OAuthUserData,\n existingUserByEmail: User,\n session: Session | null,\n connectionInfo: ConnectionInfo,\n outcome: OAuthOutcome\n) {\n const authConfig = getAuthConfig();\n const linkingMode = authConfig.oauthAccountLinking ?? 'manual';\n\n if (linkingMode === 'auto' && userData.emailVerified) {\n if (existingUserByEmail.status === 'disabled' || existingUserByEmail.status === 'deleted') {\n sendOAuthError(res, 400, 'User account is not active.', outcome);\n return;\n }\n\n const matchedEmail = existingUserByEmail.emails?.find(\n (emailDoc: UserEmail) => emailDoc.address.toLowerCase() === userData.email.toLowerCase()\n );\n\n // Prevent pre-registration takeover by requiring local ownership verification too.\n if (!matchedEmail?.verified) {\n sendOAuthError(\n res,\n 400,\n 'User with this email already exists. Please log in instead.',\n outcome\n );\n return;\n }\n\n try {\n // Build profile fields to backfill from provider data if missing\n const profileUpdate: Partial<Pick<OAuthUserData, 'firstName' | 'lastName' | 'avatarUrl'>> = {\n ...(existingUserByEmail.firstName === undefined &&\n userData.firstName && { firstName: userData.firstName }),\n ...(existingUserByEmail.lastName === undefined &&\n userData.lastName && { lastName: userData.lastName }),\n ...(existingUserByEmail.avatarUrl === undefined &&\n userData.avatarUrl && { avatarUrl: userData.avatarUrl }),\n };\n\n // Single atomic update — link provider + backfill profile in one round trip\n const updateResult = await usersCollection.updateOne(\n {\n _id: existingUserByEmail._id,\n status: { $nin: ['deleted', 'disabled'] },\n $or: [\n { [`authMethods.${userData.providerName}.id`]: { $exists: false } },\n { [`authMethods.${userData.providerName}.id`]: userData.id },\n ],\n },\n {\n $set: {\n [`authMethods.${userData.providerName}.id`]: userData.id,\n ...profileUpdate,\n },\n }\n );\n\n const autoLinkSuccessful = updateResult.matchedCount > 0;\n\n if (!autoLinkSuccessful) {\n // User was deleted/disabled between findOne and updateOne, or linked to a *different* ID\n sendOAuthError(\n res,\n 400,\n 'User with this email already exists. Please log in instead.',\n outcome\n );\n return;\n }\n\n await authenticateUser(res, existingUserByEmail._id, userData.providerName, outcome);\n\n // Construct updated user in-memory to provide fresh data to callbacks\n const updatedUser: User = {\n ...existingUserByEmail,\n ...profileUpdate,\n authMethods: {\n ...existingUserByEmail.authMethods,\n [userData.providerName]: {\n id: userData.id,\n },\n },\n };\n\n if (shouldFireLoginHooks(outcome)) {\n authConfig.onAfterLogin?.({\n provider: userData.providerName,\n user: updatedUser,\n session,\n connectionInfo,\n });\n authConfig.login?.onSuccess?.(updatedUser);\n }\n\n return;\n } catch (error) {\n if (error instanceof Error) {\n authConfig.login?.onError?.(error);\n\n authConfig.onLoginError?.({\n provider: userData.providerName,\n error,\n session,\n connectionInfo,\n });\n }\n throw error;\n }\n }\n\n // Manual mode (default) or unverified email — reject\n sendOAuthError(res, 400, 'User with this email already exists. Please log in instead.', outcome);\n return;\n}\n\nasync function handleNewUserSignup(\n res: Response,\n userData: OAuthUserData,\n session: Session | null,\n connectionInfo: ConnectionInfo,\n outcome: OAuthOutcome\n) {\n const authConfig = getAuthConfig();\n\n try {\n let handle: string;\n\n if (authConfig.generateHandle) {\n const generated = await authConfig.generateHandle!({\n email: userData.email,\n firstName: userData.firstName,\n lastName: userData.lastName,\n });\n //Don't throw error if handle is already taken, instead add a suffix '_2', '_3', etc. to the handle\n handle = await resolveUniqueHandle(generated, userData.email, {\n throwOnConflict: false,\n });\n } else {\n handle = await resolveUniqueHandle(undefined, userData.email);\n }\n\n const userDoc = {\n handle: handle,\n status: 'active' as const,\n emails: [\n {\n address: userData.email,\n verified: userData.emailVerified,\n },\n ],\n createdAt: new Date(),\n authMethods: {\n [userData.providerName]: {\n id: userData.id,\n },\n },\n ...(userData.firstName !== undefined && { firstName: userData.firstName }),\n ...(userData.lastName !== undefined && { lastName: userData.lastName }),\n ...(userData.avatarUrl !== undefined && { avatarUrl: userData.avatarUrl }),\n };\n\n const newUser = await usersCollection.insertOne(userDoc);\n\n await authenticateUser(res, newUser.insertedId, userData.providerName, outcome);\n\n const userDocument = await usersCollection.findOne(\n { _id: newUser.insertedId },\n { readPreference: 'primary' }\n );\n\n if (userDocument) {\n authConfig.onAfterSignup?.({\n provider: userData.providerName,\n user: userDocument,\n session,\n connectionInfo,\n });\n\n authConfig.signup?.onSuccess?.(userDocument);\n }\n } catch (error) {\n if (error instanceof Error) {\n authConfig.onSignupError?.({\n provider: userData.providerName,\n error,\n session,\n connectionInfo,\n });\n\n authConfig.signup?.onError?.(error);\n }\n throw error;\n }\n}\n\nexport function getRedirectUri(provider: string): string {\n return `${getConfig('_system.site.url')}/api/_internal/auth/${provider}/callback`;\n}\n\nexport async function handleOAuthUserAuthentication(\n req: Request,\n res: Response,\n userData: OAuthUserData,\n outcome: OAuthOutcome = { platform: 'web' }\n): Promise<void> {\n // 1. Try to fetch existing user by OAuth ID\n const existingUser = await usersCollection.findOne({\n [`authMethods.${userData.providerName}.id`]: userData.id,\n });\n\n const { session, connectionInfo } = await getCallContext(req, res);\n\n if (existingUser) {\n return handleExistingProviderLogin(\n res,\n userData,\n existingUser,\n session,\n connectionInfo,\n outcome\n );\n }\n\n // 2. Validate Email is provided by Provider\n if (!userData.email) {\n sendOAuthError(\n res,\n 400,\n `Email address is required for ${userData.providerName} authentication.`,\n outcome\n );\n return;\n }\n\n // 3. Try to fetch existing user by Email\n let existingUserByEmail;\n\n try {\n existingUserByEmail = await usersCollection.findOne(\n { 'emails.address': userData.email, status: { $ne: 'deleted' } },\n { collation: { locale: 'en', strength: 2 } }\n );\n } catch (error) {\n if (error instanceof Error) {\n const authConfig = getAuthConfig();\n authConfig.onSignupError?.({\n provider: userData.providerName,\n error,\n session,\n connectionInfo,\n });\n\n authConfig.signup?.onError?.(error);\n }\n throw error;\n }\n\n //User Already existed via email verification but now trying to login via OAuth Providers from the same email\n if (existingUserByEmail) {\n return handleExistingEmailLogin(\n res,\n userData,\n existingUserByEmail,\n session,\n connectionInfo,\n outcome\n );\n }\n\n //New User\n return handleNewUserSignup(res, userData, session, connectionInfo, outcome);\n}\n\nexport function clearOAuthLinkCookie(res: Response) {\n // Important: must clear the httpOnly cookie used during OAuth linking\n res.cookie('oauthLinkToken', '', {\n httpOnly: true,\n maxAge: 0,\n path: '/api/_internal/auth/',\n sameSite: 'lax',\n secure: process.env.NODE_ENV === 'production',\n });\n}\n\nfunction safelyCallHook(hook?: () => void) {\n if (!hook) return;\n\n try {\n hook();\n } catch (err) {\n console.error('Error executing OAuth hook:', err);\n }\n}\n\nexport interface OAuthStateResult {\n mode: string;\n /** Set only in the React Native linking flow. */\n linkedUserId?: string;\n /** Which client started the flow; decides how the callback hands back the result. */\n platform: OAuthPlatform;\n /** Deep link to return to. Set only when `platform` is 'mobile'. */\n redirectUri?: string;\n /** Device binding for the exchange code. Set only when `platform` is 'mobile'. */\n codeChallenge?: string;\n}\n\n/**\n * Builds the value of the per-provider OAuth state cookie.\n *\n * Fields are colon-separated for backwards compatibility with in-flight cookies\n * written before mobile support (`state:mode` and `state:mode:userId` both still\n * parse). `redirectUri` contains colons and slashes of its own, so it is\n * base64url-encoded rather than embedded raw — otherwise a deep link would split\n * into several bogus fields.\n */\nexport function encodeOAuthState(params: {\n state: string;\n mode: string;\n linkedUserId?: string | null;\n platform?: OAuthPlatform;\n redirectUri?: string | null;\n codeChallenge?: string | null;\n}): string {\n const { state, mode, linkedUserId, platform = 'web', redirectUri, codeChallenge } = params;\n const fields = [state, mode, linkedUserId ?? ''];\n\n // Only extend the cookie when there is something to say — a web flow keeps\n // producing exactly the value it produced before mobile existed.\n if (platform !== 'web' || redirectUri) {\n fields.push(platform);\n fields.push(redirectUri ? Buffer.from(redirectUri, 'utf8').toString('base64url') : '');\n fields.push(codeChallenge ?? '');\n }\n\n return fields.join(':');\n}\n\nfunction decodeOAuthState(storedState: string): {\n stateValue: string;\n mode: string;\n linkedUserId?: string;\n platform: OAuthPlatform;\n redirectUri?: string;\n codeChallenge?: string;\n} {\n const [stateValue, mode, linkedUserId, platform, encodedRedirectUri, codeChallenge] = (\n storedState || ''\n ).split(':');\n\n // Never throws: invalid base64url decodes to garbage rather than raising, and\n // garbage is rejected by the allowlist check in the caller.\n const redirectUri = encodedRedirectUri\n ? Buffer.from(encodedRedirectUri, 'base64url').toString('utf8') || undefined\n : undefined;\n\n return {\n stateValue,\n mode: mode || 'login',\n ...(linkedUserId ? { linkedUserId } : {}),\n platform: platform === 'mobile' ? 'mobile' : 'web',\n ...(redirectUri ? { redirectUri } : {}),\n ...(codeChallenge ? { codeChallenge } : {}),\n };\n}\n\nexport function validateOAuthStateAndGetMode(\n req: Request,\n res: Response,\n stateCookieName: string\n): OAuthStateResult | null {\n const state = req.query.state as string;\n const storedState = req.cookies[stateCookieName];\n\n const decoded = decodeOAuthState(storedState || '');\n\n // Re-check against the current allowlist: it may have changed while the user\n // was on the consent screen, and a redirect target is worth validating on both\n // sides of a round trip.\n const mobileOutcome = resolveDecodedMobileOutcome(decoded);\n\n if (!state || !storedState || state !== decoded.stateValue) {\n // Deep-link the failure back when the decoded target survives re-validation,\n // rather than stranding the user on JSON. Trusted because the allowlist just\n // approved it, not because the cookie said so.\n //\n // The message says \"sign in again\" rather than naming CSRF: the overwhelming\n // majority of the users who see it are victims of an expired cookie, a stale\n // bookmarked callback, or a back button — not of an attack. Someone actually\n // forging a state learns nothing from being told so, while everyone else is\n // handed an accusation instead of the one action that fixes it. `invalid_state`\n // still identifies the case precisely for anything branching on it.\n sendOAuthError(\n res,\n 400,\n 'Your sign-in session has expired. Please sign in again.',\n mobileOutcome ?? { platform: 'web' },\n 'invalid_state'\n );\n return null;\n }\n\n res.clearCookie(stateCookieName);\n\n if (decoded.platform === 'mobile' && !mobileOutcome) {\n sendOAuthError(\n res,\n 400,\n 'Invalid OAuth redirect target.',\n { platform: 'web' },\n 'invalid_redirect'\n );\n return null;\n }\n\n return {\n mode: decoded.mode,\n ...(decoded.linkedUserId ? { linkedUserId: decoded.linkedUserId } : {}),\n platform: decoded.platform,\n ...(decoded.redirectUri ? { redirectUri: decoded.redirectUri } : {}),\n ...(decoded.codeChallenge ? { codeChallenge: decoded.codeChallenge } : {}),\n };\n}\n\n/**\n * Mobile outcome for an already-decoded state cookie, or null when the flow is\n * not mobile or its target no longer passes the allowlist.\n *\n * Used to answer \"can this failure be deep-linked back into the app?\" on error\n * paths that run before — or instead of — a successful state validation.\n */\nfunction resolveDecodedMobileOutcome(decoded: {\n platform: OAuthPlatform;\n redirectUri?: string;\n codeChallenge?: string;\n}): OAuthErrorTarget | null {\n if (decoded.platform !== 'mobile') return null;\n if (!decoded.redirectUri || !isAllowedMobileRedirectUrl(decoded.redirectUri)) return null;\n\n return { platform: 'mobile', redirectUri: decoded.redirectUri };\n}\n\n/**\n * Mobile outcome recovered from the raw state cookie, for failures that happen\n * before the state is validated at all — a missing `code`, or a consent screen\n * the user declined. The provider sends those back to the same callback with no\n * usable query state, but the cookie still says where the app lives.\n */\nexport function resolveMobileOutcomeFromCookie(\n req: Request,\n stateCookieName: string\n): OAuthErrorTarget | null {\n return resolveDecodedMobileOutcome(decodeOAuthState(req.cookies?.[stateCookieName] || ''));\n}\n\n/**\n * Explains why a redirect URI was rejected, in terms of what to change.\n *\n * Every scheme is a legitimate target — Expo Web serves the app over https,\n * Expo Go over exp:// — so the scheme only selects which hint to add.\n *\n * Classifies rather than echoes: the URI is caller-controlled and this message\n * reaches `authConfig.errorComponent`, which renders unescaped HTML.\n */\nfunction describeRejectedRedirectUri(redirectUri: string): string {\n const configured = getAllowedMobileRedirectUrls();\n\n if (configured.length === 0) {\n return (\n 'Mobile sign-in is not configured: auth.mobile.redirectUrls is empty. ' +\n \"Add your app's deep link (e.g. 'myapp://auth') to enable it.\"\n );\n }\n\n let protocol: string;\n try {\n protocol = new URL(redirectUri).protocol.toLowerCase();\n } catch {\n return (\n \"This redirectUri is not a valid URL. Pass your app's deep link, \" +\n \"e.g. 'myapp://auth', and list it in auth.mobile.redirectUrls.\"\n );\n }\n\n // http(s) and exp: are legitimate — Expo Web and Expo Go serve the app over\n // them — so the advice matches any other scheme, plus a note on why the value\n // differs between build targets.\n if (protocol === 'http:' || protocol === 'https:' || protocol === 'exp:') {\n return (\n 'This redirectUri is not in auth.mobile.redirectUrls. Add it verbatim. ' +\n 'Note that Linking.createURL returns a different value per build target — ' +\n \"your app's scheme in a native build, an https URL on Expo Web, an exp:// URL \" +\n 'in Expo Go — so each one you test needs its own entry.'\n );\n }\n\n return (\n 'This redirectUri is not in auth.mobile.redirectUrls. Add it verbatim — ' +\n 'entries match on scheme, host and path, so a differing path or port is a different target.'\n );\n}\n\n/**\n * Reads and validates the mobile handoff parameters from an OAuth initiation\n * request. Returns null when the request is not a mobile flow.\n *\n * Validation happens here, before the redirect to the provider, so a\n * misconfigured or malicious target fails immediately instead of after the user\n * has already granted consent.\n */\nexport function resolveMobileRedirectRequest(\n req: Request,\n res: Response,\n mode: string\n): { ok: true; redirectUri: string | null; codeChallenge: string | null } | { ok: false } {\n if (req.query.platform !== 'mobile') {\n return { ok: true, redirectUri: null, codeChallenge: null };\n }\n\n const redirectUri = typeof req.query.redirectUri === 'string' ? req.query.redirectUri : '';\n\n if (!redirectUri) {\n sendOAuthError(res, 400, 'A redirectUri is required for mobile authentication.', {\n platform: 'api',\n });\n return { ok: false };\n }\n\n if (!isAllowedMobileRedirectUrl(redirectUri)) {\n // The full value goes to the log, not the response: `errorMessage` reaches\n // `authConfig.errorComponent`, whose HTML is sent unescaped, so echoing\n // caller-controlled text back would hand app authors an XSS foot-gun.\n console.error(\n `[modelence] Rejected mobile OAuth redirectUri: ${JSON.stringify(redirectUri)}. ` +\n `Allowed: ${JSON.stringify(getAllowedMobileRedirectUrls())}`\n );\n\n sendOAuthError(\n res,\n 400,\n describeRejectedRedirectUri(redirectUri),\n { platform: 'api' },\n 'invalid_redirect'\n );\n return { ok: false };\n }\n\n const rawChallenge = typeof req.query.codeChallenge === 'string' ? req.query.codeChallenge : '';\n const hasValidChallenge = /^[A-Za-z0-9._~-]{16,256}$/.test(rawChallenge);\n\n // Only a login mints an exchange code, so only a login needs the binding. An\n // omitted challenge there would produce an unbound code, redeemable by anyone\n // — including a victim mid-flow whose verifier the server would then ignore.\n // Linking redirects with `linked=<provider>` and no credential, so there is\n // nothing to bind. The charset is constrained because a colon would corrupt\n // the delimiter-separated state cookie.\n if (mode !== 'link' && !hasValidChallenge) {\n sendOAuthError(\n res,\n 400,\n 'This sign-in request is missing a valid codeChallenge. Update the Modelence ' +\n 'client package — signInWithOAuth generates it automatically.',\n { platform: 'api' }\n );\n return { ok: false };\n }\n\n return { ok: true, redirectUri, codeChallenge: hasValidChallenge ? rawChallenge : null };\n}\n\n/**\n * Everything an OAuth initiation route needs to do between \"auth is configured\"\n * and \"redirect the user to the provider\": validate the mobile handoff, resolve\n * a link nonce, and build the state cookie value.\n *\n * Shared so a newly added provider cannot quietly ship a mobile flow that skips\n * redirect validation or device binding. Returns null when a response has\n * already been sent.\n */\nexport async function prepareOAuthInitiation(\n req: Request,\n res: Response,\n stateCookieName: string\n): Promise<{ state: string; mode: string } | null> {\n const state = randomBytes(32).toString('hex');\n const mode = req.query.mode === 'link' ? 'link' : 'login';\n\n // Validate the mobile deep link before leaving the app, so a bad target\n // fails here rather than after the user has granted consent.\n const mobileRequest = resolveMobileRedirectRequest(req, res, mode);\n if (!mobileRequest.ok) return null;\n const { redirectUri: mobileRedirectUri, codeChallenge } = mobileRequest;\n\n // React Native: consume single-use nonce and embed resolved userId in state cookie.\n let linkedUserId: string | null = null;\n if (mode === 'link' && req.query.linkNonce) {\n linkedUserId = await resolveUserIdFromLinkNonce(req.query.linkNonce as string);\n if (!linkedUserId) {\n // The redirect target was validated just above, so an expired nonce can\n // be reported into the app rather than as JSON in the device browser.\n sendOAuthError(\n res,\n 401,\n 'Invalid or expired link nonce for OAuth linking.',\n mobileRedirectUri\n ? { platform: 'mobile', redirectUri: mobileRedirectUri }\n : // Also an initiation-time failure: the caller gets JSON, not a redirect.\n { platform: 'api' },\n 'invalid_link_nonce'\n );\n return null;\n }\n }\n\n const stateValue = encodeOAuthState({\n state,\n mode,\n linkedUserId,\n platform: mobileRedirectUri ? 'mobile' : 'web',\n redirectUri: mobileRedirectUri,\n codeChallenge,\n });\n\n res.cookie(stateCookieName, stateValue, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: time.minutes(10),\n });\n\n return { state, mode };\n}\n\nexport async function handleOAuthProviderLink(\n req: Request,\n res: Response,\n userData: OAuthUserData,\n linkedUserId?: string,\n outcome: OAuthOutcome = { platform: 'web' }\n): Promise<void> {\n const authConfig = getAuthConfig();\n const { session, connectionInfo } = await getCallContext(req, res);\n\n // React Native: linkedUserId (from state cookie) takes precedence over the\n // browser session, which may belong to a different user.\n let resolvedUserId: ObjectId | null = null;\n if (linkedUserId) {\n if (!ObjectId.isValid(linkedUserId)) {\n clearOAuthLinkCookie(res);\n sendOAuthError(res, 400, 'Invalid OAuth linking state.', outcome);\n return;\n }\n resolvedUserId = new ObjectId(linkedUserId);\n } else {\n resolvedUserId = session?.userId ?? null;\n }\n\n if (!resolvedUserId) {\n clearOAuthLinkCookie(res);\n sendOAuthError(res, 401, 'You must be signed in to link a provider.', outcome);\n return;\n }\n\n const userId = resolvedUserId;\n\n try {\n // Atomically attach the provider to the current user while preventing\n // overwriting an existing provider ID on the same user.\n // A unique index on the provider ID ensures it cannot be linked to another user.\n const providerField = `authMethods.${userData.providerName}.id`;\n\n const updateResult = await usersCollection.updateOne(\n {\n _id: userId,\n status: { $nin: ['deleted', 'disabled'] },\n $or: [{ [providerField]: { $exists: false } }, { [providerField]: userData.id }],\n },\n {\n $set: {\n [providerField]: userData.id,\n },\n }\n );\n\n // If no document matched, figure out why\n if (updateResult.matchedCount === 0) {\n const currentUser = await usersCollection.findOne({ _id: userId });\n\n if (!currentUser || currentUser.status === 'deleted' || currentUser.status === 'disabled') {\n safelyCallHook(() =>\n authConfig.onOAuthLinkError?.({\n provider: userData.providerName,\n error: new Error('User account not found or not active'),\n session,\n connectionInfo,\n })\n );\n\n clearOAuthLinkCookie(res);\n\n sendOAuthError(res, 400, 'User account is not active.', outcome);\n return;\n }\n\n // Detect if the user already linked a different OAuth account\n const existingProviderId = currentUser?.authMethods?.[userData.providerName]?.id;\n\n if (existingProviderId && existingProviderId !== userData.id) {\n safelyCallHook(() =>\n authConfig.onOAuthLinkError?.({\n provider: userData.providerName,\n error: new Error(\n `User already has a different ${userData.providerName} account linked`\n ),\n session,\n connectionInfo,\n })\n );\n\n clearOAuthLinkCookie(res);\n\n sendOAuthError(\n res,\n 400,\n `You have already linked a different ${userData.providerName} account.`,\n outcome\n );\n return;\n }\n\n // Fallback safety guard in case the DB state does not match any expected branch\n safelyCallHook(() =>\n authConfig.onOAuthLinkError?.({\n provider: userData.providerName,\n error: new Error(`Unexpected OAuth linking state for ${userData.providerName}`),\n session,\n connectionInfo,\n })\n );\n\n clearOAuthLinkCookie(res);\n\n sendOAuthError(res, 400, `Unable to link ${userData.providerName} account.`, outcome);\n return;\n }\n\n const updatedUser = await usersCollection.findOne(\n { _id: userId },\n { readPreference: 'primary' }\n );\n\n if (updatedUser) {\n safelyCallHook(() =>\n authConfig.onAfterOAuthLink?.({\n provider: userData.providerName,\n user: updatedUser,\n session,\n connectionInfo,\n })\n );\n }\n\n // Redirect back to the app after successful link. On mobile this returns to\n // the deep link with no code: linking happens for an already-signed-in user,\n // so there is no new session to hand over.\n clearOAuthLinkCookie(res);\n\n if (outcome.platform === 'mobile') {\n res.set('Referrer-Policy', 'no-referrer');\n res.redirect(buildMobileRedirect(outcome.redirectUri, { linked: userData.providerName }));\n return;\n }\n\n res.redirect('/');\n } catch (error) {\n if (error instanceof MongoServerError && error.code === 11000) {\n safelyCallHook(() =>\n authConfig.onOAuthLinkError?.({\n provider: userData.providerName,\n error,\n session,\n connectionInfo,\n })\n );\n\n clearOAuthLinkCookie(res);\n\n sendOAuthError(\n res,\n 400,\n `This ${userData.providerName} account is already linked to a different user.`,\n outcome\n );\n return;\n }\n\n if (error instanceof Error) {\n safelyCallHook(() =>\n authConfig.onOAuthLinkError?.({\n provider: userData.providerName,\n error,\n session,\n connectionInfo,\n })\n );\n }\n\n clearOAuthLinkCookie(res);\n if (!res.headersSent) {\n throw error;\n }\n }\n}\n\nexport function validateOAuthCode(code: unknown): string | null {\n if (!code || typeof code !== 'string') {\n return null;\n }\n return code;\n}\n","import { getConfig } from '@/server';\nimport {\n Router,\n type Request,\n type Response,\n type NextFunction,\n type Router as ExpressRouter,\n} from 'express';\nimport {\n getRedirectUri,\n handleOAuthUserAuthentication,\n handleOAuthProviderLink,\n validateOAuthCode,\n type OAuthUserData,\n clearOAuthLinkCookie,\n validateOAuthStateAndGetMode,\n sendOAuthError,\n prepareOAuthInitiation,\n resolveMobileOutcomeFromCookie,\n toOAuthOutcome,\n} from './oauthCommon';\n\ninterface GoogleTokenResponse {\n access_token: string;\n expires_in: number;\n scope: string;\n token_type: string;\n id_token: string;\n}\n\ninterface GoogleUserInfo {\n id: string;\n name: string;\n given_name?: string;\n family_name?: string;\n email: string;\n verified_email: boolean;\n picture?: string;\n}\n\nasync function exchangeCodeForToken(\n code: string,\n clientId: string,\n clientSecret: string,\n redirectUri: string\n): Promise<GoogleTokenResponse> {\n const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n code,\n client_id: clientId,\n client_secret: clientSecret,\n redirect_uri: redirectUri,\n grant_type: 'authorization_code',\n }),\n });\n\n if (!tokenResponse.ok) {\n throw new Error(`Failed to exchange code for token: ${tokenResponse.statusText}`);\n }\n\n return tokenResponse.json();\n}\n\nasync function fetchGoogleUserInfo(accessToken: string): Promise<GoogleUserInfo> {\n const userInfoResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!userInfoResponse.ok) {\n throw new Error(`Failed to fetch user info: ${userInfoResponse.statusText}`);\n }\n\n return userInfoResponse.json();\n}\n\nasync function handleGoogleAuthenticationCallback(req: Request, res: Response) {\n const code = validateOAuthCode(req.query.code);\n\n if (!code) {\n // No usable state to validate yet (a declined consent screen sends none),\n // but the state cookie still records where the app lives, so this can be\n // reported into the app instead of as JSON in the device browser.\n sendOAuthError(\n res,\n 400,\n 'Missing authorization code',\n resolveMobileOutcomeFromCookie(req, 'authStateGoogle') ?? undefined,\n 'missing_code'\n );\n return;\n }\n\n const stateResult = validateOAuthStateAndGetMode(req, res, 'authStateGoogle');\n if (!stateResult) return;\n const { mode, linkedUserId } = stateResult;\n const outcome = toOAuthOutcome(stateResult);\n\n const googleClientId = String(getConfig('_system.user.auth.google.clientId'));\n const googleClientSecret = String(getConfig('_system.user.auth.google.clientSecret'));\n const redirectUri = getRedirectUri('google');\n\n try {\n // Exchange code for tokens\n const tokenData = await exchangeCodeForToken(\n code,\n googleClientId,\n googleClientSecret,\n redirectUri\n );\n\n // Fetch user info\n const googleUser = await fetchGoogleUserInfo(tokenData.access_token);\n\n const userData: OAuthUserData = {\n id: googleUser.id,\n email: googleUser.email,\n emailVerified: googleUser.verified_email,\n providerName: 'google',\n firstName: googleUser.given_name || undefined,\n lastName: googleUser.family_name || undefined,\n avatarUrl: googleUser.picture || undefined,\n };\n if (mode === 'link') {\n await handleOAuthProviderLink(req, res, userData, linkedUserId, outcome);\n } else {\n await handleOAuthUserAuthentication(req, res, userData, outcome);\n }\n } catch (error) {\n console.error('Google OAuth error:', error);\n if (mode === 'link') {\n clearOAuthLinkCookie(res);\n }\n sendOAuthError(res, 500, 'Authentication failed', outcome);\n }\n}\n\nfunction getRouter(): ExpressRouter {\n const googleAuthRouter = Router();\n\n // Middleware to check if Google auth is enabled and configured\n const checkGoogleEnabled = (_req: Request, res: Response, next: NextFunction) => {\n const googleEnabled = Boolean(getConfig('_system.user.auth.google.enabled'));\n const googleClientId = String(getConfig('_system.user.auth.google.clientId'));\n const googleClientSecret = String(getConfig('_system.user.auth.google.clientSecret'));\n\n if (!googleEnabled || !googleClientId || !googleClientSecret) {\n sendOAuthError(res, 503, 'Google authentication is not configured', {\n platform: 'api',\n });\n return;\n }\n\n next();\n };\n\n // Initiate OAuth flow\n googleAuthRouter.get(\n '/api/_internal/auth/google',\n checkGoogleEnabled,\n async (req: Request, res: Response) => {\n const googleClientId = String(getConfig('_system.user.auth.google.clientId'));\n const redirectUri = getRedirectUri('google');\n\n const initiation = await prepareOAuthInitiation(req, res, 'authStateGoogle');\n if (!initiation) return;\n const { state } = initiation;\n\n const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');\n authUrl.searchParams.append('client_id', googleClientId);\n authUrl.searchParams.append('redirect_uri', redirectUri);\n authUrl.searchParams.append('response_type', 'code');\n authUrl.searchParams.append('scope', 'profile email');\n authUrl.searchParams.append('access_type', 'online');\n authUrl.searchParams.append('state', state);\n\n res.redirect(authUrl.toString());\n }\n );\n\n // Handle OAuth callback\n googleAuthRouter.get(\n '/api/_internal/auth/google/callback',\n checkGoogleEnabled,\n handleGoogleAuthenticationCallback\n );\n\n return googleAuthRouter;\n}\n\nexport default getRouter;\n","import { getConfig } from '@/server';\nimport {\n Router,\n type Request,\n type Response,\n type NextFunction,\n type Router as ExpressRouter,\n} from 'express';\nimport {\n getRedirectUri,\n handleOAuthUserAuthentication,\n handleOAuthProviderLink,\n validateOAuthCode,\n type OAuthUserData,\n clearOAuthLinkCookie,\n validateOAuthStateAndGetMode,\n sendOAuthError,\n prepareOAuthInitiation,\n resolveMobileOutcomeFromCookie,\n toOAuthOutcome,\n} from './oauthCommon';\n\ninterface GitHubTokenResponse {\n access_token: string;\n token_type: string;\n scope: string;\n}\n\ninterface GitHubUserInfo {\n id: number;\n login: string;\n name: string;\n email: string | null;\n avatar_url?: string;\n}\n\ninterface GitHubEmail {\n email: string;\n primary: boolean;\n verified: boolean;\n visibility: 'public' | 'private' | null;\n}\n\nasync function exchangeCodeForToken(\n code: string,\n clientId: string,\n clientSecret: string,\n redirectUri: string\n): Promise<GitHubTokenResponse> {\n const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({\n client_id: clientId,\n client_secret: clientSecret,\n code,\n redirect_uri: redirectUri,\n }),\n });\n\n if (!tokenResponse.ok) {\n throw new Error(`Failed to exchange code for token: ${tokenResponse.statusText}`);\n }\n\n return tokenResponse.json();\n}\n\nasync function fetchGitHubUserInfo(accessToken: string): Promise<GitHubUserInfo> {\n const userInfoResponse = await fetch('https://api.github.com/user', {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/vnd.github.v3+json',\n },\n });\n\n if (!userInfoResponse.ok) {\n throw new Error(`Failed to fetch user info: ${userInfoResponse.statusText}`);\n }\n\n return userInfoResponse.json();\n}\n\nasync function fetchGitHubUserEmails(accessToken: string): Promise<GitHubEmail[]> {\n const response = await fetch('https://api.github.com/user/emails', {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/vnd.github.v3+json',\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch user emails: ${response.statusText}`);\n }\n\n return response.json();\n}\n\nasync function getGitHubUserEmail(\n githubUser: GitHubUserInfo,\n accessToken: string\n): Promise<string | null> {\n if (githubUser.email) {\n return githubUser.email;\n }\n\n const emails = await fetchGitHubUserEmails(accessToken);\n return emails.find((e) => e.primary && e.verified)?.email ?? null;\n}\n\nasync function handleGitHubAuthenticationCallback(req: Request, res: Response) {\n const code = validateOAuthCode(req.query.code);\n\n if (!code) {\n // No usable state to validate yet (a declined consent screen sends none),\n // but the state cookie still records where the app lives, so this can be\n // reported into the app instead of as JSON in the device browser.\n sendOAuthError(\n res,\n 400,\n 'Missing authorization code',\n resolveMobileOutcomeFromCookie(req, 'authStateGithub') ?? undefined,\n 'missing_code'\n );\n return;\n }\n\n const stateResult = validateOAuthStateAndGetMode(req, res, 'authStateGithub');\n if (!stateResult) return;\n const { mode, linkedUserId } = stateResult;\n const outcome = toOAuthOutcome(stateResult);\n\n const githubClientId = String(getConfig('_system.user.auth.github.clientId'));\n const githubClientSecret = String(getConfig('_system.user.auth.github.clientSecret'));\n const redirectUri = getRedirectUri('github');\n\n try {\n // Exchange code for access token\n const tokenData = await exchangeCodeForToken(\n code,\n githubClientId,\n githubClientSecret,\n redirectUri\n );\n\n // Fetch user info\n const githubUser = await fetchGitHubUserInfo(tokenData.access_token);\n\n // Resolve a usable GitHub email (public email or fallback to primary verified email)\n const githubEmail = await getGitHubUserEmail(githubUser, tokenData.access_token);\n\n if (!githubEmail) {\n if (mode === 'link') {\n clearOAuthLinkCookie(res);\n }\n\n sendOAuthError(\n res,\n 400,\n 'Unable to retrieve a primary verified email from GitHub. Please ensure your GitHub account has a verified email set as primary.',\n outcome\n );\n return;\n }\n\n const nameParts = githubUser.name ? githubUser.name.trim().split(/\\s+/) : [];\n const firstName = nameParts[0] || undefined;\n const lastName = nameParts.length > 1 ? nameParts.slice(1).join(' ') : undefined;\n\n const userData: OAuthUserData = {\n id: String(githubUser.id),\n email: githubEmail,\n emailVerified: true, // Assume public email is verified\n providerName: 'github',\n firstName,\n lastName,\n avatarUrl: githubUser.avatar_url || undefined,\n };\n\n if (mode === 'link') {\n await handleOAuthProviderLink(req, res, userData, linkedUserId, outcome);\n } else {\n await handleOAuthUserAuthentication(req, res, userData, outcome);\n }\n } catch (error) {\n console.error('GitHub OAuth error:', error);\n if (mode === 'link') {\n clearOAuthLinkCookie(res);\n }\n sendOAuthError(res, 500, 'Authentication failed', outcome);\n }\n}\n\nfunction getRouter(): ExpressRouter {\n const githubAuthRouter = Router();\n\n // Middleware to check if GitHub auth is enabled and configured\n const checkGitHubEnabled = (_req: Request, res: Response, next: NextFunction) => {\n const githubEnabled = Boolean(getConfig('_system.user.auth.github.enabled'));\n const githubClientId = String(getConfig('_system.user.auth.github.clientId'));\n const githubClientSecret = String(getConfig('_system.user.auth.github.clientSecret'));\n\n if (!githubEnabled || !githubClientId || !githubClientSecret) {\n sendOAuthError(res, 503, 'GitHub authentication is not configured', {\n platform: 'api',\n });\n return;\n }\n\n next();\n };\n\n // Initiate OAuth flow\n githubAuthRouter.get(\n '/api/_internal/auth/github',\n checkGitHubEnabled,\n async (req: Request, res: Response) => {\n const githubClientId = String(getConfig('_system.user.auth.github.clientId'));\n const redirectUri = getRedirectUri('github');\n const githubScopes = getConfig('_system.user.auth.github.scopes');\n const scopes = githubScopes\n ? String(githubScopes)\n .split(',')\n .map((s) => s.trim())\n .join(' ')\n : 'user:email';\n\n const initiation = await prepareOAuthInitiation(req, res, 'authStateGithub');\n if (!initiation) return;\n const { state } = initiation;\n\n const authUrl = new URL('https://github.com/login/oauth/authorize');\n authUrl.searchParams.append('client_id', githubClientId);\n authUrl.searchParams.append('redirect_uri', redirectUri);\n authUrl.searchParams.append('scope', scopes);\n authUrl.searchParams.append('state', state);\n\n res.redirect(authUrl.toString());\n }\n );\n\n // Handle OAuth callback\n githubAuthRouter.get(\n '/api/_internal/auth/github/callback',\n checkGitHubEnabled,\n handleGitHubAuthenticationCallback\n );\n\n return githubAuthRouter;\n}\n\nexport default getRouter;\n","import { Request, Response, NextFunction } from 'express';\nimport { RouteHandler } from './types';\nimport { ModelenceError } from '../error';\nimport { authenticate } from '../auth';\nimport { getMongodbUri } from '../db/client';\nimport type { Context } from '../methods/types';\nimport { startTransaction, redactSensitive, captureError } from '../telemetry';\n\n// TODO: Use cookies for authentication and automatically add session/user to context if accessing from browser\nexport function createRouteHandler(method: string, path: string, handler: RouteHandler) {\n return async (req: Request, res: Response, next: NextFunction) => {\n const authToken = req.headers['x-modelence-auth-token'];\n let context: Pick<Context, 'session' | 'user'> = { session: null, user: null };\n\n if (typeof authToken === 'string' && getMongodbUri()) {\n try {\n const { session, user } = await authenticate(authToken);\n context = { session, user };\n } catch {\n // If authentication fails, context remains null\n }\n }\n\n const transaction = startTransaction('route', `route:${method.toLowerCase()}:${path}`, {\n method,\n path,\n // Redact secrets (e.g. tokens in the URL) before they reach the APM sink.\n query: redactSensitive(req.query),\n body: redactSensitive(req.body),\n params: redactSensitive(req.params),\n });\n\n try {\n const response = await handler(\n {\n query: req.query as Record<string, string>,\n body: req.body,\n params: req.params as Record<string, string>,\n headers: req.headers as Record<string, string>,\n cookies: req.cookies,\n rawBody: Buffer.isBuffer(req.body) ? req.body : undefined,\n req,\n res,\n next,\n },\n context\n );\n\n transaction.end();\n\n // If the handler returns null, we expect it to handle the response itself\n if (response) {\n res.status(response.status || 200);\n\n // Apply response headers before sending the body/redirect, otherwise\n // res.redirect()/res.send() flush the response and the headers are lost.\n if (response.contentType) {\n res.setHeader('Content-Type', response.contentType);\n }\n\n // Custom headers are applied after contentType so callers can override\n // it with headers['Content-Type'] when needed.\n if (response.headers) {\n Object.entries(response.headers).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n }\n\n if (response.redirect) {\n res.redirect(response.redirect);\n } else {\n res.send(response.data);\n }\n }\n } catch (error) {\n captureError(error instanceof Error ? error : new Error(String(error)));\n transaction.end('error');\n\n if (error instanceof ModelenceError) {\n res.status(error.status).send(error.message);\n } else {\n console.error(`Error in route handler: ${req.path}`);\n console.error(error);\n res.status(500).send(String(error));\n }\n }\n };\n}\n","import googleAuthRouter from '@/auth/providers/google';\nimport githubAuthRouter from '@/auth/providers/github';\nimport { runMethod } from '@/methods';\nimport { getResponseTypeMap, sanitizeResult } from '@/methods/serialize';\nimport { createRouteHandler } from '@/routes/handler';\nimport { HttpMethod } from '@/server';\nimport { logInfo } from '@/telemetry';\nimport cookieParser from 'cookie-parser';\nimport express, { Request, Response } from 'express';\nimport http from 'http';\nimport z from 'zod';\nimport type { AppServer } from '../types';\nimport { authenticate } from '../auth';\nimport { getUnauthenticatedRoles } from '../auth/role';\nimport { getMongodbUri } from '../db/client';\nimport { ModelenceError } from '../error';\nimport { Module } from './module';\nimport { isSetupRequired } from './setupStatus';\nimport { ConnectionInfo } from '@/methods/types';\nimport { ServerChannel } from '@/websocket/serverChannel';\nimport { getSecurityConfig } from './securityConfig';\nimport { getWebsocketConfig } from './websocketConfig';\nimport { getConfig } from '@/config/server';\nimport { getLocalSiteUrl } from '@/config/local';\nimport { issueLinkNonce } from '@/auth/session';\n\nfunction getBodyParserMiddleware(config?: {\n json?: boolean | { limit?: string };\n urlencoded?: boolean | { limit?: string; extended?: boolean };\n raw?: boolean | { limit?: string; type?: string | string[] };\n}) {\n const middlewares: express.RequestHandler[] = [];\n\n if (!config) {\n // Default: apply JSON and urlencoded parsing\n middlewares.push(express.json({ limit: '16mb' }));\n middlewares.push(express.urlencoded({ extended: true, limit: '16mb' }));\n return middlewares;\n }\n\n // Handle JSON parsing\n if (config.json !== false) {\n const jsonOptions = typeof config.json === 'object' ? config.json : { limit: '16mb' };\n middlewares.push(express.json(jsonOptions));\n }\n\n // Handle URL-encoded parsing\n if (config.urlencoded !== false) {\n const urlencodedOptions =\n typeof config.urlencoded === 'object' ? config.urlencoded : { extended: true, limit: '16mb' };\n middlewares.push(express.urlencoded(urlencodedOptions));\n }\n\n // Handle raw body parsing\n if (config.raw) {\n const rawOptions = typeof config.raw === 'object' ? config.raw : {};\n const defaultRawOptions = {\n limit: rawOptions.limit || '16mb',\n type: rawOptions.type || '*/*',\n };\n middlewares.push(express.raw(defaultRawOptions));\n }\n\n return middlewares;\n}\n\nfunction registerModuleRoutes(\n app: express.Application,\n modules: Module[],\n cors: express.RequestHandler\n) {\n for (const module of modules) {\n for (const route of module.routes) {\n const { path, handlers, body } = route;\n const middlewares = getBodyParserMiddleware(body);\n\n Object.entries(handlers).forEach(([method, handler]) => {\n // CORS first: it only sets response headers and always calls next(), so\n // it must not sit behind a body parser that could reject the request\n // before the headers are attached.\n app[method as HttpMethod](\n path,\n cors,\n ...middlewares,\n createRouteHandler(method, path, handler)\n );\n });\n }\n }\n}\n\nlet globalProcessListenersRegistered = false;\n\nexport async function startServer(\n server: AppServer,\n {\n combinedModules,\n channels,\n }: {\n combinedModules: Module[];\n channels: ServerChannel[];\n }\n) {\n const app = express();\n\n // Express's proxy-addr implementation walks X-Forwarded-For from the socket\n // toward the client and stops at the first untrusted hop.\n // This is important for IP rate limits: selecting the left-most value would\n // let a caller evade them by prepending an arbitrary address.\n const { trustedProxies } = getSecurityConfig();\n const envTrustedProxies = parseTrustedProxies(process.env.MODELENCE_TRUSTED_PROXIES);\n // Keep the historical trust-all behavior when neither setting is present so\n // upgrading does not collapse every client behind a reverse proxy into one\n // rate-limit identity. Modelence-hosted environments set the trusted proxy\n // ranges before rolling out this framework version.\n const trustProxy = envTrustedProxies ?? trustedProxies;\n app.set('trust proxy', trustProxy === undefined ? true : trustProxy);\n\n app.use(cookieParser());\n\n // CSP and X-Frame-Options belong on every response, SSR pages included, so\n // this one stays global.\n app.use(securityHeadersMiddleware());\n // Preflights only. The matching response headers are attached per-route below,\n // which keeps SSR pages and static assets out of CORS scope.\n app.use(corsPreflightMiddleware());\n\n const cors = corsRouteMiddleware();\n\n // Register module routes first (with per-route body parser config)\n registerModuleRoutes(app, combinedModules, cors);\n\n // Apply global body parsing for remaining routes\n app.use(express.json({ limit: '16mb' }));\n app.use(express.urlencoded({ extended: true, limit: '16mb' }));\n\n // Both OAuth routers register only under this prefix, so one mount covers\n // every route they add, including the callbacks.\n app.use('/api/_internal/auth', cors);\n app.use(googleAuthRouter());\n app.use(githubAuthRouter());\n\n // Browser OAuth linking: set httpOnly cookie so the authToken never travels in a URL.\n app.post('/api/_internal/auth/set-link-cookie', cors, async (req: Request, res: Response) => {\n const { session } = await getCallContext(req, res);\n\n if (!session?.userId) {\n res.status(401).json({ error: 'Not authenticated' });\n return;\n }\n\n res.cookie('oauthLinkToken', session.authToken, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/api/_internal/auth/',\n maxAge: 10 * 60 * 1000, // 10 minutes\n });\n\n res.json({ ok: true });\n });\n\n // React Native OAuth linking: issues a single-use nonce the app puts in the OAuth URL.\n app.post('/api/_internal/auth/issue-link-nonce', cors, async (req: Request, res: Response) => {\n const { session } = await getCallContext(req, res);\n\n if (!session?.userId) {\n res.status(401).json({ error: 'Not authenticated' });\n return;\n }\n\n const nonce = await issueLinkNonce(String(session.userId));\n res.json({ nonce });\n });\n\n app.post('/api/_internal/method/:methodName(*)', cors, async (req: Request, res: Response) => {\n const methodName = req.params.methodName as string;\n const context = await getCallContext(req, res);\n\n try {\n const result = sanitizeResult(await runMethod(methodName, req.body.args, context));\n res.json({\n data: result,\n typeMap: getResponseTypeMap(result),\n });\n } catch (error) {\n handleMethodError(res, methodName, error);\n }\n });\n\n const httpServer = http.createServer(app);\n\n // Keep-alive must exceed the idle timeout of any load balancer in front of the\n // app, or Node destroys pooled sockets the LB still considers live and requests\n // dispatched onto them fail with intermittent 502s. Node's stock default is 5s,\n // below every common LB idle timeout (AWS ALB defaults to 60s). This is the\n // post-response idle timeout only; it never applies to an in-flight request.\n const keepAliveTimeoutMs = Number(process.env.MODELENCE_KEEP_ALIVE_TIMEOUT_MS) || 65000;\n httpServer.keepAliveTimeout = keepAliveTimeoutMs;\n // Must exceed keepAliveTimeout, or requests whose headers are still in flight\n // when keep-alive expires are dropped (nodejs/node#27363).\n httpServer.headersTimeout = keepAliveTimeoutMs + 5000;\n\n await server.init({ httpServer });\n\n if (server.middlewares) {\n app.use(server.middlewares());\n }\n\n app.all('*', (req: Request, res: Response, next) => {\n Promise.resolve(server.handler(req, res)).catch(next);\n });\n\n if (!globalProcessListenersRegistered) {\n globalProcessListenersRegistered = true;\n process.on('unhandledRejection', (reason, promise) => {\n console.error('Unhandled Promise Rejection:');\n console.error(reason instanceof Error ? reason.stack : reason);\n console.error('Promise:', promise);\n });\n\n // Global uncaught exceptions\n process.on('uncaughtException', (error) => {\n console.error('Uncaught Exception:');\n console.error(error.stack); // This gives you the full stack trace\n console.trace('Full application stack:'); // Additional context\n });\n }\n\n const websocketProvider = getWebsocketConfig()?.provider;\n if (websocketProvider) {\n void websocketProvider.init({\n httpServer,\n channels,\n });\n }\n\n const port = process.env.MODELENCE_PORT || process.env.PORT || 3000;\n httpServer.listen(port, () => {\n logInfo(`Application started`, { source: 'app' });\n const siteUrl = getConfig('_system.site.url') || getLocalSiteUrl();\n console.log(`\\nApplication started on ${siteUrl}\\n`);\n\n // The browser shows the setup screen in this state (see\n // client/SetupScreen); say the same thing here for anyone who only sees\n // the terminal — a developer glancing at logs, or a coding agent running\n // the dev server.\n if (isSetupRequired()) {\n console.log(\n 'This project is not connected to a backend yet, so the app serves setup instructions instead of its UI.\\n' +\n 'To connect it to Modelence Cloud, run `npx modelence@latest setup` and restart the dev server.\\n'\n );\n }\n });\n}\n\nexport async function getCallContext(req: Request, res: Response | null = null) {\n const path = (req.path ?? req.url ?? '').split('?')[0];\n\n const isOAuthCallback = path.startsWith('/api/_internal/auth/') && path.endsWith('/callback');\n\n const body = (req.body ?? {}) as Record<string, unknown>;\n\n const authToken = z\n .string()\n .nullish()\n .transform((val) => val ?? null)\n .parse(\n req.cookies.authToken ||\n (isOAuthCallback ? req.cookies.oauthLinkToken : null) ||\n body.authToken\n );\n\n const clientInfo = z\n .object({\n screenWidth: z.number(),\n screenHeight: z.number(),\n windowWidth: z.number(),\n windowHeight: z.number(),\n pixelRatio: z.number(),\n orientation: z.string().nullable(),\n })\n .nullish()\n .parse(body.clientInfo) ?? {\n screenWidth: 0,\n screenHeight: 0,\n windowWidth: 0,\n windowHeight: 0,\n pixelRatio: 1,\n orientation: null,\n };\n\n const connectionInfo: ConnectionInfo = {\n ip: getClientIp(req),\n userAgent: req.get('user-agent'),\n acceptLanguage: req.get('accept-language'),\n referrer: req.get('referrer'),\n baseUrl: getRequestBaseUrl(req),\n };\n\n const hasDatabase = Boolean(getMongodbUri());\n if (hasDatabase) {\n const { session, user, roles } = await authenticate(authToken);\n return {\n clientInfo,\n connectionInfo,\n session,\n user,\n roles,\n req,\n res,\n };\n }\n\n return {\n clientInfo,\n connectionInfo,\n session: null,\n user: null,\n roles: getUnauthenticatedRoles(),\n req,\n res,\n };\n}\n\nfunction handleMethodError(res: Response, methodName: string, error: unknown) {\n // TODO: add an option to silence these error console logs, especially when Elastic logs are configured\n\n if (error instanceof ModelenceError) {\n if (error.status >= 500 && error.status < 600) {\n console.error(`Error calling ${methodName}:`, error);\n }\n // Surface a machine-readable code (when present) via a header so clients can\n // branch on the error kind without parsing the human-readable message. The\n // response body is left as the message text to preserve the existing format.\n if (error.code) {\n res.setHeader('X-Modelence-Error-Code', error.code);\n }\n res.status(error.status).send(error.message);\n return;\n }\n\n if (error instanceof Error && error?.constructor?.name === 'ZodError' && 'errors' in error) {\n let errorMessage = '';\n try {\n errorMessage = parseZodError(error as z.ZodError);\n } catch (parsingError) {\n console.error(`Error parsing Zod error in ${methodName}:`, parsingError);\n errorMessage = 'Validation failed';\n }\n res.status(400).send(errorMessage);\n return;\n }\n\n console.error(`Error calling ${methodName}:`, error);\n res.status(500).send(error instanceof Error ? error.message : String(error));\n}\n\nfunction parseZodError(zodError: z.ZodError): string {\n const flattened = zodError.flatten();\n const fieldMessages = Object.entries(flattened.fieldErrors).map(\n ([key, errors]) => `${key}: ${(errors ?? []).join(', ')}`\n );\n const formMessages = flattened.formErrors;\n const allMessages = [...fieldMessages, ...formMessages].filter(Boolean);\n return allMessages.join('; ');\n}\n\nfunction securityHeadersMiddleware(): express.RequestHandler {\n const { frameAncestors } = getSecurityConfig();\n const hasCustomAncestors = frameAncestors && frameAncestors.length > 0;\n const ancestors = hasCustomAncestors ? [\"'self'\", ...frameAncestors].join(' ') : \"'self'\";\n\n return (_req, res, next) => {\n res.setHeader('Content-Security-Policy', `frame-ancestors ${ancestors}`);\n // X-Frame-Options only supports DENY and SAMEORIGIN (ALLOW-FROM is deprecated).\n // When custom ancestors are configured, only CSP frame-ancestors can express that,\n // so we omit X-Frame-Options to avoid conflicting with the CSP directive.\n if (!hasCustomAncestors) {\n res.setHeader('X-Frame-Options', 'SAMEORIGIN');\n }\n next();\n };\n}\n\n/**\n * Applies the CORS response headers for a single request, returning whether the\n * request's Origin is allowed.\n *\n * Shared by the preflight and per-route middlewares so an allowed origin gets\n * exactly the same headers whether the browser is preflighting or making the\n * real call — a mismatch between the two is invisible in tests and shows up as\n * an intermittent browser-only failure.\n */\nfunction applyCorsHeaders(\n req: express.Request,\n res: express.Response,\n allowed: Set<string>\n): boolean {\n // Set before the match check, not inside it: a response sent to a\n // disallowed origin (or to a request with no Origin at all) still depends on\n // Origin, and without Vary a CDN or shared proxy may cache that\n // header-less response and later replay it to an allowed origin.\n // res.vary appends and dedupes, so a Vary set by static/compression\n // middleware further down the stack survives — setHeader would clobber it.\n res.vary('Origin');\n\n const origin = req.headers.origin;\n const isAllowed = typeof origin === 'string' && allowed.has(origin);\n if (!isAllowed) {\n return false;\n }\n\n // Echo the matched origin rather than '*': a wildcard is rejected by\n // browsers on credentialed requests, which the cookie-based web flows use.\n res.setHeader('Access-Control-Allow-Origin', origin);\n res.setHeader('Access-Control-Allow-Credentials', 'true');\n // Reflect what the preflight asked for, falling back to the only header\n // method calls send. A custom route taking e.g. Authorization would\n // otherwise be blocked before its handler ever ran.\n const requestedHeaders = req.headers['access-control-request-headers'];\n res.setHeader(\n 'Access-Control-Allow-Headers',\n typeof requestedHeaders === 'string' ? requestedHeaders : 'Content-Type'\n );\n // Covers every verb RouteDefinition's HttpMethod allows, so a custom\n // route is not silently blocked by a preflight the framework rejects.\n res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS');\n // Browsers expose only a small safelist of response headers to JS. Without\n // this the error-code header is hidden cross-origin, so MethodError.code\n // silently becomes undefined and clients cannot branch on the error kind.\n res.setHeader('Access-Control-Expose-Headers', 'X-Modelence-Error-Code');\n return true;\n}\n\n/**\n * Answers CORS preflights. Mounted globally, unlike the per-route header\n * middleware: a preflight must be answered before route dispatch, and Express\n * does not reliably match `OPTIONS /todos` against a `.get('/todos')`\n * registration. Scoping this per-route would leave those preflights to fall\n * through to the SSR catch-all, which answers them with HTML.\n *\n * It only ever handles preflights, so it never adds headers to a real response —\n * that stays the per-route middleware's job, which is what keeps SSR and static\n * responses out of scope.\n */\nfunction corsPreflightMiddleware(): express.RequestHandler {\n const { allowedOrigins } = getSecurityConfig();\n const allowed = new Set(allowedOrigins ?? []);\n\n return (req, res, next) => {\n // Opt-in only. With no configured origins this is a no-op, so deployments\n // that add CORS at a proxy/router keep exactly one Access-Control-Allow-Origin\n // header — a duplicate is invalid and browsers reject the response outright.\n //\n // A preflight is OPTIONS *plus* Access-Control-Request-Method. Matching on\n // the method alone would swallow every other OPTIONS request: HttpMethod\n // lets a module register an `options` (or `all`/`use`) handler, and this is\n // mounted ahead of those routes, so a plain OPTIONS must fall through to\n // them rather than be answered here.\n const isPreflight =\n req.method === 'OPTIONS' && typeof req.headers['access-control-request-method'] === 'string';\n if (allowed.size === 0 || !isPreflight) {\n next();\n return;\n }\n\n const isAllowed = applyCorsHeaders(req, res, allowed);\n // Allow-Headers is reflected from the request, so the preflight result\n // depends on this header too and must not be cached across requests that\n // ask for different ones.\n res.vary('Access-Control-Request-Headers');\n if (isAllowed) {\n // Every method call sends Content-Type: application/json, so every one\n // is preflighted. Without Max-Age browsers re-preflight constantly\n // (Chrome defaults to 5s), doubling the request count. 600s is under\n // Chrome's 7200s cap and Firefox's 86400s cap, so it applies as given.\n res.setHeader('Access-Control-Max-Age', '600');\n }\n res.sendStatus(isAllowed ? 204 : 403);\n };\n}\n\n/**\n * Adds CORS headers to a real (non-preflight) response.\n *\n * Attached to the app's route surface — module routes and the framework's own\n * API routes — rather than mounted globally, so an allowlisted origin can call\n * the API without also being able to read SSR pages and static assets with\n * credentials. The scope is derived from the routes actually registered rather\n * than matched by path pattern: module routes carry no framework-imposed prefix\n * (the docs' example mounts `/todos` at the root), so no pattern could express\n * \"the API\" without silently missing user-defined routes.\n */\nfunction corsRouteMiddleware(): express.RequestHandler {\n const { allowedOrigins } = getSecurityConfig();\n const allowed = new Set(allowedOrigins ?? []);\n\n return (req, res, next) => {\n if (allowed.size > 0) {\n applyCorsHeaders(req, res, allowed);\n }\n next();\n };\n}\n\nfunction getRequestBaseUrl(req: Request): string {\n // Behind a reverse proxy the inbound Host header / connection protocol can\n // reflect the internal container address rather than the public URL. Honor\n // the X-Forwarded-Host / X-Forwarded-Proto headers when present (the first\n // value in each comma-separated list is the original client-facing value),\n // falling back to the direct request values otherwise.\n const forwardedHost = req.headers['x-forwarded-host'];\n const host =\n (Array.isArray(forwardedHost) ? forwardedHost[0] : forwardedHost?.split(',')[0])?.trim() ||\n req.get('host');\n\n // Do not parse X-Forwarded-Proto directly: `req.protocol` already resolves it\n // through the `trust proxy` setting, so an untrusted caller cannot influence\n // the base URL used for auth callbacks and emails.\n return `${req.protocol}://${host}`;\n}\n\n/**\n * Parses the comma-separated `MODELENCE_TRUSTED_PROXIES` value into the array\n * form Express expects. Returns undefined when unset or empty so the caller can\n * fall back to the `startApp()` security config.\n */\nfunction parseTrustedProxies(value: string | undefined): string[] | undefined {\n const entries = value\n ?.split(',')\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0);\n\n return entries?.length ? entries : undefined;\n}\n\n/**\n * Whether the immediate peer on the socket is one of the configured trusted\n * proxies. Only the socket address is consulted: it is the one value in the\n * request a caller cannot forge.\n */\nfunction isPeerTrusted(req: Request): boolean {\n const peerIp = req.socket?.remoteAddress;\n if (!peerIp) {\n return false;\n }\n\n // Express compiles the `trust proxy` setting into this predicate. It is\n // absent when the app was never configured (older embeddings, tests), in\n // which case we fall back to the `trust proxy` value itself.\n const trust = req.app?.get?.('trust proxy fn');\n if (typeof trust !== 'function') {\n return req.app?.get?.('trust proxy') !== false;\n }\n\n return Boolean(trust(peerIp, 0));\n}\n\nfunction getClientIp(req: Request): string | undefined {\n // Cloudflare and similar proxies expose the originating client in a\n // single-value header. Prefer it over the X-Forwarded-For chain, which\n // Cloudflare appends to rather than overwrites, but only when the peer is a\n // trusted proxy - otherwise a direct caller could set it themselves.\n const { clientIpHeader } = getSecurityConfig();\n if (clientIpHeader && isPeerTrusted(req)) {\n const forwardedIp = req.headers[clientIpHeader.toLowerCase()];\n const clientIp = (Array.isArray(forwardedIp) ? forwardedIp[0] : forwardedIp)?.trim();\n if (clientIp) {\n return normalizeIp(clientIp);\n }\n }\n\n // `req.ip` only incorporates X-Forwarded-For according to the application's\n // `trust proxy` setting. Never parse the header directly: its left-most value\n // is controlled by the caller unless every ingress proxy overwrites it.\n const directIp = req.ip || req.socket?.remoteAddress;\n if (directIp) {\n return normalizeIp(directIp);\n }\n\n return undefined;\n}\n\nfunction normalizeIp(ip: string): string {\n // Remove IPv6-to-IPv4 mapping prefix\n return ip.startsWith('::ffff:') ? ip.substring(7) : ip;\n}\n"]}