How Hosted OpenSEO Handles Authentication and Billing: Better Auth and Stripe Integration

Hosted OpenSEO uses Better Auth for identity management with Turnstile-protected signups and Google SSO, while Stripe-backed Autumn handles usage-based billing with credit pools, 1.28× platform markup, and automatic feature gating.

The every-app/open-seo repository supports two deployment modes: a fully managed SaaS offering and a self-hosted instance. In hosted mode, the platform implements enterprise-grade authentication and billing through specific integrations with Better Auth and Autumn, creating a seamless flow from user registration to credit-based API consumption.

Deployment Mode Detection

The system distinguishes between hosted and self-hosted environments through the AUTH_MODE environment variable. In src/lib/auth-mode.ts, the helper isHostedAuthMode(env.AUTH_MODE) returns true only when the variable equals "hosted", triggering the full SaaS authentication stack. When running in self-hosted mode, the system initializes a trimmed auth instance designed solely for token-minting for Search Console integration, bypassing all billing and social login configurations.

Authentication Architecture with Better Auth

All hosted authentication logic centralized in src/lib/auth.ts configures a betterAuth instance with SaaS-specific plugins and security layers.

Core Configuration and Environment Validation

The hosted auth engine requires three critical environment variables validated at runtime:

  • BETTER_AUTH_URL: Must be HTTPS or localhost, validated by getHostedBaseUrl()
  • BETTER_AUTH_SECRET: Minimum 32 characters, enforced by getHostedSecret()
  • AUTH_MODE: Must be set to "hosted" to activate the full feature set
// src/lib/auth.ts
export const auth = betterAuth({
  baseURL: getHostedBaseUrl(),
  secret: getHostedSecret(),
  // Hosted-only plugins conditionally added based on isHostedAuthMode()
});

Security Layers: Turnstile and Disposable Email Protection

Hosted deployments integrate Cloudflare Turnstile to protect signup endpoints from automation. When TURNSTILE_SECRET_KEY is present, the Turnstile plugin attaches to the auth router. Additionally, a user.create.before hook rejects registrations from disposable email domains before the user record persists to the database.

Social Login and Email Automation

Google OAuth is available exclusively in hosted mode via getGoogleSocialProviderConfig(), which reads GOOGLE_CLIENT_ID and GOOGLE_SECRET from environment variables. For transactional communications, the system wires Loops email automation through sendHostedVerificationEmail and sendHostedPasswordResetEmail functions, ensuring users verify ownership before accessing paid features.

Organization Management and Session Enrichment

Every new user receives a default organization upon signup. The organizationHooks configuration in src/lib/auth.ts implements owner-only invitation logic and role validation. During authentication, the session.create.before hook invokes resolveSignInHostedOrganization to populate the session with activeOrganizationId, ensuring subsequent API calls route to the correct billing context.

// src/lib/auth.ts - Session enrichment
session: {
  create: {
    before: async (session) => {
      const orgId = await resolveSignInHostedOrganization(session.userId);
      return { data: { ...session, activeOrganizationId: orgId } };
    }
  }
}

Billing Infrastructure via Autumn

Hosted OpenSEO monetizes through Autumn, a Stripe-backed service that manages credit pools, subscriptions, and usage tracking. All billing constants and conversion utilities reside in src/shared/billing.ts.

Credit Pool Architecture

The platform operates on a dual-credit system:

  • Usage Credits: AUTUMN_SEO_DATA_BALANCE_FEATURE_ID tracks DataForSEO consumption
  • Top-up Credits: AUTUMN_SEO_DATA_TOPUP_BALANCE_FEATURE_ID records additional credit purchases

Credits convert to USD at a fixed rate of 1,000 credits per dollar, calculated via autumnSeoDataCreditsToUsd(). Hosted customers incur a 1.28× platform markup applied through applyBillingMarkupUsd(), meaning every $1 of DataForSEO costs consumes $1.28 from the customer's credit balance.

