From 6216923f5eb42247db152c86944f7a84d7d497ee Mon Sep 17 00:00:00 2001 From: Brandon Corbett Date: Tue, 20 Oct 2026 21:31:50 -0400 Subject: [PATCH 1/3] docs: add an identity cost calculator Adds compare/cost, a calculator that shows Auth0 and Okta Customer Identity list prices for a MAU count, B2B or B2C, enterprise connections, and MFA, next to the flat cost of self-hosting Seamless Auth on DigitalOcean. Prices live in one file with a source URL and checked date for each. Tiers without a published price show contact sales, and the page degrades to static price tables without JavaScript. Links the calculator from the Auth0 and Keycloak comparisons, and corrects the Auth0 enterprise connection add-on to the starting price the page shows. Closes #110 --- .../cost-calculator/CostCalculator.astro | 278 ++++++++++++++ src/components/cost-calculator/pricing.ts | 360 ++++++++++++++++++ src/content/docs/compare/auth0.mdx | 12 +- src/content/docs/compare/cost.mdx | 139 +++++++ src/content/docs/compare/keycloak.mdx | 4 + 5 files changed, 788 insertions(+), 5 deletions(-) create mode 100644 src/components/cost-calculator/CostCalculator.astro create mode 100644 src/components/cost-calculator/pricing.ts create mode 100644 src/content/docs/compare/cost.mdx diff --git a/src/components/cost-calculator/CostCalculator.astro b/src/components/cost-calculator/CostCalculator.astro new file mode 100644 index 0000000..a8a36da --- /dev/null +++ b/src/components/cost-calculator/CostCalculator.astro @@ -0,0 +1,278 @@ +--- +// The static tables are the no-JavaScript version of the calculator, so they print every +// published price in pricing.ts and nothing the script could not also show. +import { + type Auth0Plan, + auth0MauSteps, + auth0Plans, + okta, + selfHost, + selfHostMonthlyUsd, + sources, + usd, +} from './pricing' + +const plansFor = (useCase: 'b2c' | 'b2b') => auth0Plans.filter((plan) => plan.useCase === useCase) + +type Cell = { text: string; salesUrl?: string } + +const cell = (plan: Auth0Plan, mau: number): Cell => { + const tier = plan.tiers.find((t) => mau <= t.upToMau) + if (tier) return { text: usd(tier.monthlyUsd) } + if (plan.aboveTiers === 'unavailable') return { text: 'Not on plan' } + return { text: 'Contact sales', salesUrl: sources.auth0Sales.url } +} + +const steps = [ + ...auth0MauSteps.map((mau) => ({ label: mau.toLocaleString('en-US'), mau })), + { label: 'Above 30,000', mau: Number.POSITIVE_INFINITY }, +] + +const selfHostSummary = `${usd(selfHostMonthlyUsd)} per month at any MAU (${selfHost.provider} ${usd( + selfHost.app.monthlyUsd, +)} Droplet plus ${usd(selfHost.database.monthlyUsd)} managed PostgreSQL, checked ${ + selfHost.app.source.checked +})` +--- + +
+ + + + +
+

+ Auth0 list prices per month, monthly billing, from auth0.com/pricing (checked {sources.auth0.checked}). A MAU count between two steps uses the next step up. +

+ { + (['b2c', 'b2b'] as const).map((useCase) => ( + + + + + + {plansFor(useCase).map((plan) => ( + + ))} + + + + + {steps.map(({ label, mau }) => ( + + + {plansFor(useCase).map((plan) => { + const { text, salesUrl } = cell(plan, mau) + return + })} + + + ))} + +
Auth0 {useCase === 'b2c' ? 'B2C' : 'B2B'} plans
MAU up to{plan.name}Enterprise
{label}{salesUrl ? {text} : text} + Contact sales +
+ )) + } +

+ Okta Customer Identity, from okta.com/pricing (checked { + sources.okta.checked + }): the Enterprise base platform starts at {usd(okta.basePlatformFromUsd)} per month, { + okta.billing + }, with usage-priced add-ons quoted by sales. The B2C Suite and B2B Suite are contact sales. +

