Topics: Authentication

Authentication

FluxDeploy uses a multi-layered authentication system to secure the admin UI, agent communications, and relay-to-Core connections.

Admin Authentication

Password

The admin password is hashed using PBKDF2-SHA256 with a random 16-byte salt and 100,000 iterations. It is set during first-run setup and cannot be removed - only changed via Settings > Security.

All password comparisons use constant-time comparison (CryptographicOperations.FixedTimeEquals) to prevent timing attacks.

Session Management

After login, the server issues a signed session cookie using the ASP.NET Data Protection API. The cookie is:

  • HttpOnly - Not accessible to JavaScript
  • Secure - Only sent over HTTPS
  • SameSite=Strict - No cross-origin requests
  • Signed and encrypted - Cannot be forged without server key material

Sessions have a configurable timeout (default 24 hours) with sliding expiration - active users' tokens are refreshed when past the halfway point of their lifetime.

Pages that need no sign-in

Everything in the web UI needs a session except: the sign-in page, first-run setup (/setup), the relay's waiting screen, the technicians' PXE status page on a relay (/pxe-status, LAN only), and this documentation (/help/... and the /docs/... pop-out). The documentation is public so the setup and PXE status pages can link to it. It is the built-in help text only, with no server data, and it is readable before setup and while the product is locked. Someone who is not signed in sees it without the app's menu. Only those two addresses are open: /helpdesk or /help/../recipes still need a session.

Password Change Invalidation

When the admin password is changed, all existing sessions are immediately invalidated. Every logged-in user is forced to re-authenticate.

Roles

Role Can
Admin Everything, including settings, security, licensing, users and notifications
Operator Day-to-day deployment work: recipes, task sequences, images, drivers, boot images, sites and jobs. Refused from administrative settings, the audit log, certificates, registration keys, users, the SAML wizard tooling and Connected Cache node pruning
Read Only View everything an Operator can, change nothing; stored secrets are withheld from the page

An Operator is an administrator of every machine FluxDeploy deploys. Task steps run on the deployed machine as SYSTEM, and a step that runs after the answer file is applied has the plaintext unattend.xml (domain-join and local Administrator passwords) on disk beside it. An Operator cannot read those secrets in FluxDeploy, but can author a step that does. Give the Operator role only to people you would give local administrator rights on the fleet; use Read Only for everyone else.

Role gates apply to the web UI as well as the API: a page reads the role from the session at the start of the browser's connection and re-checks it on every change, so signing everyone out, rotating the password or demoting a user takes effect on the next click, and a browser with no valid session is sent back to the sign-in page whichever address it navigates to.

Login Rate Limiting

The LoginRateLimiter tracks failed login attempts per IP address:

  • Default threshold: 5 failed attempts
  • Lockout duration: 15 minutes
  • HTTP 429 (Too Many Requests) returned when locked out
  • Successful login clears the failure count

The lockout is checked before the password is looked at, so while an address is locked out even the correct password is refused and no session is issued. The refusal depends only on the address and the clock, so it gives away nothing about what was submitted. All three password paths behave the same way: the login API, the login form the sign-in page posts, and the current-password check when changing the admin password.

The lockout always expires on its own, and further attempts during it do not extend it - the built-in local admin has no second credential, so a lockout that could be prolonged would lock you out of your own server. Set the threshold to 0 in Settings > Security > Authentication to disable lockouts entirely. A lockout is recorded in the Audit Log as its own ACCOUNT_LOCKED event, and raises a Security notification if one is configured.

SSO Role Mapping

When SAML SSO is enabled (Settings > Security > SAML/SSO, step 4), each sign-in derives the user's role from the IdP's group or role claims:

  1. A claim matching Admin Groups / Roles gets Admin.
  2. A claim matching the configured operator groups gets Operator.
  3. Anyone matching neither gets the Default Role, which is Read Only unless you change it.

The role is re-derived on every sign-in, so the IdP is the source of truth: changing a SAML user's role by hand on the Users list lasts until their next login. Move the user between groups in the IdP, or change the mapping here, instead.

Set the Default Role to Operator or Admin only deliberately: it applies to every account your IdP admits to FluxDeploy that is not named in a group. If the page shows a warning that the saved default role could not be read, an older version stored a value this one does not recognise - unmapped users are being treated as Read Only in the meantime. Pick a role and save to make it explicit.

API Tokens

For scripted or third-party access to the admin API, create named API tokens in Settings > Security > API Tokens instead of sharing the admin password.

  • Each token has a name, a scope, and an optional expiry date
  • The full token (prefixed fdpat_) is shown once at creation - copy it then. The server stores only a SHA-256 hash plus a short prefix so tokens can be told apart in the UI
  • Requests authenticate with an Authorization: Bearer <token> header, which takes precedence over the session cookie when present

Scopes

Scope Access
ReadOnly GET requests only - equivalent to a Read Only admin user
CertInstall Only /api/certificates/install - for external certificate-automation pushes
FullAdmin Full API access - equivalent to the Admin role

An invalid or revoked token returns 401; a valid token used outside its scope returns 403 ("Token scope does not permit this action.").

Tracking and Revocation

The last-used timestamp and IP address of each token are recorded and shown in the token list, so unused or unexpected tokens are easy to spot. Tokens can be revoked at any time; revocation takes effect immediately.

Agent Authentication

Boot Password

An optional agent boot password can be configured in Settings > Deployment > Agent. It prevents unauthorized machines from registering themselves.

How it works end to end:

  1. Set a password on the Agent tab and enable the requirement.
  2. When an agent boots in WinPE, it prompts the technician to type the password.
  3. The password is sent with the registration request to the server.
  4. The server validates it before accepting the machine registration.
  5. If the password is wrong, the agent displays an error and halts.

