Keen documentation
Open Keen ↗

Administering Keen

Local accounts, MFA and security email

Chapter 2 of 30

Local two-factor authentication

Local accounts can use authenticator apps, security keys or passkeys, and single-use recovery codes. This chapter sets the policy, generates the one key that must never be lost, and explains how an operator recovers a locked-out account. SSO and trusted-proxy accounts get their second factor from the upstream provider.

User management: create accounts, set roles, deactivate, reset passwords and open each user's access.
User management: create accounts, set roles, deactivate, reset passwords and open each user's access.
Groups & permissions: create a group, give it a role, then assign permissions and members.
Groups & permissions: create a group, give it a role, then assign permissions and members.
  1. Generate the encryption key with the API image's cryptography library:
    docker compose run --rm --no-deps keen-api python -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())'
  2. Store it as KEEN_MFA_ENCRYPTION_KEY in the deployment's secret configuration, outside the database, with a secure backup. Use the same key on every API replica.
  3. Choose the policy and recreate the API containers:
    KEEN_MFA_POLICY=optional
    KEEN_MFA_ENCRYPTION_KEY=<generated Fernet key>
PolicyWho must enrol
optionalNobody; users choose.
adminsAccounts with the effective administrator role.
allEvery local account.

The key protects TOTP secrets. Losing or replacing it without migrating existing secrets prevents their verification; restore the original key or perform a controlled account recovery. Security keys store public keys and recovery codes are stored as SHA-256 hashes, so both keep working without it.

MFA ceremonies use KEEN_PUBLIC_BASE_URL as their origin and its hostname as the WebAuthn relying party, so that origin must be HTTPS (HTTP localhost is accepted for development) and the reverse proxy must preserve the browser's Origin header. Changing the hostname later means planning WebAuthn re-enrolment.

Going deeper: how the policy is enforced

Accounts subject to the policy complete enrolment after password verification and before receiving an application session, and existing local sessions without the required factor are denied at their next authentication check. Changes to passwords, factors or recovery-code sets invalidate local sessions at subsequent checks; already-open WebSocket connections are not actively disconnected. Keep at least one recovery route before enforcing a policy.

The Account Security page requires the local password and, once enrolled, an existing second factor before any change. Recovery codes are shown once, generating replacements invalidates the previous set, and a required account cannot remove its last authenticator.

Operator recovery

Verify the account owner's identity through your organisation's recovery process first. Then an operator with access to the API container and database can reset local factors:

docker compose exec keen-api python scripts/mfa_admin.py alice --reset --confirm-username alice

This removes authenticators, recovery codes and pending challenges, revokes local sessions and records an operator-reset audit entry. It keeps the password, and a required account enrols again at its next login. A password reset on its own leaves MFA enrolment in place. Restrict shell and database access accordingly.

Security notification delivery

With mail configured, Keen e-mails users about sign-ins from unfamiliar addresses and about changes to their credentials. This section configures the mail path and the proxy settings that make the IP checks truthful.

Security alerts use the ordinary SMTP settings, applied to both the API and the worker, with the Celery worker and beat running:

KEEN_SMTP_HOST=smtp.example.org
KEEN_SMTP_PORT=587
KEEN_SMTP_USERNAME=keen
KEEN_SMTP_PASSWORD=<SMTP credential>
KEEN_SMTP_USE_TLS=true
KEEN_SMTP_USE_SSL=false
KEEN_SMTP_FROM_EMAIL=keen@example.org
KEEN_SMTP_FROM_NAME=Keen
KEEN_PUBLIC_BASE_URL=https://keen.example.org

For implicit TLS on port 465, set KEEN_SMTP_USE_SSL=true and KEEN_SMTP_USE_TLS=false. Keen sends the SMTP username and password only over STARTTLS or implicit TLS. Each recipient needs an e-mail address on their Keen account; without SMTP or an address, no alert is queued.

Going deeper: which events produce an e-mail

Alerts cover unfamiliar-IP successful local and native SSO logins, local password changes and administrator resets, factor enrolment and removal, recovery-code regeneration and operator MFA resets. A password-only pending MFA challenge is not a successful login. Failed attempts, individual recovery-code use, and password or MFA changes made inside an external identity provider produce no alert, and trusted-header authentication has no Keen login ceremony, so arrange sign-in alerts at that provider. Successful-login IP history is kept even when mail is unconfigured.

Identify the client IP through proxies

KEEN_SECURITY_TRUSTED_PROXY_CIDRS lists the real reverse-proxy addresses or CIDRs, including the UI nginx hop when applicable. With an empty value, Keen uses the socket address, which behind a proxy is the proxy itself. Configure the real proxy chain before relying on unfamiliar-IP alerts, trust only the hops you operate, and restrict direct access to the API.

Going deeper: how the forwarded address is chosen

Keen walks X-Forwarded-For from right to left while the current hop is trusted, stopping at the first untrusted address. Proxies must append the actual connecting peer or overwrite the header at the public edge; avoid broad trust ranges that include untrusted clients. If Uvicorn rewrites client addresses, either use --no-proxy-headers and let Keen perform this check, or make Uvicorn's trusted-proxy configuration equally restrictive.

IP history begins when the feature is deployed, so each account's first observed address triggers one alert once mail is configured. Addresses are normalised, including IPv4-mapped IPv6; concurrent logins from the same account and address enqueue one alert; known addresses remain until the account is deleted; VPN and network changes can legitimately trigger alerts.

Delivery and troubleshooting

A change and its notification intent commit together in PostgreSQL. Every minute the worker processes up to ten queued messages and retries SMTP failures up to eight times with increasing delays capped at one hour, so delivery never runs inside a login or account-change request. SMTP acceptance means the relay took the message; mailbox delivery is the mail service's job.

Going deeper: inspecting the outbox

The worker logs notification IDs, attempt counts and status, without SMTP exception text or message contents. Operators can inspect the security_notifications table for status, attempts, next_attempt_at and last_error, and requeue failed rows after fixing SMTP. Sent and failed rows are removed after thirty days while SMTP is configured. A crash after SMTP acceptance and before recording success can produce a duplicate notification. Passwords, TOTP secrets and recovery-code values never appear in these messages.