Skip to main content
Version: 0.9

Authentication

Admin bootstrap

TokenTimer creates the first admin user automatically on first startup. Before the first start, set the bootstrap environment variables:

ADMIN_EMAIL=admin@company.com
ADMIN_PASSWORD=YourSecurePassword123!
ADMIN_NAME=Administrator

On first startup, TokenTimer will:

  1. Detect no users exist.
  2. Create the admin user with the provided credentials (is_admin = true, system admin).
  3. Create the shared Default workspace and add the admin as workspace admin.
  4. Log the admin credentials (email shown, password hidden).

The bootstrap is not an HTTP endpoint. It runs internally on API startup and is:

  • Skipped if any user already exists in the database.
  • Skipped if DISABLE_ADMIN_BOOTSTRAP=true.
warning

After first login, remove ADMIN_PASSWORD from your .env file (or Helm values) and restart services. It is only needed for the bootstrap on first start.

note

When deploying via the Helm chart, config.adminEmail is required. The admin password is auto-generated if not provided; retrieve it from the Kubernetes secret after install (see the install runbook).

Disabling the bootstrap

Set DISABLE_ADMIN_BOOTSTRAP=true to skip automatic admin creation and create users manually via SQL:

INSERT INTO users (email, password_hash, display_name, auth_method, email_verified)
VALUES ('admin@company.com', '$2b$12$...', 'Admin', 'local', TRUE);

System admin vs workspace owner

TokenTimer separates installation-wide administration from workspace ownership. They use different database fields and are granted through different paths.

System adminWorkspace owner
Stored asusers.is_admin = TRUEworkspace_memberships.role = 'admin'
ScopeWhole installation (System Settings, SMTP, grant/revoke system admin)One workspace (delete workspace, transfer tokens, org-scoped audit for that workspace)
DashboardSystem Settings nav (when the session user is admin)Workspaces owner actions (rename, delete, transfer tokens)
How to grantWorkspaces > Members > System admin toggle (system admins only)Automatic when creating a workspace; not assignable from the Members tab
How manyMultiple system admins supportedOne owner per workspace in normal operation (creator at provision time)

Granting system admin to a second user does not make them workspace owner on the shared Default workspace. They remain manager there unless they separately created that workspace.

For the day-to-day role model (Admin, Workspace Manager, Viewer) and permission boundaries, see Teams, Workspaces, and RBAC.

Granting a second system admin

A system admin can open Workspaces > Members, find any member, and enable System admin. That sets users.is_admin = TRUE only. The member keeps their current workspace role (typically manager on Default). They gain System Settings and admin API access immediately (the session reloads is_admin on each request).

The last system admin cannot demote themselves.

What you cannot do from the UI

  • Invite someone as workspace owner (admin membership is rejected by the API).
  • Promote an existing member to workspace owner via the role dropdown (only Viewer and Manager).
  • Demote or remove a workspace owner via the Members tab (the API blocks changes to admin membership rows).

Adding another workspace owner currently requires a direct database change; the product intentionally supports a single owner per workspace until co-owner management ships.

Default workspace join semantics

When a user without workspace membership logs in or registers (and is not joining via invitation):

  1. Pending invitations are accepted first.
  2. Otherwise, if the Default workspace already exists, they join it.
  3. If exactly one workspace exists (legacy installs), they join that workspace.
  4. Otherwise, a new Default workspace is created.

Join role rules:

  • Join role is always workspace_manager when Default already exists, even if users.is_admin = TRUE.
  • Join role is admin (workspace owner) only when this login creates a brand-new Default workspace (empty install).

Invitation flow

For admins and workspace managers

  1. Log in to the dashboard.
  2. Go to Workspaces, select a workspace, then Members.
  3. Invite by email with role Viewer or Manager.
  4. To grant system admin (installation-wide access to System Settings and admin APIs), toggle System admin on an existing member. Only current system admins see this control.

If the email belongs to an existing user, they are added to the workspace directly. If not, an invitation token is created and (optionally) emailed.

For invited users

  1. Receive the invitation email (or a link shared by the admin).
  2. Click the invitation link: https://your-instance.com/auth/verify-email/<token>.
  3. Set a password (min 8 characters, uppercase and number required by default).
  4. The account is created and automatically added to the workspace with the assigned role.
note

If SMTP is not configured, share invitation links via a secure channel manually. Invitations expire after 7 days by default. See Configuration for SMTP setup.

Password reset and recovery

Users can reset their own passwords only if SMTP is configured. Otherwise, an admin must reset via the database or re-invite the user.

Forgotten admin password

The bootstrap only runs when no users exist, so setting ADMIN_PASSWORD again will not help. Options:

  1. Use the password reset flow (requires SMTP to be configured).
  2. Reset via the database:

Generate a bcrypt hash (cost 12):

node -e "require('bcryptjs').hash('NewPassword123!',12).then(h=>console.log(h))"

Then update the user row:

UPDATE users SET password_hash = '<hash from above>' WHERE email = 'admin@company.com';

Feature summary and tuning

FeatureStatus
Admin bootstrap (env vars)Enabled by default
User invitations (admin only)Always available
Local email/password authEnabled by default
Two-factor authentication (TOTP)Enabled by default
CSRF protectionEnabled by default
Email verificationConfigurable (REQUIRE_EMAIL_VERIFICATION)

All auth tuning variables are optional with sensible defaults:

LOCAL_AUTH_ENABLED=true
REQUIRE_EMAIL_VERIFICATION=true
TWO_FACTOR_ENABLED=true
SESSION_MAX_AGE=86400000 # 24h in ms
MIN_PASSWORD_LENGTH=8
REQUIRE_UPPERCASE=true
REQUIRE_NUMBERS=true
CSRF_ENABLED=true

See the Configuration Reference for the full authentication and security variable tables.

Best practices

  • Use a strong ADMIN_PASSWORD and remove it from env after first login.
  • Use a secrets manager or Helm existingSecret for credentials.
  • Invite users with the least-privileged role (Viewer by default).
  • Encourage all users to enable 2FA.
  • Review the audit log for invitation history.