+

Seamless Auth self-hosted: {selfHostSummary}.

+
+
+ + + + diff --git a/src/components/cost-calculator/pricing.ts b/src/components/cost-calculator/pricing.ts new file mode 100644 index 0000000..dc68e89 --- /dev/null +++ b/src/components/cost-calculator/pricing.ts @@ -0,0 +1,360 @@ +// Every price on the Identity Cost Calculator page comes from this file. Each value was read +// from the vendor's own page on the `checked` date of its source. Re-check them all on the day +// the page is republished, and never add a price the source does not print. + +export type UseCase = 'b2c' | 'b2b' +export type Mfa = 'none' | 'otp' | 'enterprise' + +export interface Source { + label: string + url: string + checked: string +} + +export const sources = { + auth0: { + label: 'Auth0 pricing', + url: 'https://auth0.com/pricing', + checked: '2026-10-20', + }, + auth0Sales: { + label: 'Auth0 sales', + url: 'https://auth0.com/contact-us', + checked: '2026-10-20', + }, + okta: { + label: 'Okta plans and pricing, Customer Identity tab', + url: 'https://www.okta.com/pricing/', + checked: '2026-10-20', + }, + oktaSales: { + label: 'Okta sales', + url: 'https://www.okta.com/contact-sales/', + checked: '2026-10-20', + }, + doDroplets: { + label: 'DigitalOcean Droplet pricing', + url: 'https://www.digitalocean.com/pricing/droplets', + checked: '2026-10-20', + }, + doDatabases: { + label: 'DigitalOcean Managed Databases pricing', + url: 'https://www.digitalocean.com/pricing/managed-databases', + checked: '2026-10-20', + }, + doPostgresDocs: { + label: 'DigitalOcean PostgreSQL pricing details', + url: 'https://docs.digitalocean.com/products/databases/postgresql/details/pricing/', + checked: '2026-10-20', + }, +} satisfies Record + +export interface Tier { + upToMau: number + monthlyUsd: number +} + +export type Availability = 'included' | 'add-on' | 'not-included' | 'not-listed' + +export interface Auth0Plan { + id: string + name: string + useCase: UseCase + // Monthly billing. A MAU above the last tier is "Get in touch" on the pricing page. + tiers: Tier[] + // Free has no sales path, only a MAU ceiling. + aboveTiers: 'contact' | 'unavailable' + enterpriseConnections: { included: number; addOnFromUsd: number | null } + mfaOtp: Availability + mfaEnterprise: Availability + mfaEnterpriseAddOnFromUsd: number | null +} + +const free = (useCase: UseCase): Auth0Plan => ({ + id: `${useCase}-free`, + name: 'Free', + useCase, + tiers: [{ upToMau: 25000, monthlyUsd: 0 }], + aboveTiers: 'unavailable', + enterpriseConnections: { included: 1, addOnFromUsd: null }, + mfaOtp: 'not-included', + mfaEnterprise: 'not-listed', + mfaEnterpriseAddOnFromUsd: null, +}) + +export const auth0Plans: Auth0Plan[] = [ + free('b2c'), + { + id: 'b2c-essentials', + name: 'Essentials', + useCase: 'b2c', + tiers: [ + { upToMau: 500, monthlyUsd: 35 }, + { upToMau: 1000, monthlyUsd: 70 }, + { upToMau: 2500, monthlyUsd: 175 }, + { upToMau: 5000, monthlyUsd: 350 }, + { upToMau: 7500, monthlyUsd: 525 }, + { upToMau: 10000, monthlyUsd: 700 }, + { upToMau: 20000, monthlyUsd: 1400 }, + { upToMau: 30000, monthlyUsd: 2100 }, + ], + aboveTiers: 'contact', + enterpriseConnections: { included: 0, addOnFromUsd: null }, + mfaOtp: 'included', + mfaEnterprise: 'not-listed', + mfaEnterpriseAddOnFromUsd: null, + }, + { + id: 'b2c-professional', + name: 'Professional', + useCase: 'b2c', + tiers: [ + { upToMau: 500, monthlyUsd: 240 }, + { upToMau: 1000, monthlyUsd: 240 }, + { upToMau: 2500, monthlyUsd: 545 }, + { upToMau: 5000, monthlyUsd: 1000 }, + { upToMau: 7500, monthlyUsd: 1200 }, + { upToMau: 10000, monthlyUsd: 1600 }, + { upToMau: 20000, monthlyUsd: 3200 }, + ], + aboveTiers: 'contact', + enterpriseConnections: { included: 0, addOnFromUsd: null }, + mfaOtp: 'included', + mfaEnterprise: 'included', + mfaEnterpriseAddOnFromUsd: null, + }, + free('b2b'), + { + id: 'b2b-essentials', + name: 'Essentials', + useCase: 'b2b', + tiers: [ + { upToMau: 500, monthlyUsd: 150 }, + { upToMau: 1000, monthlyUsd: 300 }, + { upToMau: 2500, monthlyUsd: 700 }, + { upToMau: 5000, monthlyUsd: 1300 }, + { upToMau: 7500, monthlyUsd: 1725 }, + { upToMau: 10000, monthlyUsd: 2100 }, + { upToMau: 20000, monthlyUsd: 3800 }, + ], + aboveTiers: 'contact', + enterpriseConnections: { included: 3, addOnFromUsd: 100 }, + mfaOtp: 'included', + mfaEnterprise: 'add-on', + mfaEnterpriseAddOnFromUsd: 100, + }, + { + id: 'b2b-professional', + name: 'Professional', + useCase: 'b2b', + tiers: [ + { upToMau: 500, monthlyUsd: 800 }, + { upToMau: 1000, monthlyUsd: 800 }, + { upToMau: 2500, monthlyUsd: 1200 }, + { upToMau: 5000, monthlyUsd: 1500 }, + { upToMau: 7500, monthlyUsd: 1800 }, + { upToMau: 10000, monthlyUsd: 2400 }, + ], + aboveTiers: 'contact', + enterpriseConnections: { included: 5, addOnFromUsd: 100 }, + mfaOtp: 'included', + mfaEnterprise: 'included', + mfaEnterpriseAddOnFromUsd: null, + }, +] + +// The MAU steps on Auth0's pricing slider, used for the static table. +export const auth0MauSteps = [500, 1000, 2500, 5000, 7500, 10000, 20000, 30000] + +export const okta = { + basePlatformFromUsd: 3000, + billing: 'billed annually', +} + +export const selfHost = { + provider: 'DigitalOcean', + region: 'any DigitalOcean region (the pricing pages list one price, not a price per region)', + app: { + name: 'Basic Droplet, Regular CPU, 2 GiB memory, 1 vCPU, 50 GiB SSD, 2,000 GiB transfer', + monthlyUsd: 12, + source: sources.doDroplets, + }, + database: { + name: 'Managed PostgreSQL, Standard Edition, Basic node, 1 GiB memory, 1 vCPU, single node, 10 GiB storage', + monthlyUsd: 15.15, + source: sources.doDatabases, + }, + extraStoragePerGibUsd: 0.215, +} + +export const selfHostMonthlyUsd = selfHost.app.monthlyUsd + selfHost.database.monthlyUsd + +export interface Inputs { + mau: number + useCase: UseCase + enterpriseConnections: number + mfa: Mfa +} + +export type Quote = + | { kind: 'price'; usd: number; notes: string[] } + | { kind: 'from'; usd: number; notes: string[] } + | { kind: 'contact'; notes: string[] } + | { kind: 'unavailable'; notes: string[] } + +export interface Row { + vendor: 'Auth0' | 'Okta Customer Identity' | 'Seamless Auth' + plan: string + quote: Quote + salesUrl?: string +} + +export function usd(value: number): string { + return value.toLocaleString('en-US', { + style: 'currency', + currency: 'USD', + minimumFractionDigits: Number.isInteger(value) ? 0 : 2, + maximumFractionDigits: 2, + }) +} + +export function auth0Quote(plan: Auth0Plan, inputs: Inputs): Quote { + const tier = plan.tiers.find((t) => inputs.mau <= t.upToMau) + if (!tier) { + const top = plan.tiers[plan.tiers.length - 1].upToMau.toLocaleString('en-US') + if (plan.aboveTiers === 'unavailable') { + return { kind: 'unavailable', notes: [`${plan.name} covers up to ${top} MAU.`] } + } + return { + kind: 'contact', + notes: [`The pricing page prints no ${plan.name} price above ${top} MAU.`], + } + } + + const notes: string[] = [] + let extra = 0 + let startingPrice = false + + const { included, addOnFromUsd } = plan.enterpriseConnections + if (inputs.enterpriseConnections > included) { + if (addOnFromUsd === null) { + return { + kind: 'unavailable', + notes: [ + included === 0 + ? `The pricing page lists no enterprise connections on ${useCaseName(plan.useCase)} ${plan.name}.` + : `${plan.name} includes ${included} enterprise connection and lists no add-on for more.`, + ], + } + } + const added = inputs.enterpriseConnections - included + extra += added * addOnFromUsd + startingPrice = true + notes.push( + `${included} enterprise connections included; ${added} more at a starting price of ${usd(addOnFromUsd)} a month each.`, + ) + } else if (inputs.enterpriseConnections > 0) { + notes.push(`Includes ${included} enterprise connection${included === 1 ? '' : 's'}.`) + } + + if (inputs.mfa === 'otp' && plan.mfaOtp !== 'included') { + return { + kind: 'unavailable', + notes: [`The pricing page lists Pro MFA (OTP apps, Duo) as not included on ${plan.name}.`], + } + } + if (inputs.mfa === 'enterprise') { + if (plan.mfaEnterprise === 'add-on' && plan.mfaEnterpriseAddOnFromUsd !== null) { + extra += plan.mfaEnterpriseAddOnFromUsd + startingPrice = true + notes.push( + `Enterprise MFA add-on at a starting price of ${usd(plan.mfaEnterpriseAddOnFromUsd)} a month; SMS is charged separately by carriers.`, + ) + } else if (plan.mfaEnterprise !== 'included') { + return { + kind: 'unavailable', + notes: [ + `The pricing page does not list Enterprise MFA (SMS, voice, email, WebAuthn, push) on ${useCaseName(plan.useCase)} ${plan.name}.`, + ], + } + } + } + + const upTo = tier.upToMau.toLocaleString('en-US') + notes.unshift(`Tier: up to ${upTo} MAU, monthly billing.`) + const total = tier.monthlyUsd + extra + return startingPrice ? { kind: 'from', usd: total, notes } : { kind: 'price', usd: total, notes } +} + +export function oktaRows(inputs: Inputs): Row[] { + const addOns: string[] = [] + if (inputs.mfa !== 'none') addOns.push('MFA') + if (inputs.enterpriseConnections > 0) addOns.push('Inbound Federation') + const base: Row = { + vendor: 'Okta Customer Identity', + plan: 'Enterprise base platform', + salesUrl: sources.oktaSales.url, + quote: { + kind: 'from', + usd: okta.basePlatformFromUsd, + notes: [ + `Starting price, ${okta.billing}. Add-ons are priced on usage such as MAU and quoted by sales; the page publishes no price per MAU.`, + ...(addOns.length + ? [ + `${addOns.join(' and ')}: add-on${addOns.length > 1 ? 's' : ''} with no published price.`, + ] + : []), + ], + }, + } + const suite: Row = { + vendor: 'Okta Customer Identity', + plan: inputs.useCase === 'b2c' ? 'B2C Suite' : 'B2B Suite', + salesUrl: sources.oktaSales.url, + quote: { kind: 'contact', notes: ['Inquire for pricing.'] }, + } + return [base, suite] +} + +export function seamlessRow(inputs: Inputs): Row { + const notes = [ + `${selfHost.provider} ${usd(selfHost.app.monthlyUsd)} Droplet plus ${usd(selfHost.database.monthlyUsd)} managed PostgreSQL. No per-user fee, so the cost does not change with MAU.`, + ] + if (inputs.enterpriseConnections > 0) { + notes.push( + 'Seamless Auth has no SAML or per-customer enterprise connections; OAuth and OIDC providers are configured instance-wide.', + ) + } + if (inputs.mfa === 'enterprise') { + notes.push( + 'Second factors are TOTP and passkey (WebAuthn) step-up; there is no push or SMS second factor.', + ) + } + return { + vendor: 'Seamless Auth', + plan: 'Self-hosted', + quote: { kind: 'price', usd: selfHostMonthlyUsd, notes }, + } +} + +export function calculate(inputs: Inputs): Row[] { + const auth0Rows: Row[] = auth0Plans + .filter((plan) => plan.useCase === inputs.useCase) + .map((plan) => ({ + vendor: 'Auth0', + plan: plan.name, + quote: auth0Quote(plan, inputs), + salesUrl: sources.auth0Sales.url, + })) + auth0Rows.push({ + vendor: 'Auth0', + plan: 'Enterprise', + quote: { kind: 'contact', notes: ['Priced by Auth0 sales.'] }, + salesUrl: sources.auth0Sales.url, + }) + return [...auth0Rows, ...oktaRows(inputs), seamlessRow(inputs)] +} + +function useCaseName(useCase: UseCase): string { + return useCase === 'b2c' ? 'B2C' : 'B2B' +} diff --git a/src/content/docs/compare/auth0.mdx b/src/content/docs/compare/auth0.mdx index c4dcfcc..d1b5924 100644 --- a/src/content/docs/compare/auth0.mdx +++ b/src/content/docs/compare/auth0.mdx @@ -65,7 +65,8 @@ on [auth0.com/pricing](https://auth0.com/pricing) before you decide. plan. - **You prefer not to pay per active user or per enterprise connection.** Self-hosting has no per-user fee; you pay for the infrastructure you run. Auth0 plans scale with monthly active users, and its - pricing page lists a charge for each enterprise SSO connection above a plan's allowance. + pricing page lists a charge for each enterprise SSO connection above a plan's allowance. The + [Identity Cost Calculator](/compare/cost) puts both side by side for your numbers. - **You want your user data in a database you can query.** Self-hosted, users, memberships, and auth events are rows in a Postgres database you run, and the auth event trail exports as a hash-chained file you can verify. Auth0's bulk user export is not on its Free plan, and a password @@ -110,9 +111,10 @@ page. As checked on 2026-10-20, Auth0's pricing page lists a Free plan up to 25,000 monthly active users, Essentials from $35 per month (B2C) or $150 per month (B2B), Professional from $240 per month (B2C) or $800 per month (B2B), and Enterprise on request. Those starting prices are for 500 - MAU, and the price rises with the MAU tier. Extra enterprise SSO connections on B2B plans are listed at $100 per - month each, up to 30 in total. Check [auth0.com/pricing](https://auth0.com/pricing) for current - figures. + MAU, and the price rises with the MAU tier. Extra enterprise SSO connections on B2B plans are + listed at a starting price of $100 per month each. Check [auth0.com/pricing](https://auth0.com/pricing) + for current figures, or use the [Identity Cost Calculator](/compare/cost) to see the published + price at your MAU next to the cost of self-hosting Seamless Auth. --- @@ -203,7 +205,7 @@ maintains Seamless Auth, offers hands-on migration help (see the end of that gui Auth0 and Okta details, each checked on the date shown: -- [Auth0 pricing](https://auth0.com/pricing), checked 2026-10-20: plans and starting prices, B2C and B2B split, MAU limits, passkeys on all plans, MFA tiers, Organizations, enterprise connection allowances and add-on price, SCIM, Actions counts, SLA +- [Auth0 pricing](https://auth0.com/pricing), checked 2026-10-20: plans and starting prices, B2C and B2B split, MAU limits, passkeys on all plans, MFA tiers, Organizations, enterprise connection allowances and add-on starting price, SCIM, Actions counts, SLA - [Passkeys](https://auth0.com/docs/authenticate/database-connections/passkeys), checked 2026-10-20: database connections, 20 passkeys per user, existing credentials not replaced - [Passwordless connections](https://auth0.com/docs/authenticate/passwordless), checked 2026-10-20: email and SMS codes, magic links in Classic Login only - [Multi-factor authentication factors](https://auth0.com/docs/secure/multi-factor-authentication/multi-factor-authentication-factors), checked 2026-10-20: factor list, factors depend on plan diff --git a/src/content/docs/compare/cost.mdx b/src/content/docs/compare/cost.mdx new file mode 100644 index 0000000..8fbb769 --- /dev/null +++ b/src/content/docs/compare/cost.mdx @@ -0,0 +1,139 @@ +--- +title: "Identity Cost Calculator" +description: "Compare the published list prices of Auth0 and Okta Customer Identity plans with the cost of self-hosting Seamless Auth on one container and managed Postgres, by monthly active users, B2B or B2C, enterprise connections, and MFA. List prices only, each dated and sourced." +sidebar: + order: 20 +--- + +import { Aside } from '@astrojs/starlight/components'; +import CostCalculator from '../../../components/cost-calculator/CostCalculator.astro'; +import { selfHost, selfHostMonthlyUsd, usd } from '../../../components/cost-calculator/pricing'; + +The bill is often the first reason a team looks past its identity provider. This page puts a number +on it: enter your monthly active users (MAU), whether you sell to consumers (B2C) or businesses +(B2B), how many enterprise connections you need, and what kind of MFA, and it shows the published +list price of each Auth0 and Okta Customer Identity plan next to the cost of running Seamless Auth +yourself. + +These are **list prices only**, read from each vendor's own pricing page on the date given in +[Sources](#sources). They leave out discounts, annual commitments, negotiated contracts, taxes, +and usage charges a page does not print. Where a vendor publishes no price, the calculator says +"Contact sales" and links to the vendor. It never estimates one. + +--- + +## Calculator + + + + + +--- + +## How The Numbers Are Read + +**Auth0.** Auth0's pricing page has a MAU slider with steps at 500, 1,000, 2,500, 5,000, 7,500, +10,000, 20,000, and 30,000+, and separate B2C and B2B prices. The calculator uses the price the +page shows for each plan at each step, on monthly billing, and rounds your MAU up to the next step +(so 6,000 MAU uses the "up to 7,500" price). It does not interpolate between steps. Where a plan +shows "Get in touch" at a step, or your MAU is above 30,000, the calculator shows "Contact sales". +Free covers up to 25,000 MAU and has no paid path above that. Enterprise has no published price. + +**Enterprise connections.** Auth0's Free plan includes 1 enterprise connection (plus unlimited +Okta enterprise connections), B2B Essentials includes 3, and B2B Professional includes 5. The B2C plans +list none on Essentials or Professional; the pricing page tells B2C customers to move to a B2B +plan for enterprise connections. On the B2B plans, extra connections are an add-on that the page +lists at "starting at $100 monthly per active connection", so any result that uses it is shown as +"From" the starting price. The page publishes no add-on for extra connections on Free. + +**MFA.** The pricing page lists Pro MFA (one-time password apps and Duo) on every paid plan but not +on Free, and Enterprise MFA (SMS, voice, email, WebAuthn, and push through Auth0 Guardian) as +included on Professional, an add-on on B2B Essentials "starting at $100 monthly", and not listed on +B2C Essentials or Free. The page notes that SMS for MFA is charged separately by carriers. Where a +plan does not list the MFA you pick, the calculator says so rather than guessing a price. + +**Okta Customer Identity.** Okta's own pricing page has a Customer Identity tab, separate from the +Auth0 pricing page it also links to. It lists an Enterprise base platform starting at $3,000 per +month, billed annually, with add-ons (including MFA and Inbound Federation) priced on usage such as +MAU and quoted by sales, and a B2C Suite and a B2B Suite with no published price. The calculator +shows the base platform as "From $3,000" whatever your inputs, because the page does not say how +MAU, MFA, or federation change it. See also +[Okta Customer Identity](/compare/auth0#okta-customer-identity) on the Auth0 comparison page. + +--- + +## The Seamless Auth Number + +Self-hosted Seamless Auth has no per-user, per-connection, or per-factor fee. You pay for the +infrastructure, so the cost is flat as MAU grows until you choose to add capacity. The calculator +uses one typical setup, priced from DigitalOcean's own pricing pages on 2026-10-20: + +| | Basis | List price | +| --- | --- | --- | +| Provider | DigitalOcean; the pricing pages list one price, not a price per region | | +| Auth API container | Basic Droplet, Regular CPU: 2 GiB memory, 1 vCPU, 50 GiB SSD, 2,000 GiB transfer | {usd(selfHost.app.monthlyUsd)} per month | +| Managed Postgres | Managed PostgreSQL, Standard Edition, Basic node: 1 GiB memory, 1 vCPU, single node, 10 GiB storage | {usd(selfHost.database.monthlyUsd)} per month | +| **Total** | | **{usd(selfHostMonthlyUsd)} per month** | + +DigitalOcean's PostgreSQL pricing details page says single node clusters are not highly available +and recommends them for development and testing, and that high availability clusters begin at +$30.00 per month for the primary node plus at least one matching standby node at $30.00 per month. +Extra storage on a Standard Edition cluster costs $0.215 per GiB per month. That page gives +round starting prices (single node clusters from $15.00 per month); the calculator uses the +$15.15 that the Managed Databases pricing page lists for the plan. + +This is a starting point, not a measured capacity. Seamless Auth has not published a benchmark of +how many users or sign-ins a given size handles. The one measurement on record is an idle API +container using about 73 MiB of memory with an empty database (see the +[Keycloak comparison's Footprint section](/compare/keycloak#footprint)). Size your deployment by +testing it with your own traffic. + +The Seamless Auth number does **not** include: + +- your time to deploy, upgrade, monitor, and secure it, which is the main cost of self-hosting +- backups beyond what your database provider includes, and their storage +- monitoring, logging, and alerting +- email and SMS delivery, which your providers (for example Twilio or AWS SNS) bill you for +- a load balancer, a second app instance, or a high availability database +- domain names and TLS certificates, if you pay for them + +Seamless Auth also does less than Auth0 and Okta. It has no SAML, no per-customer enterprise +connections, no SCIM, and no push or SMS second factor, so if your inputs need those, a lower price +does not mean the same product. The calculator notes this against the Seamless Auth row. See +[Seamless Auth vs Auth0](/compare/auth0) for the full comparison. + +The managed Seamless Auth service is priced in the portal at +[dashboard.seamlessauth.com](https://dashboard.seamlessauth.com); this page does not quote it. + +--- + +## Sources + +Each page was checked on the date shown. All prices are in US dollars. + +- [Auth0 pricing](https://auth0.com/pricing), checked 2026-10-20: Free up to 25,000 MAU; monthly + B2C and B2B prices at each MAU step; "Get in touch" steps; enterprise connection allowances and + the "starting at $100 monthly per active connection" add-on; Pro MFA and Enterprise MFA by plan, + the B2B Essentials Enterprise MFA add-on "starting at $100 monthly", and SMS charged separately; + Enterprise by contact +- [Auth0 contact sales](https://auth0.com/contact-us), checked 2026-10-20 +- [Okta plans and pricing](https://www.okta.com/pricing/), Customer Identity tab, checked + 2026-10-20: Enterprise base platform starting at $3,000 per month billed annually, B2C and B2B + Suites by inquiry, add-ons priced on usage, annual contract required +- [Okta contact sales](https://www.okta.com/contact-sales/), checked 2026-10-20 +- [DigitalOcean Droplet pricing](https://www.digitalocean.com/pricing/droplets), checked + 2026-10-20: Basic Droplet, Regular CPU, 2 GiB, 1 vCPU, $12.00 per month +- [DigitalOcean Managed Databases pricing](https://www.digitalocean.com/pricing/managed-databases), + checked 2026-10-20: PostgreSQL Standard, Basic node, 1 GiB, 1 vCPU, $15.15 per month, storage + range 10 to 30 GiB +- [DigitalOcean PostgreSQL pricing details](https://docs.digitalocean.com/products/databases/postgresql/details/pricing/), + checked 2026-10-20: single node clusters not highly available, high availability from $30.00 per + month per node, extra Standard Edition storage at $0.215 per GiB per month + +The calculator's prices all live in one file, +[`src/components/cost-calculator/pricing.ts`](https://github.com/fells-code/seamless-auth-docs/blob/main/src/components/cost-calculator/pricing.ts), +with the source and checked date next to each, so they can be re-checked in one place. diff --git a/src/content/docs/compare/keycloak.mdx b/src/content/docs/compare/keycloak.mdx index 6a263a8..7cc0c38 100644 --- a/src/content/docs/compare/keycloak.mdx +++ b/src/content/docs/compare/keycloak.mdx @@ -135,6 +135,10 @@ setup with 1,000,000 users and 20,000 clients on OpenJDK 21. Keycloak's containe traffic. +Neither charges a license or per-user fee, so what you pay for is the infrastructure. For a priced +example of one Seamless Auth container plus managed Postgres on a typical host, next to Auth0 and +Okta list prices, see the [Identity Cost Calculator](/compare/cost). + --- ## Setup From 2fadd57efdb8c63ecab2f9a0983a26c9487a23eb Mon Sep 17 00:00:00 2001 From: Brandon Corbett Date: Wed, 21 Oct 2026 10:14:55 -0400 Subject: [PATCH 2/3] docs: re-check calculator prices on publish day Every Auth0, Okta, and DigitalOcean value was re-read from the vendor pages on 2026-10-21. No price, allowance, or add-on changed, so only the checked dates move. --- src/components/cost-calculator/pricing.ts | 14 +++++++------- src/content/docs/compare/auth0.mdx | 2 +- src/content/docs/compare/cost.mdx | 16 ++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/components/cost-calculator/pricing.ts b/src/components/cost-calculator/pricing.ts index dc68e89..98cb70c 100644 --- a/src/components/cost-calculator/pricing.ts +++ b/src/components/cost-calculator/pricing.ts @@ -15,37 +15,37 @@ export const sources = { auth0: { label: 'Auth0 pricing', url: 'https://auth0.com/pricing', - checked: '2026-10-20', + checked: '2026-10-21', }, auth0Sales: { label: 'Auth0 sales', url: 'https://auth0.com/contact-us', - checked: '2026-10-20', + checked: '2026-10-21', }, okta: { label: 'Okta plans and pricing, Customer Identity tab', url: 'https://www.okta.com/pricing/', - checked: '2026-10-20', + checked: '2026-10-21', }, oktaSales: { label: 'Okta sales', url: 'https://www.okta.com/contact-sales/', - checked: '2026-10-20', + checked: '2026-10-21', }, doDroplets: { label: 'DigitalOcean Droplet pricing', url: 'https://www.digitalocean.com/pricing/droplets', - checked: '2026-10-20', + checked: '2026-10-21', }, doDatabases: { label: 'DigitalOcean Managed Databases pricing', url: 'https://www.digitalocean.com/pricing/managed-databases', - checked: '2026-10-20', + checked: '2026-10-21', }, doPostgresDocs: { label: 'DigitalOcean PostgreSQL pricing details', url: 'https://docs.digitalocean.com/products/databases/postgresql/details/pricing/', - checked: '2026-10-20', + checked: '2026-10-21', }, } satisfies Record diff --git a/src/content/docs/compare/auth0.mdx b/src/content/docs/compare/auth0.mdx index d1b5924..acb5e9f 100644 --- a/src/content/docs/compare/auth0.mdx +++ b/src/content/docs/compare/auth0.mdx @@ -108,7 +108,7 @@ page. | Pricing model | Free to self-host; managed plans in the portal | Free plan up to 25,000 MAU; paid plans priced by MAU tier, separate B2C and B2B prices, add-ons for enterprise connections, MFA, and M2M tokens |