Skip to content
cronpad
Self-hosting / Authentication

Authentication

People sign in to cronpad with Google, GitHub or a generic OIDC provider; programs use API keys. A self-hosted server needs at least one sign-in provider configured (or development login for local testing) and refuses to start otherwise, so it’s never accidentally open. Production servers also require secrets.encryption_key (32 random bytes encoded as base64) to protect webhook signing secrets and cached create responses. Set CRONPAD_SECRETS__ENCRYPTION_KEY before creating jobs, including during development. You can restrict sign-in to your company’s email domains.

GitHub

  1. GitHub → Settings → Developer settings → OAuth Apps → New OAuth App.
  2. Homepage URL: your server.public_url.
  3. Authorization callback URL: <public_url>/auth/github/callback.
  4. Generate a client secret, then set:
CRONPAD_AUTH__GITHUB__CLIENT_ID=Iv1.…
CRONPAD_AUTH__GITHUB__CLIENT_SECRET=…

cronpad uses the account’s primary verified email address.

Google

  1. Google Cloud Console → APIs & Services → Credentials → Create credentials → OAuth client ID → Web application.
  2. Authorized redirect URI: <public_url>/auth/google/callback.
  3. Set:
CRONPAD_AUTH__GOOGLE__CLIENT_ID=….apps.googleusercontent.com
CRONPAD_AUTH__GOOGLE__CLIENT_SECRET=…

Only Google accounts with a verified email can sign in.

Restricting access

[auth]
allowed_email_domains = ["example.com"]   # only @example.com addresses
allow_signup = false                      # default; true lets anyone who can sign in create an account

If someone signs in with GitHub and later with Google using the same verified email, both identities are linked to one user.

An identity is the provider’s account ID, not its email. If someone’s email changes at the provider, they still sign in to the same user, and cronpad updates the user’s address to the new one (the new address must still match allowed_email_domains). If another user already has the new address, cronpad keeps the old one and logs a warning.

First sign-in

Sign-up is off by default (allow_signup = false), with one exception: while the server has no users, the first person to sign in gets an account. Until then the dashboard opens on a setup page (/setup) instead of the sign-in page, and the server logs no users yet at startup. After that first account exists, people without an account see “This server doesn’t take new accounts” when they try to sign in.

Because the first sign-in on an empty server always succeeds, sign in yourself as soon as the server is reachable, or set allowed_email_domains before you expose it.

To let more people create accounts, set allow_signup = true (CRONPAD_AUTH__ALLOW_SIGNUP=true), ideally together with allowed_email_domains. Development login is local-only and always creates accounts.

Users and workspaces

Each person who signs in for the first time gets a user and a personal workspace of their own. Jobs, runs, secrets, alert channels and API keys belong to that workspace, and only its owner sees them. A self-hosted server has no shared workspaces, members or invitations: two people who sign in with the same provider still see separate dashboards.

To let a script, a CI pipeline or an assistant manage someone’s jobs, give it an API key for that person’s workspace: POST /api/v1/api-keys with only the scopes it needs (the dashboard’s API keys tab shows the command), or cronpad user create-key on the server.

Development login

CRONPAD_AUTH__DEV_LOGIN=true

Shows a simple name field on the sign-in page with no password. cronpad logs a warning on every start while it’s on. Use it only on your own machine.

API keys without a browser

To automate a fresh server before anyone has opened the dashboard, sign in once through the dashboard, then mint a key from the server itself:

cronpad user create-key --email you@example.com --name provisioning

Sessions

Session cookies (cp_session) are HttpOnly, SameSite=Lax, Secure when public_url is https, and last 30 days (auth.session_lifetime). Signing out (POST /auth/logout) deletes the session on the server. Login also sets cp_csrf; send its value in X-CSRF-Token on cookie-authenticated mutations. OAuth state, PKCE verifier and nonce live in a signed, ten-minute HttpOnly cookie. Hosted sign-in endpoints allow ten requests per minute per peer IP.

Generic OIDC

Configure one instance-wide OpenID Connect provider. cronpad discovers the provider’s endpoints and signing keys, uses authorization code flow with PKCE and nonce validation, and stores identities as oidc:default. This provider satisfies the startup requirement for a configured OAuth provider.

[auth.oidc]
issuer = "https://auth.example.com/application/o/cronpad/"
client_id = "cronpad"
client_secret = "replace-with-your-client-secret"
display_name = "Company SSO"
scopes = ["openid", "email", "profile"]
require_email_verified = true

Register the exact redirect URI <public_url>/auth/oidc:default/callback with your identity provider. Set server.public_url to the public HTTPS origin. Keep the client secret on the server; for example, set CRONPAD_AUTH__OIDC__CLIENT_SECRET instead of saving it in a configuration file. The dashboard uses display_name for the login button; the default is SSO. The unauthenticated /api/v1/features endpoint lists the provider and its label without credentials.

Authentik

Create an OAuth2/OpenID Connect provider and an application with slug cronpad. Use a confidential client, authorization code flow, and the redirect URI above. Select scope mappings for openid, email and profile, and use a signing key. Use per-provider issuer mode so the configured issuer and discovery document match:

[auth.oidc]
issuer = "https://auth.example.com/application/o/cronpad/"
client_id = "your-authentik-client-id"
client_secret = "your-authentik-client-secret"
display_name = "Authentik"

The application slug forms the issuer path. See Authentik’s OAuth2 provider reference.

Keycloak

Create an OpenID Connect client named cronpad in your realm, enable client authentication and Standard Flow, and set the exact redirect URI above. Ensure users have verified email addresses and that the email/profile scopes include the expected claims. Copy the client secret into cronpad:

[auth.oidc]
issuer = "https://identity.example.com/realms/company"
client_id = "cronpad"
client_secret = "your-keycloak-client-secret"
display_name = "Keycloak"

The issuer is the realm URL, without /.well-known/openid-configuration. See Keycloak’s OIDC endpoint reference.

Email trust and workspace policies

The default requires an email_verified claim. allowed_email_domains and allow_signup apply to OIDC too, and matching trusted email addresses link identities. Only disable require_email_verified for a provider you trust to assert authoritative email addresses: disabling it also permits email-based account linking without that claim.

Per-workspace providers, SAML and SSO enforcement are Enterprise features; instance-wide OIDC needs no license.