Skip to content

AuthHero Configuration

This document describes how to configure the AuthHero package for your application.

Configuration Options

When initializing the AuthHero instance, you can provide a configuration object with the following options:

typescript
import { init, AuthHeroConfig } from "@authhero/authhero";

const config: AuthHeroConfig = {
  dataAdapter: adapter, // Required
  allowedOrigins: ["https://example.com"], // Optional
  samlSigner: new HttpSamlSigner("https://signing-service.com/sign"), // Optional
  hooks: {
    // Optional hooks
  },
};

const { app } = init(config);

Core Configuration

dataAdapter (required)

The database adapter to use. AuthHero supports multiple adapters:

typescript
import { createKyselyAdapter } from "@authhero/kysely";

const config: AuthHeroConfig = {
  dataAdapter: createKyselyAdapter(db),
};

allowedOrigins (optional)

An array of allowed origins for CORS. This is useful for restricting which domains can access your authentication endpoints.

typescript
const config: AuthHeroConfig = {
  dataAdapter: adapter,
  allowedOrigins: ["https://app.example.com", "https://example.com"],
};

samlSigner (optional)

A SAML signer instance for signing SAML responses. This is required if you need SAML authentication support.

typescript
import { HttpSamlSigner } from "authhero";

const config: AuthHeroConfig = {
  dataAdapter: adapter,
  samlSigner: new HttpSamlSigner("https://signing-service.com/sign"),
};

For detailed SAML configuration options, see the SAML Package Documentation.

poweredByLogo (optional)

An optional powered-by logo to display at the bottom left of the login widget. This is only configurable in code and is not stored in the database.

The logo appears with reduced opacity (70%) and increases to full opacity on hover. It can optionally be made clickable by providing an href.

typescript
const config: AuthHeroConfig = {
  dataAdapter: adapter,
  poweredByLogo: {
    url: "https://example.com/logo.svg",
    alt: "Powered by Example",
    href: "https://example.com", // Optional - makes the logo clickable
    height: 24, // Optional - defaults to 20 pixels
  },
};

Configuration Options:

  • url (required): URL of the logo image (SVG, PNG, etc.)
  • alt (required): Alt text for accessibility
  • href (optional): If provided, the logo becomes a clickable link that opens in a new tab
  • height (optional): Height of the logo in pixels. Defaults to 20px if not specified

TIP

This option is different from powered_by_logo_url in the branding API. The poweredByLogo config option is set in code at initialization time and applies to all tenants, while the branding API field is stored in the database per tenant. Use this config option when you want to enforce a consistent powered-by logo across all tenants.

codeExecutor (optional)

A code executor for running user-authored code hooks in a sandboxed environment. If not provided, code hooks are silently skipped.

AuthHero ships three implementations:

  • LocalCodeExecutor (from authhero) — Uses new Function(), suitable for local development only (no isolation)
  • WorkerLoaderCodeExecutor (from @authhero/cloudflare-adapter) — Uses Cloudflare Dynamic Workers (Worker Loader binding) for isolated, sandboxed execution from in-memory code. No separate deploy step.
  • DispatchNamespaceCodeExecutor (from @authhero/cloudflare-adapter) — Uses Workers for Platforms dispatch namespaces. User code is pre-deployed as individual worker scripts.
typescript
import { init } from "authhero";
import { WorkerLoaderCodeExecutor } from "@authhero/cloudflare-adapter";

// Cloudflare Workers environment
const config: AuthHeroConfig = {
  dataAdapter: adapter,
  codeExecutor: new WorkerLoaderCodeExecutor({
    loader: env.LOADER, // Worker Loader binding
  }),
};

The Worker Loader executor requires a worker_loaders binding in your wrangler.toml:

toml
[[worker_loaders]]
binding = "LOADER"

For more details, see the Hooks Guide — Code Executors.

webhookInvoker (optional)

A custom function that replaces the default webhook invocation logic. By default, AuthHero sends webhook requests as POST with a JSON body and a Bearer service token. Use this option to format the message differently, add your own authentication, set custom headers, or route webhooks through a proxy.

The function receives an object with these properties:

ParameterTypeDescription
hookHookThe hook being invoked (contains url, hook_id, trigger_id, etc.)
dataRecord<string, unknown>The payload data for the webhook
tenant_idstringThe tenant ID
createServiceToken(scope?: string) => Promise<string>Lazily creates a service token. Only generates the token when called — no overhead if not used.

The function must return a Response object. If the response status is >= 400, the invocation will be logged as failed.

