Skip to content

Dashboard Authentication

The Ironflow dashboard protects the Web UI using email and password authentication. This is managed independently of the API Keys used for SDK and worker communication.

During the Bootstrap Process, Ironflow creates a default administrator:

FieldValue
Emailadmin@ironflow.local
PasswordRandomly generated (see server logs on first boot).

  1. Login: The user submits credentials to POST /api/v1/auth/login.
  2. Session: The server returns a JWT (JSON Web Token) signed with an HMAC-SHA256 secret.
  3. Storage: The dashboard stores this token as a browser cookie (ironflow_dashboard_token) and in localStorage.
  4. Expiry: Sessions are valid for 24 hours.
  5. Validation: On load, if a token exists in localStorage, the dashboard calls GET /api/v1/auth/validate to verify the JWT is still accepted by the server. If the token is stale (e.g., the signing secret changed), the token is cleared and the user is redirected to the login page. If no token exists, the login page is shown directly.
sequenceDiagram
    participant Browser
    participant Dashboard as Dashboard (React)
    participant Server as Ironflow Server

    Note over Server: Startup: load JWT secret from<br/>.ironflow_jwt_secret (or generate + persist)

    rect rgb(40, 40, 55)
    Note right of Browser: First visit (no token in localStorage)
    Dashboard->>Server: GET /api/v1/capabilities
    Server-->>Dashboard: { auth_required: true }
    Note right of Dashboard: isLoggedIn() = false, skip validate
    Dashboard->>Browser: Show login page
    Browser->>Server: POST /api/v1/auth/login { email, password }
    Server-->>Browser: { token: "eyJ..." }
    Browser->>Browser: Store JWT in cookie + localStorage
    end

    rect rgb(40, 55, 40)
    Note right of Browser: Normal usage (JWT valid)
    Dashboard->>Server: GET /api/v1/functions<br/>Cookie: ironflow_dashboard_token=eyJ...<br/>X-Ironflow-Environment: env_default
    Server->>Server: Verify JWT signature + expiry
    Server-->>Dashboard: 200 { functions: [...] }

    Note right of Browser: User switches to new project/environment
    Dashboard->>Server: GET /api/v1/functions<br/>Cookie: ironflow_dashboard_token=eyJ...<br/>X-Ironflow-Environment: env_myproject_staging
    Server->>Server: Same JWT, different env header
    Server-->>Dashboard: 200 { functions: [...] }
    end

    rect rgb(55, 50, 40)
    Note right of Browser: Returning after server restart
    Dashboard->>Server: GET /api/v1/capabilities
    Server-->>Dashboard: { auth_required: true }
    Note right of Dashboard: isLoggedIn() = true (localStorage token exists)
    Dashboard->>Server: GET /api/v1/auth/validate<br/>Cookie: ironflow_dashboard_token=eyJ...
    Server->>Server: Same secret from file, JWT still valid
    Server-->>Dashboard: 200 { valid: true }
    Note right of Dashboard: Session continues, no re-login needed
    end

    rect rgb(55, 40, 40)
    Note right of Browser: Stale token (secret was reset)
    Dashboard->>Server: GET /api/v1/capabilities
    Server-->>Dashboard: { auth_required: true }
    Note right of Dashboard: isLoggedIn() = true (localStorage token exists)
    Dashboard->>Server: GET /api/v1/auth/validate<br/>Cookie: ironflow_dashboard_token=eyJ...
    Server-->>Dashboard: 401 (signature mismatch)
    Dashboard->>Dashboard: Clear stale token (logout)
    Dashboard->>Browser: Redirect to login page
    end

The JWT signing secret is resolved at startup with the following precedence:

  1. Explicit config: IRONFLOW_JWT_SECRET env var or auth.jwtSecret in YAML (hex-encoded, minimum 16 bytes).
  2. Persisted file: .ironflow_jwt_secret next to the SQLite database, or in the working directory for PostgreSQL deployments.
  3. Auto-generate: If neither exists, a 32-byte random secret is generated and written to the file for next time.
Terminal window
# Via environment variable (hex-encoded, minimum 16 bytes)
IRONFLOW_JWT_SECRET="$(openssl rand -hex 32)" ironflow serve
# Via ironflow.yaml
spec:
auth:
jwtSecret: ${IRONFLOW_JWT_SECRET}

Once logged in as an admin, you can manage users through the Users page in the dashboard sidebar. You can:

  • Add team members by email.
  • Assign roles (viewer, developer, admin).
  • Revoke access by deleting user accounts.

For local development, you can bypass dashboard authentication entirely:

Terminal window
ironflow serve --dev

This skips the login screen and grants full admin access to the dashboard. See the CLI Reference for details.