# How OpenSEO Handles Scheduled Jobs for OAuth and Audits

> Discover how OpenSEO uses Cloudflare Workers Cron triggers for audits and on-demand OAuth token refreshes with a 30-day TTL, ensuring efficient site management.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: internals
- Published: 2026-09-10

---

**OpenSEO leverages Cloudflare Workers' native Cron triggers for periodic site audits while handling OAuth token refreshes on-demand through a dedicated token endpoint that enforces a 30-day TTL, implemented across the oauth-provider and audit workflow files.**

OpenSEO is an open-source SEO platform designed for edge deployment on Cloudflare Workers. Its architecture separates time-sensitive audit workflows from authentication lifecycle management, using distinct patterns for scheduled execution versus token refresh orchestration.

## OAuth Token Refresh Architecture

Unlike traditional cron-based refresh mechanisms, OpenSEO implements an **on-demand token rotation strategy** within its OAuth provider. When an access token expires, the consumer initiates a refresh request that validates, rotates, and persists new credentials atomically.

### Token Endpoint and TTL Management

The OAuth provider logic resides in [`src/server/mcp/oauth-provider.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/oauth-provider.ts). This file defines the token endpoint handling for `grant_type=refresh_token` requests and enforces the **MCP_REFRESH_TOKEN_TTL_SECONDS** constant, which sets a 30-day lifespan for refresh tokens.

When a refresh request arrives, the provider validates the existing token against its TTL, generates a new access token, and conditionally rotates the refresh token if the current one approaches expiration. This approach eliminates the need for background scheduled jobs to maintain token freshness while ensuring credentials never exceed their maximum age.

### Secure Token Persistence

Refresh tokens are encrypted and stored via the self-hosted Google OAuth implementation in [`src/server/features/google/selfHostedOAuth.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/google/selfHostedOAuth.ts). This module persists the rotated refresh tokens securely between refresh cycles, ensuring that subsequent authentication requests can retrieve valid credentials without user intervention.

## Scheduled Site Audit Workflows

Site audits run on true scheduled intervals using Cloudflare Workers' **ExportedHandlerScheduledHandler** interface. The platform defines these triggers in type declarations and implements the execution logic in dedicated workflow modules.

### Cron Configuration in Worker Definitions

The [`worker-configuration.d.ts`](https://github.com/every-app/open-seo/blob/main/worker-configuration.d.ts) file declares the scheduled handler interface for the auxiliary audit worker. This configuration exposes the Cron trigger to the Cloudflare runtime, allowing the platform to invoke the audit workflow at defined intervals without external scheduling services.

### SiteAuditWorkflow Orchestration

The actual audit execution is managed by [`src/server/features/rank-tracking/services/scheduledRankChecks.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/rank-tracking/services/scheduledRankChecks.ts) and similar workflow modules like [`src/server/workflows/siteAuditWorkflowPhases.ts`](https://github.com/every-app/open-seo/blob/main/src/server/workflows/siteAuditWorkflowPhases.ts). When the Cron trigger fires, the scheduled handler extracts pending audit configurations and invokes `SiteAuditWorkflow.run()`.

This workflow orchestrates a multi-phase process including site crawling, Lighthouse performance analysis, and result persistence through the `AuditRepository`. The pattern demonstrated in [`scheduledRankChecks.ts`](https://github.com/every-app/open-seo/blob/main/scheduledRankChecks.ts) shows how the platform retrieves pending jobs, iterates through audit IDs, and launches workflow instances with payloads containing `auditId`, `origin`, `startUrl`, and `maxPages`.

## Implementation Examples

The following patterns illustrate how OpenSEO handles both OAuth refreshes and scheduled audits.

Refreshing an OAuth token on-demand:

```typescript
// Client-side refresh request to OpenSEO's OAuth endpoint
const response = await fetch(
  `${BASE_URL}/mcp/oauth/token`,
  {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "refresh_token",
      refresh_token: storedRefreshToken,
      client_id: CLIENT_ID,
    }),
  }
);
const { access_token, refresh_token } = await response.json();

```

Scheduled audit handler in the worker:

```typescript
// Defined in the audit worker's scheduled handler
export const scheduled: ExportedHandlerScheduledHandler<Env> = async (controller) => {
  const pending = await AuditService.getPendingAudits();
  
  for (const audit of pending) {
    await SiteAuditWorkflow.run({
      auditId: audit.id,
      origin: audit.origin,
      startUrl: audit.startUrl,
      maxPages: audit.maxPages,
    });
  }
};

```

Launching rank-checking jobs using the same pattern:

```typescript
// Pattern from scheduledRankChecks.ts
import { runScheduledRankChecks } from "@/server/features/rank-tracking/services/scheduledRankChecks";

export const scheduled: ExportedHandlerScheduledHandler<Env> = async () => {
  await runScheduledRankChecks();
};

```

## Summary

- **On-Demand OAuth**: OpenSEO refreshes Google OAuth tokens via the `grant_type=refresh_token` endpoint in [`src/server/mcp/oauth-provider.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/oauth-provider.ts) rather than using cron jobs, utilizing a 30-day TTL and secure storage in [`selfHostedOAuth.ts`](https://github.com/every-app/open-seo/blob/main/selfHostedOAuth.ts).
- **Cron-Based Audits**: Site audits execute through Cloudflare Workers' `ExportedHandlerScheduledHandler` declared in [`worker-configuration.d.ts`](https://github.com/every-app/open-seo/blob/main/worker-configuration.d.ts), triggering `SiteAuditWorkflow.run()` orchestrated in the workflow phases module.
- **Workflow Pattern**: The platform handles scheduled jobs by retrieving pending configurations and dispatching them to typed workflow runners, as demonstrated in the rank-tracking service implementation.

## Frequently Asked Questions

### Does OpenSEO use a cron job to refresh OAuth tokens?

No. OpenSEO handles OAuth token refreshes on-demand through the token endpoint defined in [`src/server/mcp/oauth-provider.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/oauth-provider.ts). When a client requests a refresh, the provider validates the existing token, checks its TTL, and returns new credentials without requiring a background scheduled process.

### How are refresh tokens stored securely between refreshes?

The [`src/server/features/google/selfHostedOAuth.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/google/selfHostedOAuth.ts) module encrypts and persists refresh tokens in the Cloudflare Workers KV or similar storage. This ensures that tokens remain available for subsequent on-demand refreshes while maintaining encryption at rest.

### What triggers the SiteAuditWorkflow to run?

The workflow triggers via Cloudflare's Cron mechanism through the `scheduled` handler exported in the audit worker configuration. When the Cron interval specified in [`worker-configuration.d.ts`](https://github.com/every-app/open-seo/blob/main/worker-configuration.d.ts) elapses, the handler invokes `SiteAuditWorkflow.run()` with the appropriate audit parameters.

### Where is the scheduled job handler configured for rank tracking?

The pattern is implemented in [`src/server/features/rank-tracking/services/scheduledRankChecks.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/rank-tracking/services/scheduledRankChecks.ts), which provides a `runScheduledRankChecks()` function called by the worker's `scheduled` export. This follows the same architectural pattern as site audits, retrieving pending configurations and dispatching them to workflow runners.