# 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:

```sh
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:

```sh
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

```toml
[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

```sh
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:

```sh
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.

```toml
[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:

```toml
[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](https://docs.goauthentik.io/add-secure-apps/providers/oauth2/).

### 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:

```toml
[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](https://www.keycloak.org/securing-apps/oidc-layers).

### 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.