Skip to main content

Configuration

All auth configuration is read from environment variables and bindings — no constructor options or config files.

What Must/Might Be in env

Required Config

Only one variable is needed to get started:

VariableRead ByDescription
LUMENIZE_AUTH_REDIRECTAuth DO302 Location after magic link / invite — relative (/app) or absolute (https://example.com/app)

Optional Config

The Auth DO and Worker hooks read these from environment variables. Sensible defaults mean you can skip all of them initially.

VariableDefaultRead ByDescription
LUMENIZE_AUTH_ISSUER'https://lumenize.local'Auth DO, hooksJWT iss claim
LUMENIZE_AUTH_AUDIENCE'https://lumenize.local'Auth DO, hooksJWT aud claim
LUMENIZE_AUTH_ACCESS_TOKEN_TTL900 (15 min)Auth DOAccess token expiration in seconds
LUMENIZE_AUTH_REFRESH_TOKEN_TTL2592000 (30 days)Auth DORefresh token expiration in seconds
LUMENIZE_AUTH_MAGIC_LINK_TTL1800 (30 min)Auth DOOne-time login token expiration in seconds
LUMENIZE_AUTH_INVITE_TTL604800 (7 days)Auth DOInvite token expiration in seconds (reusable until expiry)
LUMENIZE_AUTH_PREFIX'/auth'Auth DO, createAuthRoutesURL prefix for all auth endpoints. createAuthRoutes uses it for route matching; the Auth DO uses it to compose links in emails
LUMENIZE_AUTH_BOOTSTRAP_EMAILAuth DOEmail idempotently promoted to admin on every login. Without this, no bootstrap admin is created — you'll need another mechanism to create the first admin
LUMENIZE_AUTH_TEST_MODE"false"Auth DOSet to "true" to enable test modenever deploy to production

Secrets and Keys

VariableTypeRequiredRead ByDescription
JWT_PRIVATE_KEY_BLUESecretYes¹Auth DOEd25519 private key (BLUE slot)
JWT_PUBLIC_KEY_BLUESecretYes¹Auth DO, hooksEd25519 public key (BLUE slot)
JWT_PRIVATE_KEY_GREENSecretNo²Auth DOEd25519 private key (GREEN slot)
JWT_PUBLIC_KEY_GREENSecretNo²Auth DO, hooksEd25519 public key (GREEN slot)
PRIMARY_JWT_KEYVariableYesAuth DOActive signing slot: "BLUE" or "GREEN"
TURNSTILE_SECRET_KEYSecretRecommendedcreateAuthRoutesCloudflare Turnstile server-side secret. Without it, the magic-link endpoint has no bot protection. See Get Started with Turnstile
RESEND_API_KEYSecretYes³ResendEmailSenderResend API key for email delivery. See Email Provider

¹ At least one key pair (BLUE or GREEN) is required. ² Second slot needed only during key rotation. ³ Only when using ResendEmailSender (the default). Bring-your-own-provider implementations use their own credentials.

Naming convention: LUMENIZE_AUTH_* for Lumenize-specific config, JWT_* for industry-standard key material (shared across packages), vendor prefix (e.g., TURNSTILE_*) for third-party services.

Bindings

These are Lumenize conventions — define them in your wrangler.jsonc with these exact names.

BindingTypeRequiredRead ByDescription
LUMENIZE_AUTHDurable Object namespaceYescreateAuthRoutesNamespace binding for the LumenizeAuth DO
AUTH_EMAIL_SENDERService bindingRecommendedAuth DOSelf-referencing service binding pointing to your AuthEmailSender entrypoint. Without it, emails are not delivered (debug-logged instead). See Email Provider
LUMENIZE_AUTH_RATE_LIMITERRate Limiting bindingRecommendedcreateRouteDORequestAuthHooksPer-subject rate limiting keyed on sub from the decoded JWT. Without it, authenticated routes have no per-user rate limiting

Email Provider

LumenizeAuth requires outgoing email for magic links, admin notifications, approval confirmations, and invites. Email delivery is delegated to a WorkerEntrypoint that you define and export from your Worker. The Auth DO calls it via the AUTH_EMAIL_SENDER service binding.

Class Hierarchy

WorkerEntrypoint                   (Cloudflare)
└─ AuthEmailSenderBase (@lumenize/auth — templates, subjects, from/replyTo/appName)
└─ ResendEmailSender (@lumenize/auth — sendEmail() via Resend API)
└─ AuthEmailSender (you — sets from, optional overrides)
ClassPackageWhat you do
AuthEmailSenderBase@lumenize/authAbstract base. Provides send(message) dispatch, 5 default HTML templates, 5 default subject methods, from (abstract), replyTo, appName. You extend this for bring-your-own-provider.
ResendEmailSender@lumenize/authExtends AuthEmailSenderBase. Implements sendEmail() via fetch to https://api.resend.com/emails using RESEND_API_KEY. You extend this for the default Resend path.
AuthEmailSenderYour WorkerYour class. Set from, optionally override replyTo, appName, template methods, or subject methods. Export it from your Worker entry point.

Overridable Methods

All methods receive the corresponding EmailMessage variant as their argument.

Template methods (return HTML string):

MethodDefault behavior
magicLinkHtml(message)Login link with sign-in button
adminNotificationHtml(message)New signup notification with approve button
approvalConfirmationHtml(message)Account approved with link to app
inviteExistingHtml(message)Notification to already-verified user
inviteNewHtml(message)Onboarding link for new user

Subject methods (return string):

MethodDefault
magicLinkSubject(message)'Your login link'
adminNotificationSubject(message)`New signup: ${message.subjectEmail}`
approvalConfirmationSubject(message)'Your account has been approved'
inviteExistingSubject(message)"You've been invited"
inviteNewSubject(message)"You've been invited"

Instance Variables

VariableRequiredDefaultDescription
fromYesBare sender email address (e.g., 'auth@myapp.com'). ResendEmailSender constructs the display name as "${appName} <${from}>" for the Resend API.
replyToNono-reply@{domain}Reply-to address. Domain is extracted from from.
appNameNo'Lumenize'Used in default templates and the Resend From: display name.

ResolvedEmail

The object passed to sendEmail() after template and subject resolution:

interface ResolvedEmail {
to: string; // recipient
subject: string; // resolved by subject method
html: string; // resolved by template method
from: string; // bare email address from instance variable
replyTo: string; // resolved default or override
appName: string; // for providers that use it (e.g., From: display name)
}

For setup instructions, see Getting Started: Email Provider.