typescript
const config: AuthHeroConfig = {
  dataAdapter: adapter,
  webhookInvoker: async ({ hook, data, tenant_id, createServiceToken }) => {
    // Use the built-in service token, or replace with your own auth
    const token = await createServiceToken();

    return fetch(hook.url, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
        "X-Tenant-Id": tenant_id,
      },
      body: JSON.stringify({
        event: data.trigger_id,
        timestamp: new Date().toISOString(),
        payload: data,
      }),
    });
  },
};

For more details and examples, see the Hooks Guide — Custom Webhook Invoker.

Hooks Configuration

Hooks allow you to customize authentication logic at various points in the authentication flow. All hooks are optional.

TIP

For detailed documentation about hooks, including Auth0 compatibility, URL/Form hooks, and complete API reference, see the Hooks Documentation.

typescript
const config: AuthHeroConfig = {
  dataAdapter: adapter,
  hooks: {
    onExecuteCredentialsExchange: async (event, api) => {
      // Customize token claims or deny access
    },
    onExecutePreUserRegistration: async (event, api) => {
      // Validate or modify user data before registration
    },
    onExecutePostUserRegistration: async (event, api) => {
      // Perform actions after user registration
    },
    onExecutePreUserUpdate: async (event, api) => {
      // Validate or modify user data before update
    },
    onExecutePreUserDeletion: async (event, api) => {
      // Validate or cancel user deletion
    },
    onExecutePostUserDeletion: async (event, api) => {
      // Cleanup after user deletion
    },
    onExecutePostLogin: async (event, api) => {
      // Perform actions after successful login
    },
  },
};

Quick Hook Examples

onExecuteCredentialsExchange - Customize token claims

typescript
onExecuteCredentialsExchange: async (event, api) => {
  api.accessToken.setCustomClaim("roles", ["admin", "user"]);
  api.idToken.setCustomClaim("organization", "acme-corp");
};

onExecutePreUserRegistration - Set initial metadata

typescript
onExecutePreUserRegistration: async (event, api) => {
  api.user.setUserMetadata("signup_source", "web");
};

onExecutePostLogin - Custom redirects

typescript
onExecutePostLogin: async (event, api) => {
  api.redirect.sendUserTo("https://example.com/welcome", {
    query: { user_id: event.user?.user_id },
  });
};

For complete hook documentation including all event data, API methods, and advanced use cases, see the Hooks Documentation.

Environment Variables (Bindings)

When deploying AuthHero, additional configuration is provided through environment variables (bindings). These are typically set in your deployment environment (e.g., Cloudflare Workers, Docker, etc.):

Required Environment Variables

  • AUTH_URL: The base URL for authentication endpoints
  • ISSUER: The issuer identifier for JWT tokens
  • JWKS_CACHE_TIMEOUT_IN_SECONDS: Cache timeout for JWKS keys
  • ORGANIZATION_NAME: Organization name used in certificates

Optional Environment Variables

  • SAML_SIGN_URL: URL for the SAML signing service (used if samlSigner not provided in config)
  • JWKS_URL: Custom JWKS endpoint URL
  • UNIVERSAL_LOGIN_URL: Custom universal login page URL
  • OAUTH_API_URL: Custom OAuth API URL
  • ENVIRONMENT: Environment name (e.g., "production", "development")

SAML Configuration Priority

If both samlSigner config option and SAML_SIGN_URL environment variable are set, the samlSigner config takes priority. See SAML Configuration for details.

Email Service Adapter

Configure an email service adapter for sending authentication emails. The adapter is provided as part of DataAdapters:

typescript
import { EmailServiceAdapter } from "@authhero/adapter-interfaces";

const emailService: EmailServiceAdapter = {
  async send(params) {
    // params.emailProvider contains credentials and provider config
    // Send via your preferred provider (SendGrid, SES, etc.)
  },
};

const dataAdapter = {
  ...createAdapters(db),
  emailService,
};

const { app } = init({ dataAdapter });

The EmailServiceAdapter interface:

typescript
interface EmailServiceAdapter {
  send(params: {
    emailProvider: EmailProvider;
    to: string;
    from: string;
    subject: string;
    html?: string;
    text?: string;
    template: string;
    data: Record<string, string>;
  }): Promise<void>;
}

Built-in: MailgunEmailService

A zero-dependency Mailgun implementation is shipped from authhero directly. It uses fetch only — no SDK is pulled in.

typescript
import { init, MailgunEmailService } from "authhero";

const dataAdapter = {
  ...createAdapters(db),
  emailService: new MailgunEmailService(),
};

const { app } = init({ dataAdapter });

Per-tenant credentials are stored on the tenant's email_providers row and use the same shape as Auth0's Mailgun configuration, so tenants migrating from Auth0 can copy their values verbatim:

json
{
  "name": "mailgun",
  "enabled": true,
  "default_from_address": "noreply@example.com",
  "credentials": {
    "api_key": "key-...",
    "domain": "mg.example.com",
    "region": "eu"
  }
}

region is "eu" for Mailgun's EU region or null/omitted for US (default). Each send() call POSTs to /v3/{domain}/messages with HTTP Basic auth (api:{api_key}) and includes template + h:X-Mailgun-Variables so integrators who upload Mailgun-side templates named after the auth flows (auth-code, auth-password-reset, auth-link, auth-verify-email, auth-pre-signup-verification) get rendered output. The inline html/text is sent as fallback.

SMS Service Adapter

Configure an SMS service adapter for sending authentication codes. The adapter is provided as part of DataAdapters:

typescript
import { SmsServiceAdapter } from "@authhero/adapter-interfaces";

const smsService: SmsServiceAdapter = {
  async send(params) {
    // params.options contains provider-specific config (e.g., Twilio credentials)
    // Send via your preferred provider (Twilio, Vonage, etc.)
  },
};

const dataAdapter = {
  ...createAdapters(db),
  smsService,
};

const { app } = init({ dataAdapter });

The SmsServiceAdapter interface:

typescript
interface SmsServiceAdapter {
  send(params: {
    to: string;
    from?: string;
    text: string;
    template: string;
    options: Record<string, unknown>;
    data: Record<string, string>;
  }): Promise<void>;
}

Branding Configuration

Branding is configured per-tenant through the Management API, not through the config object. The branding configuration includes:

Logo and Favicon

typescript
{
  logo_url: "https://example.com/logo.png",
  favicon_url: "https://example.com/favicon.ico",
  powered_by_logo_url: "https://example.com/powered-by.png", // Optional
}

INFO

The powered_by_logo_url field is available in the API. When set, it displays a "powered by" logo in the footer of authentication pages. The logo appears with reduced opacity and increases on hover.

Colors

typescript
{
  colors: {
    primary: "#0066cc",
    page_background: {
      type: "gradient",
      start: "#ffffff",
      end: "#f0f0f0",
      angle_deg: 45
    }
  }
}

Custom Fonts

typescript
{
  font: {
    url: "https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap";
  }
}

Complete Example

Here's a complete example showing all configuration options:

typescript
import { init, AuthHeroConfig } from "@authhero/authhero";
import { createKyselyAdapter } from "@authhero/kysely";

const config: AuthHeroConfig = {
  // Required: Database adapter
  dataAdapter: createKyselyAdapter(db),

  // Optional: CORS configuration
  allowedOrigins: ["https://app.example.com", "https://example.com"],

  // Optional: Powered-by logo in login widget
  poweredByLogo: {
    url: "https://cdn.example.com/powered-by-logo.svg",
    alt: "Powered by Example",
    href: "https://example.com",
    height: 20,
  },

  // Optional: Custom webhook invoker
  webhookInvoker: async ({ hook, data, tenant_id, createServiceToken }) => {
    const token = await createServiceToken();
    return fetch(hook.url, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ event: data.trigger_id, payload: data }),
    });
  },

  // Optional: Hooks for custom logic
  hooks: {
    onExecuteCredentialsExchange: async (event, api) => {
      // Add custom claims based on user properties
      if (event.user?.app_metadata?.role) {
        api.accessToken.setCustomClaim("role", event.user.app_metadata.role);
      }

      // Add organization info
      if (event.organization) {
        api.idToken.setCustomClaim("org_id", event.organization.id);
      }
    },

    onExecutePreUserRegistration: async (event, api) => {
      // Set default user metadata
      api.user.setUserMetadata("created_via", "signup_form");
      api.user.setUserMetadata("terms_accepted", true);
    },

    onExecutePostUserRegistration: async (event, api) => {
      // Log registration for analytics
      console.log(`User registered: ${event.user?.email}`);
    },

    onExecutePreUserUpdate: async (event, api) => {
      // Track update timestamp
      api.user.setUserMetadata("updated_at", new Date().toISOString());
    },

    onExecutePostLogin: async (event, api) => {
      // Check if MFA is required
      if (
        event.user?.app_metadata?.require_mfa &&
        !event.authentication?.methods.find((m) => m.name === "mfa")
      ) {
        api.prompt.render("mfa-challenge");
      }
    },
  },
};

// Initialize AuthHero
const { app, managementApp, oauthApp, universalApp } = init(config);

// Export for your deployment platform
export default app;

Additional Resources

Released under the MIT License.