Implementation details:

  • The password is hashed with PBKDF2-SHA256 and stored on the server
  • Agents must provide the password during check-in (POST /api/deployment/check-in)
  • If the password is wrong, the server returns 401 and the agent halts with AUTH_FAILED
  • The password can be embedded in the boot image config for unattended deployments

Agent Bearer Token

There is one agent bearer token per FluxDeploy installation, not one per boot image. It is generated on this server's first start, stored encrypted in settings, pushed to every relay site through the heartbeat, and written in plaintext into agent.config.json inside every boot image built from it. Agents send it as Authorization: Bearer <token>; the server compares SHA-256 hashes in constant time.

It is a speed bump, not a secret. PXE serves boot images over TFTP, which has no authentication of any kind. Anyone who can reach the deployment network can download the boot image, open agent.config.json, and read the token. Do not treat it as a credential that keeps anyone out, and do not build a control on the assumption that it does.

What a holder of the token can actually do is deliberately small:

Endpoint What it gives
POST /api/deployment/support-bundle Upload an agent diagnostic bundle
GET /api/agent/discover Read this server's TLS thumbprint and site ID

It is not what protects imaging. Machine check-in is gated by the optional agent boot password (above) and a per-IP rate limit. Everything after check-in - progress, console, complete, WIM streaming, drivers, and the answer file - is gated by the per-machine deployment token, described next. Those are the controls that matter for a deployment.

Rotating it

Settings > Security > Authentication > Agent Bearer Token > Rotate Token replaces the token with a fresh 256-bit value. Rotate when a boot image or a burned ISO has left your control - the same reason you would regenerate a relay API key.

Rotation is not free, and only half of it is automatic:

  • Relay sites heal themselves. Each relay's next heartbeat notices its boot image was built with the old token, rebuilds that site's boot.wim with the new one, and re-syncs it. Nothing to do.
  • Core boot images and burned ISOs do not. They keep the old token until you rebuild them from the Boot Images page. An ISO already written to a USB stick can only be replaced.

Machines that boot a stale image in the meantime can still be deployed - check-in and imaging do not use this token - but they cannot upload support bundles until the image is rebuilt.

Rotation is recorded in the audit log as AGENT_BEARER_TOKEN_ROTATED. On a relay server the panel is not shown: the token belongs to Core, and a relay receives it through the heartbeat.

Per-Machine Deployment Token

Every POST /api/deployment/check-in mints a fresh 256-bit token for that one machine and returns it to the agent, which uses it in place of the shared bearer for the rest of the deployment. The server stores only a SHA-256 hash against the machine record, so the token cannot be read back out of the database.

Unlike the shared bearer, this one is a real credential - it authorises WIM and driver downloads and the answer file, which carries the domain join and local Administrator passwords. It is therefore time-limited: it expires 24 hours after it is issued, and a machine record with no recorded expiry is treated as expired rather than valid.

Completing a deployment retires it. On success it is revoked immediately; on failure it is narrowed to a ten-minute grace period so the agent can finish uploading its logs. Autopilot recipes are the exception - the first-boot registration script runs in full Windows well after the deployment completes and authenticates with the same token, which is what sizes the 24-hour lifetime.

Relay Authentication

Relay-to-Core communication is authenticated via 256-bit API keys generated at site registration. The API key is included in every heartbeat and sync request. Keys can be regenerated from the Sites page if compromised.

IP Allowlisting

The IpAllowlistMiddleware enforces an optional IP allowlist configured in Settings > Security > Allowed IP Ranges. CIDR notation is supported (e.g., 10.0.0.0/8, 192.168.1.0/24). A single address with no prefix is treated as that address alone. An empty allowlist means no restriction, and is the default.

What it covers. The whole administrative surface: the web pages, the Blazor circuit behind them (/_blazor), the SignalR hubs and the admin API. It used to cover only /api/ paths, which meant signing in was blocked from an address that was not allowed but a session obtained from an allowed address could then drive the entire UI from anywhere, because the pages act over the circuit rather than through the API.

What stays reachable from any address, because these are machines and probes rather than administrators - an allowlist that stopped a branch office from imaging would never be switched on:

Path Who calls it
/api/deployment, /api/images/... The WinPE agent (gated by its own per-machine deployment token). WinPE drivers are embedded in the boot image at build time, so the agent fetches none over HTTP
/api/pxe The PXE boot chain
/api/relay Relay sites (gated by the relay's own API key)
/health, /ready Monitoring. Anonymously these answer with the status code and status word only; the detail (version, Core or Relay, database provider, uptime, pending migrations, license state) is returned to a signed-in session or an API token
/.well-known/acme-challenge/... A certificate authority validating an HTTP-01 challenge
/pxe-status The LAN-only technician status page on a relay

The documentation (/help, /docs) needs no sign-in but is not on this list: with an allowlist in force it is reachable from allowed addresses only, like the rest of the web UI.

If you lock yourself out. Requests from the server itself are always allowed - loopback, and the server's own address. Open https://localhost:8400 on the server's console, sign in, and correct or clear the ranges under Settings > Security > Authentication. Clearing the field switches the allowlist off entirely. A refused browser request is answered with a short 403 page saying which setting refused it and where to change it.

Registration Rate Limiting

The POST /api/deployment/check-in endpoint is rate-limited per IP (default 30 requests per minute) to prevent registration flooding from misconfigured agents.

Security Headers

All responses include hardened headers:

  • X-Frame-Options: SAMEORIGIN
  • X-Content-Type-Options: nosniff
  • Content-Security-Policy (no unsafe-eval)
  • Strict-Transport-Security (1 year, includeSubDomains)
  • Server and X-Powered-By headers removed