Security Reference
Encryption, SSO configuration, MFA enforcement, session management, IP controls, tenant isolation, secrets vault, and compliance overview.
Encryption
At Rest
- •All data is encrypted at rest using AES-256-GCM with workspace-specific data encryption keys (DEKs).
- •DEKs are wrapped by a master key stored in an HSM-backed key management service (AWS KMS or Azure Key Vault depending on your deployment region).
- •Key rotation runs automatically every 90 days. Manual rotation is available via Settings → Workspace → Security → Key Management.
- •Secrets stored in the vault are additionally encrypted with a dedicated secrets encryption key, separate from the data DEK.
- •Database backups are encrypted with the same DEK in effect at backup time, retained for 30 days.
In Transit
- •All client-to-server and service-to-service traffic uses TLS 1.2 minimum; TLS 1.3 is negotiated where supported.
- •HTTP Strict Transport Security (HSTS) is enforced with a 1-year max-age including subdomains.
- •Internal service mesh traffic is mTLS authenticated.
- •Webhook and outbound HTTP requests from workflows use TLS 1.2+ and validate server certificates. Self-signed certs must be explicitly allowed per connector.
Authentication
Username / Password
Built-in authentication stores passwords as Argon2id hashes with per-user salts. Minimum password policy is configurable in Settings → Workspace → Security → Password Policy.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| min_length | integer | optional | 12 | Minimum password length. |
| require_uppercase | boolean | optional | true | At least one uppercase letter. |
| require_number | boolean | optional | true | At least one digit. |
| require_symbol | boolean | optional | false | At least one special character. |
| max_age_days | integer | optional | 0 | Days until password expires. 0 = no expiry. |
| history_count | integer | optional | 5 | Number of previous passwords that cannot be reused. |
| lockout_attempts | integer | optional | 10 | Failed attempts before account lock. |
| lockout_duration_min | integer | optional | 15 | Lock duration in minutes. 0 = permanent until admin unlock. |
SSO — SAML 2.0
Configure in Settings → Workspace → Security → Single Sign-On → Add Provider → SAML 2.0.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| idp_entity_id | string | required | — | Identity Provider Entity ID (from IdP metadata). |
| idp_sso_url | string | required | — | IdP SSO redirect URL. |
| idp_certificate | string | required | — | IdP public certificate (PEM). Used to verify assertions. |
| sp_entity_id | string | optional | — | Override the default SP Entity ID if your IdP requires a specific value. |
| name_id_format | enum | optional | emailAddress | urn:...:emailAddress | urn:...:persistent | urn:...:transient |
| attribute_email | string | optional | SAML attribute name containing the user's email. | |
| attribute_name | string | optional | displayName | SAML attribute for display name. |
| sign_requests | boolean | optional | false | Whether to sign outbound AuthnRequests. |
| jit_provisioning | boolean | optional | true | Auto-create user on first SSO login. |
| default_role | string | optional | viewer | Role assigned to JIT-provisioned users. |
SSO — OpenID Connect
Configure in Settings → Workspace → Security → Single Sign-On → Add Provider → OIDC.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| issuer | string | required | — | OIDC issuer URL. Discovery document is fetched from issuer + /.well-known/openid-configuration. |
| client_id | string | required | — | OAuth client ID. |
| client_secret | string | required | — | OAuth client secret. Stored encrypted. |
| scopes | string | optional | openid email profile | Space-separated scope list. |
| claim_email | string | optional | JWT claim containing the user's email. | |
| claim_name | string | optional | name | JWT claim for display name. |
| claim_groups | string | optional | — | JWT claim containing group membership for role mapping. |
| role_mapping | object | optional | — | Map IdP group names to FlowOS role slugs: { "admins": "admin", "devs": "developer" }. |
| jit_provisioning | boolean | optional | true | Auto-create user on first OIDC login. |
Enforcement Modes
| Mode | Key | Behavior |
|---|---|---|
| Optional | optional | SSO is available as an option. Users can still log in with password. |
| Preferred | preferred | SSO is the default login button. Password login still available. |
| Required | required | All users must authenticate via SSO. Password login blocked. Emergency bypass tokens can be issued by owners. |
Multi-Factor Authentication
Configure MFA in Settings → Workspace → Security → MFA Policy.
- •TOTP (Time-based OTP): Compatible with Google Authenticator, Authy, 1Password, and any RFC 6238 app. 30-second window, SHA-1.
- •WebAuthn / Passkey: Hardware security keys (YubiKey, etc.) and platform authenticators (Face ID, Windows Hello). Phishing-resistant.
- •Backup Codes: 10 single-use codes generated at MFA enrollment. Workspace admins can regenerate for a user if codes are lost.
MFA Enforcement
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| require_mfa | boolean | optional | false | When true, all users must enroll MFA before accessing the workspace. Grace period applies to new users. |
| grace_period_hours | integer | optional | 24 | Hours new users have to enroll MFA after first login before being locked out. |
| require_mfa_roles | string[] | optional | [] | Enforce only for specific role slugs (e.g. ["admin","owner"]). Overridden by require_mfa. |
| allowed_methods | string[] | optional | ["totp","webauthn"] | Which MFA methods are accepted: totp | webauthn. |
Session Management
- •Sessions are stored server-side as opaque tokens (not JWT) to allow instant revocation.
- •Default session lifetime is 8 hours of idle time; configurable up to 30 days for "remember me".
- •Absolute session expiry enforced at 30 days regardless of activity.
- •IP-binding is optional — enable in Settings → Security → Session IP Binding to invalidate sessions if the IP changes.
- •Admins can revoke any user's active sessions from the user detail page.
- •All sessions are listed per-user in their profile settings with device, browser, and last-seen data.
IP Allowlisting
Restrict API and UI access to specific IP ranges in Settings → Workspace → Security → IP Allowlist.
// Allowlist entry
{
"label": "HQ Office",
"cidr": "203.0.113.0/24",
"applies_to": ["ui", "api"] // ui | api | webhooks
}Secrets Vault
The secrets vault stores API keys, passwords, and credentials used by workflow nodes and SDK artifacts. Secrets are encrypted at rest with a dedicated DEK and are never exposed in logs or trace output.
- •Create and manage secrets at Settings → Secrets.
- •Access is controlled per-artifact via the access rules system (SDK → Access Rules).
- •In workflow nodes, reference a secret as
{{secrets.SECRET_NAME}}. - •In SDK artifacts, read via
await ctx.secrets.get('SECRET_NAME'). - •Secret values are never returned by the API — only metadata (name, created_at, last_used_at) is readable.
- •Rotation: update the value in the vault; all active workflows pick up the new value on their next run without redeployment.
Compliance
| Standard | Status | Scope |
|---|---|---|
| SOC 2 Type II | Certified | Security, Availability, Confidentiality trust service criteria |
| ISO 27001 | Certified | Information Security Management System |
| GDPR | Compliant | Data processing agreements available; EU data residency option |
| HIPAA | Available | BAA available on Enterprise plan; PHI handling controls enabled |
| PCI DSS Level 4 | Compliant | FlowOS does not store cardholder data; compliant as a service processor |
Security Headers
FlowOS sets the following HTTP security headers on all responses:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Content-Security-Policy: default-src 'self'; ...
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()