← All @molecule/* packages · App templates
@molecule/api-resource-userAPI resource · auth · API (Node) · v1.4.0 · Apache-2.0
User registration, login, JWT sessions
npm install @molecule/api-resource-user@molecule/api-resource-user is an API resource: the routes, validation and storage for auth, built on the database and auth cores so it runs on whichever providers your app has bonded.
// Extend safely: a display field in Props, a secret in SecretProps.
// propsSchema: { …, timezone: z.string().optional() } // safe → client
// secretPropsSchema: { …, passwordResetToken: z.string().optional() } // server-only, secrets table
// A custom handler returns SAFE props — never the secrets row.
router.get('/me/timezone', async (req, res) => {
const userId = getUserId(res)
if (!userId) return res.status(401).json({ error: 'Authentication required.' })
const user = await findById('users', userId) // the users table holds Props only
res.json({ timezone: user?.timezone }) // never spread a secrets-table row here
})Works with: @molecule/api-bond, @molecule/api-config, @molecule/api-database, @molecule/api-i18n, @molecule/api-jwt, @molecule/api-locales-user, @molecule/api-locales-user-payments, @molecule/api-password, @molecule/api-rate-limit, @molecule/api-resource, @molecule/api-resource-device, @molecule/api-secrets
Secrets: JWT_PRIVATE_KEY, JWT_PUBLIC_KEY
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
The user resource types, schema, and definition.
// Extend safely: a display field in Props, a secret in SecretProps.
// propsSchema: { …, timezone: z.string().optional() } // safe → client
// secretPropsSchema: { …, passwordResetToken: z.string().optional() } // server-only, secrets table
// A custom handler returns SAFE props — never the secrets row.
router.get('/me/timezone', async (req, res) => {
const userId = getUserId(res)
if (!userId) return res.status(401).json({ error: 'Authentication required.' })
const user = await findById('users', userId) // the users table holds Props only
res.json({ timezone: user?.timezone }) // never spread a secrets-table row here
})
resource
npm install @molecule/api-resource-user @molecule/api-bond @molecule/api-config @molecule/api-database @molecule/api-entitlements @molecule/api-i18n @molecule/api-jwt @molecule/api-locales-user @molecule/api-locales-user-payments @molecule/api-password @molecule/api-payments @molecule/api-push-notifications @molecule/api-rate-limit @molecule/api-resource @molecule/api-resource-device @molecule/api-secrets @molecule/api-two-factor zod
UserRequestHandlerMapShape of the user request-handler map produced by createRequestHandlerMap.
Names match the route definitions in routes.ts. Exported so helpers that
accept the map (e.g. mountDefaultUserAuthRoutes, mountDefaultUserCrudRoutes)
can type their parameter precisely instead of widening to
Record<string, MoleculeRequestHandler>.
interface UserRequestHandlerMap {
auth: MoleculeRequestHandler
authSelf: MoleculeRequestHandler
rateLimitAuth: MoleculeRequestHandler
rateLimitTwoFactor: MoleculeRequestHandler
create: MoleculeRequestHandler
logIn: MoleculeRequestHandler
oauthAuthorize: MoleculeRequestHandler
logInOAuth: MoleculeRequestHandler
logout: MoleculeRequestHandler
read: MoleculeRequestHandler
readSelf: MoleculeRequestHandler
update: MoleculeRequestHandler
del: MoleculeRequestHandler
updatePassword: MoleculeRequestHandler
forgotPassword: MoleculeRequestHandler
resetPassword: MoleculeRequestHandler
verifyTwoFactor: MoleculeRequestHandler
updatePlan: MoleculeRequestHandler
verifyPayment: MoleculeRequestHandler
billingPortal: MoleculeRequestHandler
handlePaymentNotification: MoleculeRequestHandler
requireWebhookAuthenticity: MoleculeRequestHandler
}
CreateOAuthPropsCreate O Auth Props type.
type CreateOAuthProps = z.infer<typeof createOAuthPropsSchema>
CreatePropsCreate Props type.
type CreateProps = z.infer<typeof createPropsSchema>
CreateSecretPropsCreate Secret Props type.
type CreateSecretProps = z.infer<typeof createSecretPropsSchema>
PropsUser props type inferred from schema.
type Props = z.infer<typeof propsSchema>
SecretPropsSecret Props type.
type SecretProps = z.infer<typeof secretPropsSchema>
SessionUser session data (userId, email, role, permissions, metadata) inferred from sessionSchema.
type Session = z.infer<typeof sessionSchema>
UpdatePasswordSecretPropsUpdate Password Secret Props type.
type UpdatePasswordSecretProps = z.infer<typeof updatePasswordSecretPropsSchema>
UpdatePlanPropsUpdate Plan Props type.
type UpdatePlanProps = z.infer<typeof updatePlanPropsSchema>
UpdatePropsUpdate Props type.
type UpdateProps = z.infer<typeof updatePropsSchema>
VerifyTwoFactorPropsVerify Two Factor Props type.
type VerifyTwoFactorProps = z.infer<typeof verifyTwoFactorPropsSchema>
VerifyTwoFactorSecretPropsVerify Two Factor Secret Props type.
type VerifyTwoFactorSecretProps = z.infer<typeof verifyTwoFactorSecretPropsSchema>
createRequestHandlerMap(createRequestHandler)Creates the full request handler map for the User resource. Optional features (OAuth, payments) are conditionally included based on bonded providers.
Handler names match the route definitions in routes.ts.
function createRequestHandlerMap(
createRequestHandler: (
handler: Handler,
) => (req: MoleculeRequest, res: MoleculeResponse, next: MoleculeNextFunction) => Promise<void>,
): UserRequestHandlerMap
createRequestHandler — Factory from @molecule/api-resource that wraps handler configs into Express middleware.Returns: A UserRequestHandlerMap of handler names to Express middleware.
createResource(options)Creates a user resource definition with optional OAuth servers and plan keys.
function createResource(options?: {
oauthServers?: OAuthServers
planKeys?: PlanKeys
}): types.Resource<unknown>
options — Optional configuration.options.oauthServers — Tuple of allowed OAuth server names (e.g. ['google', 'github']). Constrains the oauthServer schema field.options.planKeys — Tuple of allowed plan key strings (e.g. ['free', 'pro']). Constrains the planKey schema field.Returns: A Resource with name 'User', table 'users', and a Zod schema reflecting the options.
createSchema(options)Creates a full schema for user props.
OAuth servers and plan keys can be constrained by passing them as options.
function createSchema(options?: { oauthServers?: OAuthServers; planKeys?: PlanKeys }): z.ZodObject<
{
id: z.ZodString
createdAt: z.ZodString
updatedAt: z.ZodString
username: z.ZodOptional<z.ZodString>
name: z.ZodOptional<z.ZodString>
email: z.ZodOptional<z.ZodNullable<z.ZodString>>
emailVerified: z.ZodOptional<z.ZodBoolean>
avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>
bio: z.ZodOptional<z.ZodNullable<z.ZodString>>
twoFactorEnabled: z.ZodOptional<z.ZodBoolean>
oauthServer:
| z.ZodOptional<z.ZodString>
| z.ZodOptional<
z.ZodEnum<{
[k in keyof { [k in NonNullable<OAuthServers>[number]]: k }]: {
[k in NonNullable<OAuthServers>[number]]: k
}[k]
}>
>
oauthId: z.ZodOptional<z.ZodString>
oauthData: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>
planKey:
| z.ZodOptional<z.ZodString>
| z.ZodOptional<
z.ZodEnum<{
[k in keyof { [k in NonNullable<PlanKeys>[number]]: k }]: {
[k in NonNullable<PlanKeys>[number]]: k
}[k]
}>
>
planExpiresAt: z.ZodOptional<z.ZodString>
planAutoRenews: z.ZodOptional<z.ZodBoolean>
},
z.core.$strip
>
options — Optional configuration.options.oauthServers — Tuple of allowed OAuth server names. Constrains oauthServer to a Zod enum.options.planKeys — Tuple of allowed plan key strings. Constrains planKey to a Zod enum.Returns: A Zod object schema extending basePropsSchema with user-specific fields (username, email, OAuth, plan).
createOAuthPropsSchemaSchema for creating a user via OAuth.
const createOAuthPropsSchema: z.ZodObject<
{
username: z.ZodOptional<z.ZodString>
name: z.ZodOptional<z.ZodString>
email: z.ZodOptional<z.ZodNullable<z.ZodString>>
emailVerified: z.ZodOptional<z.ZodBoolean>
avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>
bio: z.ZodOptional<z.ZodNullable<z.ZodString>>
oauthServer: z.ZodOptional<z.ZodString> | z.ZodOptional<z.ZodEnum<{}>>
oauthId: z.ZodOptional<z.ZodString>
oauthData: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>
},
z.core.$strip
>
createPropsSchemaSchema for creating a user via password.
const createPropsSchema: z.ZodObject<
{
username: z.ZodOptional<z.ZodString>
name: z.ZodOptional<z.ZodString>
email: z.ZodOptional<z.ZodNullable<z.ZodString>>
},
z.core.$strip
>
createSecretPropsSchemaSchema for creating secret props (password hash only).
const createSecretPropsSchema: z.ZodObject<
{ passwordHash: z.ZodOptional<z.ZodString> },
z.core.$strip
>
i18nRegisteredThe i18n registered.
const i18nRegistered: true
MAX_AVATAR_LENGTHMaximum length (in characters) of a user's avatar. Sized to permit a small
inline data-URI (~256KB) without requiring an external upload/storage bond.
Larger avatars must be hosted elsewhere and referenced by URL.
const MAX_AVATAR_LENGTH: number
MAX_BIO_LENGTHMaximum length (in characters) of a user's bio.
const MAX_BIO_LENGTH: 1000
propsSchemaDefault schema for user props.
const propsSchema: z.ZodObject<
{
id: z.ZodString
createdAt: z.ZodString
updatedAt: z.ZodString
username: z.ZodOptional<z.ZodString>
name: z.ZodOptional<z.ZodString>
email: z.ZodOptional<z.ZodNullable<z.ZodString>>
emailVerified: z.ZodOptional<z.ZodBoolean>
avatar: z.ZodOptional<z.ZodNullable<z.ZodString>>
bio: z.ZodOptional<z.ZodNullable<z.ZodString>>
twoFactorEnabled: z.ZodOptional<z.ZodBoolean>
oauthServer: z.ZodOptional<z.ZodString> | z.ZodOptional<z.ZodEnum<{}>>
oauthId: z.ZodOptional<z.ZodString>
oauthData: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>
planKey: z.ZodOptional<z.ZodString> | z.ZodOptional<z.ZodEnum<{}>>
planExpiresAt: z.ZodOptional<z.ZodString>
planAutoRenews: z.ZodOptional<z.ZodBoolean>
},
z.core.$strip
>
resourceDefault user resource definition.
const resource: types.Resource<unknown>
resourceUserSecretDefinitionsSecret definitions required by the user resource.
const resourceUserSecretDefinitions: SecretDefinition[]
routesRoute definitions for the User resource. Routes marked optional require additional packages to be installed.
Declarative route definitions used by the injection engine.
const routes: (
| { method: 'post'; path: string; middlewares: string[]; handler: string; optional?: undefined }
| { method: 'get'; path: string; middlewares: string[]; handler: string; optional: string }
| { method: 'post'; path: string; middlewares: string[]; handler: string; optional: string }
| { method: 'get'; path: string; middlewares: string[]; handler: string; optional?: undefined }
| { method: 'patch'; path: string; middlewares: string[]; handler: string; optional?: undefined }
| { method: 'delete'; path: string; middlewares: string[]; handler: string; optional?: undefined }
)[]
secretPropsSchemaSecret properties stored in a separate table.
const secretPropsSchema: z.ZodObject<
{
id: z.ZodString
passwordHash: z.ZodOptional<z.ZodString>
passwordResetToken: z.ZodOptional<z.ZodString>
passwordResetTokenAt: z.ZodOptional<z.ZodString>
pendingTwoFactorSecret: z.ZodOptional<z.ZodString>
twoFactorSecret: z.ZodOptional<z.ZodString>
lastTwoFactorTimeStep: z.ZodOptional<z.ZodNumber>
},
z.core.$strip
>
sessionSchemaZod schema for JWT session payloads (userId, deviceId, optional OAuth fields).
const sessionSchema: z.ZodObject<
{
id: z.ZodOptional<z.ZodString>
userId: z.ZodString
deviceId: z.ZodString
oauthServer: z.ZodOptional<z.ZodString>
oauthId: z.ZodOptional<z.ZodString>
},
z.core.$strip
>
updatePasswordSecretPropsSchemaSchema for updating password secret props (partial password hash).
const updatePasswordSecretPropsSchema: z.ZodObject<
{ passwordHash: z.ZodOptional<z.ZodOptional<z.ZodString>> },
z.core.$strip
>
updatePlanPropsSchemaSchema for updating a user's plan (partial planKey, planExpiresAt, planAutoRenews).
const updatePlanPropsSchema: z.ZodObject<
{
planKey: z.ZodOptional<z.ZodOptional<z.ZodString> | z.ZodOptional<z.ZodEnum<{}>>>
planExpiresAt: z.ZodOptional<z.ZodOptional<z.ZodString>>
planAutoRenews: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>
},
z.core.$strip
>
updatePropsSchemaSchema for updating a user (partial username, name, email, avatar, bio).
const updatePropsSchema: z.ZodObject<
{
username: z.ZodOptional<z.ZodOptional<z.ZodString>>
name: z.ZodOptional<z.ZodOptional<z.ZodString>>
email: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>
avatar: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>
bio: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>
},
z.core.$strip
>
verifyTwoFactorPropsSchemaSchema for verifying two-factor authentication.
const verifyTwoFactorPropsSchema: z.ZodObject<
{ twoFactorEnabled: z.ZodOptional<z.ZodOptional<z.ZodBoolean>> },
z.core.$strip
>
verifyTwoFactorSecretPropsSchemaSchema for two-factor secret props.
const verifyTwoFactorSecretPropsSchema: z.ZodObject<
{
pendingTwoFactorSecret: z.ZodOptional<z.ZodOptional<z.ZodString>>
twoFactorSecret: z.ZodOptional<z.ZodOptional<z.ZodString>>
},
z.core.$strip
>
authorizationMembers:
authorization.getAuthCookieName — function: Resolve the actual cookie name for an auth cookie.authorization.getAuthCookieOptions — function: Base cookie attributes shared by EVERY auth cookie this resource sets andauthorization.invalidateDeviceExistsCache — function: Evict a single device's positive entry from the device-exists cache so theauthorization.invalidateAllDeviceExistsCache — function: Evict ALL positive entries from the device-exists cache.authorization.set — function: Set authorization headers and cookie for a session.authorization.VerifyMiddlewareOptions — interface: Options for {@link verifyMiddleware}.authorization.verifyMiddleware — function: Middleware that verifies the JWT token from the Authorization header and sets res.locals.session.authorizersMembers:
authorizers.auth — function: Middleware that checks if the request has an authenticated session (res.locals.session.userId).authorizers.authSelf — function: Middleware that checks if the authenticated user's ID matches the :id route parameter.authorizers.RateLimitAuthOptions — interface: Configuration for {@link rateLimit}.authorizers.rateLimit — function: Creates an authorizer middleware that brute-force-protects an auth endpoint.authorizers.loginAccountKey — function: Account-identifier extractor for the login endpoint: the submitted usernameauthorizers.emailAccountKey — function: Account-identifier extractor for the forgot-password endpoint: the submittedauthorizers.paramIdAccountKey — function: Account-identifier extractor for the verify-two-factor endpoint: the targetauthorizers.requireWebhookAuthenticity — function: Middleware guarding the public POST /users/payment-notification/:providerhandlersMembers:
handlers.billingPortal — function: Opens the bonded provider's hosted billing portal for the user, so they canhandlers.handlePaymentNotification — function: Generic payment notification handler that works with any bonded PaymentProvider. Reads thehandlers.verifyPayment — function: Generic payment verification handler that works with any bonded PaymentProvider. Reads thehandlers.CreateRequest — interface: Request body for user creation, including password and optional device name.handlers.create — function: Creates a user with username and password. Validates username uniqueness and email format,handlers.del — function: Deletes a user and their associated data. Removes secrets from the secrets table,handlers.ForgotPasswordRequest — interface: Request body for password reset initiation.handlers.forgotPassword — function: Generates a UUID password reset token, stores it in the secrets table, and sends a resethandlers.LogInRequest — interface: Request body for user login, supporting password, reset token, and 2FA flows.handlers.logIn — function: Logs in a user by username or email. Supports password authentication, password reset tokenhandlers.LogInOAuthRequest — interface: Request body for OAuth login, including the OAuth server name, authorization code, and PKCE verifier.handlers.logInOAuth — function: Logs in or creates a user via OAuth. Verifies the authorization code with the bonded OAuthhandlers.logout — function: Logs the current user out. Revokes the session device server-side (so the JWThandlers.oauthAuthorize — function: OAuth initiation — GET /users/oauth/:provider.handlers.read — function: Reads a user by ID from the database. Attaches plan info (with expiration/renewal status) via thehandlers.readSelf — function: Reads the AUTHENTICATED user from the session — no :id param. Backshandlers.ResetPasswordRequest — interface: Request body for confirming a password reset using a token.handlers.resetPassword — function: Confirms a password reset by validating the one-time token previously generated byhandlers.update — function: Updates a user's profile fields (username, name, email). Validates username formathandlers.UpdatePasswordRequest — interface: Request body for password update, with current password verification and new password.handlers.updatePassword — function: Updates a user's password. If the user already has a password hash, the current passwordhandlers.UpdatePlanRequest — interface: Request body for plan update, containing the target plan key.handlers.updatePlan — function: Updates a user's subscription plan. Uses the bonded PlanService to look up plan metadata andhandlers.VerifyTwoFactorRequest — interface: Request body for two-factor authentication operations (setup, enable, or disable).handlers.verifyTwoFactor — function: Handles two-factor authentication lifecycle via @molecule/api-two-factor:typesMembers:
types.CreateOAuthProps — type: Create O Auth Props type.types.CreateProps — type: Create Props type.types.CreateSecretProps — type: Create Secret Props type.types.Props — type: User props type inferred from schema.types.SecretProps — type: Secret Props type.types.Session — type: User session data (userId, email, role, permissions, metadata) inferred from sessionSchema.types.UpdatePasswordSecretProps — type: Update Password Secret Props type.types.UpdatePlanProps — type: Update Plan Props type.types.UpdateProps — type: Update Props type.types.VerifyTwoFactorProps — type: Verify Two Factor Props type.types.VerifyTwoFactorSecretProps — type: Verify Two Factor Secret Props type.types.Resource — type: An object describing the user resource.utilitiesMembers:
utilities.fetchAvatarDataUri — function: Downloads an OAuth provider's profile image and re-hosts it as an inlineutilities.getPlan — function: Get a user's current plan info.utilities.invalidateEntitlementsCache — function: Invalidates the entitlements plan-key cache for a user after their planutilities.invalidateEntitlementsCacheSafe — function: Fire-and-forget variant of {@link invalidateEntitlementsCache} for callutilities.normalizeEmail — function: Normalizes an email address for storage and lookup so case/whitespaceutilities.notify — function: Sends push notifications to all of a user's devices except the current one. Retrieves deviceszMembers:
z.core — namespacez.infer — typez.output — typez.input — typez.JSONType — typez.globalRegistry — constz.GlobalMeta — interfacez.registry — functionz.config — functionz.$output — constz.$input — constz.$brand — constz.clone — functionz.regexes — namespacez.treeifyError — functionz.prettifyError — functionz.formatError — functionz.flattenError — functionz.TimePrecision — constz.util — namespacez.NEVER — const: A special constant with type neverz.toJSONSchema — functionz.fromJSONSchema — function: Converts a JSON Schema to a Zod schema. This function should be considered semi-experimental. It's behavior is liable to change.z.locales — namespacez.ZodISODateTime — interfacez.ZodISODate — interfacez.ZodISOTime — interfacez.ZodISODuration — interfacez.iso — namespacez.ZodCoercedString — interfacez.ZodCoercedNumber — interfacez.ZodCoercedBigInt — interfacez.ZodCoercedBoolean — interfacez.ZodCoercedDate — interfacez.coerce — namespacez.string — functionz.email — functionz.guid — functionz.uuid — functionz.uuidv4 — functionz.uuidv6 — functionz.uuidv7 — functionz.url — functionz.httpUrl — functionz.emoji — functionz.nanoid — functionz.cuid — function: Validates a CUID v1 string.z.cuid2 — functionz.ulid — functionz.xid — functionz.ksuid — functionz.ipv4 — functionz.mac — functionz.ipv6 — functionz.cidrv4 — functionz.cidrv6 — functionz.base64 — functionz.base64url — functionz.e164 — functionz.jwt — functionz.stringFormat — functionz.hostname — functionz.hex — functionz.hash — functionz.number — functionz.int — functionz.float32 — functionz.float64 — functionz.int32 — functionz.uint32 — functionz.boolean — functionz.bigint — functionz.int64 — functionz.uint64 — functionz.symbol — functionz.any — functionz.unknown — functionz.never — functionz.date — functionz.array — functionz.keyof — functionz.object — functionz.strictObject — functionz.looseObject — functionz.union — functionz.xor — function: Creates an exclusive union (XOR) where exactly one option must match.z.discriminatedUnion — functionz.intersection — functionz.tuple — functionz.record — functionz.partialRecord — functionz.looseRecord — functionz.map — functionz.set — functionz.nativeEnum — functionz.literal — functionz.file — functionz.transform — functionz.optional — functionz.exactOptional — functionz.nullable — functionz.nullish — functionz._default — functionz.prefault — functionz.nonoptional — functionz.success — functionz.nan — functionz.pipe — functionz.codec — functionz.invertCodec — functionz.readonly — functionz.templateLiteral — functionz.lazy — functionz.promise — functionz._function — functionz.check — functionz.custom — functionz.refine — functionz.superRefine — functionz.json — functionz.preprocess — functionz.ZodStandardSchemaWithJSON — typez.ZodType — interfacez._ZodType — interfacez._ZodString — interfacez.ZodString — interfacez.ZodStringFormat — interfacez.ZodEmail — interfacez.ZodGUID — interfacez.ZodUUID — interfacez.ZodURL — interfacez.ZodEmoji — interfacez.ZodNanoID — interfacez.ZodCUID — interfacez.ZodCUID2 — interfacez.ZodULID — interfacez.ZodXID — interfacez.ZodKSUID — interfacez.ZodIPv4 — interfacez.ZodMAC — interfacez.ZodIPv6 — interfacez.ZodCIDRv4 — interfacez.ZodCIDRv6 — interfacez.ZodBase64 — interfacez.ZodBase64URL — interfacez.ZodE164 — interfacez.ZodJWT — interfacez.ZodCustomStringFormat — interfacez._ZodNumber — interfacez.ZodNumber — interfacez.ZodNumberFormat — interfacez.ZodInt — interfacez.ZodFloat32 — interfacez.ZodFloat64 — interfacez.ZodInt32 — interfacez.ZodUInt32 — interfacez._ZodBoolean — interfacez.ZodBoolean — interfacez._ZodBigInt — interfacez.ZodBigInt — interfacez.ZodBigIntFormat — interfacez.ZodSymbol — interfacez.ZodUndefined — interfacez.undefined — functionz.ZodNull — interfacez.null — functionz.ZodAny — interfacez.ZodUnknown — interfacez.ZodNever — interfacez.ZodVoid — interfacez.void — functionz._ZodDate — interfacez.ZodDate — interfacez.ZodArray — interfacez.SafeExtendShape — typez.ZodObject — interfacez.ZodUnion — interfacez.ZodXor — interfacez.ZodDiscriminatedUnion — interfacez.ZodIntersection — interfacez.ZodTuple — interfacez.ZodRecord — interfacez.ZodMap — interfacez.ZodSet — interfacez.ZodEnum — interfacez.enum — functionz.ZodLiteral — interfacez.ZodFile — interfacez.ZodTransform — interfacez.ZodOptional — interfacez.ZodExactOptional — interfacez.ZodNullable — interfacez.ZodDefault — interfacez.ZodPrefault — interfacez.ZodNonOptional — interfacez.ZodSuccess — interfacez.ZodCatch — interfacez.catch — functionz.ZodNaN — interfacez.ZodPipe — interfacez.ZodCodec — interfacez.ZodPreprocess — interfacez.ZodReadonly — interfacez.ZodTemplateLiteral — interfacez.ZodLazy — interfacez.ZodPromise — interfacez.ZodFunction — interfacez.function — functionz.ZodCustom — interfacez.describe — constz.meta — constz.instanceof — functionz.stringbool — constz.ZodJSONSchemaInternals — interfacez.ZodJSONSchema — interfacez.lt — functionz.lte — functionz.gt — functionz.gte — functionz.positive — functionz.negative — functionz.nonpositive — functionz.nonnegative — functionz.multipleOf — functionz.maxSize — functionz.minSize — functionz.size — functionz.maxLength — functionz.minLength — functionz.length — functionz.regex — functionz.lowercase — functionz.uppercase — functionz.includes — functionz.startsWith — functionz.endsWith — functionz.property — functionz.mime — functionz.overwrite — functionz.normalize — functionz.trim — functionz.toLowerCase — functionz.toUpperCase — functionz.slugify — functionz.RefinementCtx — interfacez.ZodIssue — typez.ZodError — interface: An Error-like class used to store Zod validation issues.z.ZodRealError — constz.ZodFlattenedError — typez.ZodFormattedError — typez.ZodErrorMap — interfacez.IssueData — typez.ZodSafeParseResult — typez.ZodSafeParseSuccess — typez.ZodSafeParseError — typez.parse — constz.parseAsync — constz.safeParse — constz.safeParseAsync — constz.encode — constz.decode — constz.encodeAsync — constz.decodeAsync — constz.safeEncode — constz.safeDecode — constz.safeEncodeAsync — constz.safeDecodeAsync — constz.setErrorMap — functionz.getErrorMap — functionz.TypeOf — typez.Infer — typez.ZodFirstPartySchemaTypes — typez.ZodIssueCode — constz.inferFlattenedErrors — typez.inferFormattedError — typez.BRAND — type: Use z.$brand insteadz.ZodTypeAny — interfacez.ZodSchema — interfacez.Schema — interfacez.ZodRawShape — type: Included for Zod 3 compatibilityz.ZodFirstPartyTypeKind — enumPeer dependencies:
@molecule/api-bond ^1.0.1@molecule/api-config ^1.0.1@molecule/api-database ^1.0.1@molecule/api-entitlements ^1.0.1@molecule/api-i18n ^1.0.1@molecule/api-jwt ^1.0.1@molecule/api-locales-user ^1.0.1@molecule/api-locales-user-payments ^1.0.1@molecule/api-password ^1.0.1@molecule/api-payments ^1.1.0@molecule/api-push-notifications ^1.0.1@molecule/api-rate-limit ^1.0.1@molecule/api-resource ^1.0.1@molecule/api-resource-device ^1.0.1@molecule/api-secrets ^1.0.1@molecule/api-two-factor ^1.0.1JWT_PRIVATE_KEY (required) — JWT signing key (RSA private)
JWT_PUBLIC_KEY (required) — JWT verification key (RSA public)
@molecule/api-bond@molecule/api-config@molecule/api-database@molecule/api-entitlements@molecule/api-i18n@molecule/api-jwt@molecule/api-locales-user@molecule/api-locales-user-payments@molecule/api-password@molecule/api-payments@molecule/api-push-notifications@molecule/api-rate-limit@molecule/api-resource@molecule/api-resource-device@molecule/api-secrets@molecule/api-two-factorzodThe user record is split across TWO schemas — pick the right one or you leak credentials:
propsSchema) — SAFE, client-facing fields (username, name, email,
emailVerified, twoFactorEnabled, plan). This is what handlers return and what lives
in the users table.secretPropsSchema) — SERVER-ONLY secrets: passwordHash, the
TOTP twoFactorSecret (and its pending-setup value). Stored in a SEPARATE secrets table
and NEVER serialized to the client. Note the pair twoFactorEnabled (safe boolean, in
Props) vs twoFactorSecret (secret, in SecretProps).When you extend the user, put a secret (token, hash, key, provider refresh token) in
SecretProps; put a display field in Props. Never add a secret to Props, never
return a secrets-table value in a response or log, and never res.json(userRow) a raw DB
row — return Props.
Auth is ALREADY wired globally (the router's verifyMiddleware → res.locals.session),
so a handler reads the current user with getUserId(res) and does NOT add per-route auth
middleware (see the auth skill). Scope every custom user query by the authenticated id.
On the CLIENT, the bearer token is held IN MEMORY only — a localStorage copy is
XSS-exfiltratable and is forbidden. The session is restored after a reload via the
httpOnly cookie + GET /users/me; don't persist the token yourself.
Client-facing endpoints (mounted under the app's /api prefix → /api/users/...).
The auth CLIENT (useAuth() → login / register / logout / refresh) already wraps
login / signup / logout — do NOT hand-roll those against the raw routes. The rest have NO
client method; call them with raw http.*. Use these EXACT paths — a weak model guesses
/api/auth/* or /api/user (singular), and neither exists:
POST /api/users/forgot-password — request a reset email (body { email })POST /api/users/reset-password — confirm with the emailed token (body { token, password })PATCH /api/users/:id — update profile fields (name, username, email, bio); NOT PUT /api/userPATCH /api/users/:id/password — change password · DELETE /api/users/:id — delete accountPATCH /api/users/:id/plan — update the subscription plan (a paid plan with no
existing subscription answers 201 { checkoutUrl }; send the browser there)POST /api/users/:id/verify-payment/:provider — confirm a purchase server-side
(body/query subscriptionId); the provider returns the buyer to the APP's
/plan-updated?provider=…&sessionId=…, and THAT page calls this from the app
origin so the session cookie applies. A hosted checkout must never redirect
straight to this route on a separate API host: the top-level navigation carries
no credentials, so it answers 401 and the paid plan is never granted.POST /api/users/:id/billing-portal/:provider — open the provider's hosted
billing portal (update card, invoices, cancel); responds { url }, optional body
{ returnPath } to come back to a specific app page. 404 user.payment.noBillingAccount
when the user has no customer record yetGET /api/users/me — the current user (session restore) · GET /api/users/:id — read one
The full, authoritative route list is the routes export (see routes.ts).Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
read_activity tool (filter type 'email') and follow the link/code
in its payload; never mock the flow or modify production code to expose it.GET /users/me — never from a token persisted in localStorage).