Stripe Integration and Checkout Configuration

Subscription upgrades redirect users to Stripe Checkout sessions configured with AUTUMN_CHECKOUT_SESSION_PARAMS, which collect tax IDs and billing addresses for compliance. The Autumn client initialization in @/server/billing/autumn creates Stripe customers lazily—no billing record exists until the user performs an action requiring DataForSEO credits.

Feature Gating and Permissions

Server workflows enforce plan restrictions through requireOrgPermission() in src/serverFunctions/organization.ts. Only organizations with the AUTUMN_PAID_PLAN_FEATURE_ID can access premium API endpoints. The free tier retains access to the managed_service feature but cannot execute credit-consuming operations.

Integration: From Signup to Monetization

The hosted flow connects authentication and billing through five discrete stages:

  1. Registration: Better Auth validates Turnstile tokens, verifies email via Loops, and blocks disposable domains.
  2. Organization Provisioning: The auth system creates a default organization and injects activeOrganizationId into the session.
  3. Lazy Billing Initialization: First interaction with a paid feature (e.g., creating a project requiring DataForSEO) triggers autumn.createCustomer() using the organization ID.
  4. Credit Consumption: Workflows like RankCheckWorkflow.ts receive a BillingCustomerContext object, deducting credits from the pool while applying the 1.28× markup for cost estimation.
  5. Subscription Management: Successful Stripe Checkout updates the organization's feature set to paid_plan, unlocking elevated rate limits and additional credits.
// src/server/workflows/RankCheckWorkflow.ts (conceptual)
export async function rankCheckWorkflow(input: RankCheckInput, billingCustomer: BillingCustomerContext) {
  // Deduct credits via Autumn client
  await autumn.trackUsage(billingCustomer.id, 'seo_data', input.keywords.length);
}

Summary

  • Mode Detection: The isHostedAuthMode() helper in src/lib/auth-mode.ts gates all SaaS-specific authentication and billing features.
  • Better Auth Integration: Hosted mode activates Turnstile protection, Google SSO, Loops email automation, and organization management through configuration in src/lib/auth.ts.
  • Autumn Billing: Stripe-backed billing uses dual credit pools (usage and top-up) with 1.28× markup, defined in src/shared/billing.ts.
  • Lazy Provisioning: Billing customers initialize only when users first consume paid resources, connecting the activeOrganizationId from auth sessions to Autumn customer records.
  • Feature Gating: Server workflows check AUTUMN_PAID_PLAN_FEATURE_ID via requireOrgPermission() to restrict premium functionality.

Frequently Asked Questions

What is the difference between hosted and self-hosted authentication in OpenSEO?

Self-hosted deployments run a minimal Better Auth instance sufficient for Search Console token minting, excluding Turnstile captcha, Google social login, and organization features. Hosted mode activates the full authentication stack including email verification via Loops and disposable email blocking, as controlled by the AUTH_MODE environment variable.

How does OpenSEO prevent fake signups in hosted mode?

The platform implements two layers of protection: Cloudflare Turnstile integration validates humans during registration when TURNSTILE_SECRET_KEY is configured, and a user.create.before hook in src/lib/auth.ts rejects email addresses from known disposable domain lists before database insertion.

How are usage credits calculated and billed in hosted OpenSEO?

DataForSEO consumption draws from credit pools managed by Autumn. The system applies a 1.28× platform markup to base costs using applyBillingMarkupUsd(), converts USD to credits at 1,000 credits per dollar, and tracks balances via AUTUMN_SEO_DATA_BALANCE_FEATURE_ID. When credits deplete, users must purchase top-ups through Stripe Checkout sessions.

Can self-hosted users bypass the billing system entirely?

Yes. Self-hosted deployments do not initialize the Autumn client or check AUTUMN_PAID_PLAN_FEATURE_ID gates. The billing context (BillingCustomerContext) passed to workflows is optional or mocked in self-hosted mode, allowing unlimited local usage without Stripe integration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →