Authentication
Stylus SFTP Server supports four authentication provider types — XML flat-file, JDBC, LDAP / Active Directory, and public key. Any number of provider instances can be enabled at the same time; the server probes them all in parallel on every login and admits the user if exactly one returns Success. Password providers use BCrypt hashing throughout. Public-key authentication is available with every provider.
Traditional installs continue to work: enabling a single JDBC or LDAP provider gives the same behavior as before — one probe, one result — with no visible change. The probe-all-parallel behavior only matters when more than one provider is enabled, typically because the operator supports multiple customer directories on the same instance.
Login orchestrator (probe-all-parallel)
Every login request is handed to the Login Orchestrator. It walks
the list of enabled providers (rows in
sftp_authentication_providers, ordered by chain_order)
and fires a probe against each one on a virtual-thread executor. The
orchestrator then reduces the results:
- Zero successes — login rejected
(
AUTH_FAILURE). - One success — login accepted
(
AUTH_SUCCESS, with the winningprovider_idin the audit detail). - Two or more successes — login rejected as
ambiguous (
AUTH_AMBIGUOUS). Usernames must be unique across all enabled providers.
Timeouts are enforced at two levels: 3 seconds per provider (a slow
LDAP directory can't stall the whole login), and 10 seconds globally.
A provider that exceeds its per-provider budget contributes
AUTH_PROVIDER_TIMEOUT to the audit stream but does not fail
the login on its own — another responsive provider can still admit
the user.
Per-provider circuit breaker
The orchestrator keeps a small state machine per provider to keep a temporarily broken directory from dragging every login attempt down to the timeout limit. Defaults:
- 3 consecutive timeouts or 5 consecutive errors within 60 seconds opens the breaker.
- An open breaker returns
Timeoutimmediately (no network call, no thread spent). - After 30 seconds in the OPEN state, the next probe is allowed through; a Success or BadCredentials reply closes the breaker.
Both open and close transitions emit audit events
(AUTH_PROVIDER_CIRCUIT_BROKEN,
AUTH_PROVIDER_CIRCUIT_RECOVERED). The thresholds are tunable
per provider via circuit.open_threshold_timeouts and
circuit.half_open_after_seconds config properties.
LDAP post-authentication group refresh
When an LDAP provider returns Success, the orchestrator captures the
user's memberOf attributes and refreshes the user's rows in
sftp_user_groups to match. Groups in Stylus SFTP Server (see
Organizations & Groups) are matched
against LDAP CNs by name, case-insensitively. A user who has been added to
a new AD group sees the effect on their next login — no admin
intervention required.
If a proposed group addition would introduce a mount conflict for the user,
that specific addition is skipped and the audit stream records
AUTH_MOUNT_CONFLICT_SUPPRESSED. Login still proceeds with the
safe pre-existing memberships. This keeps a badly-configured customer
directory from locking a user out entirely.
| Provider | Config Value | Best For |
|---|---|---|
| XML flat file | xml |
Small deployments with a handful of users |
| JDBC database | jdbc |
Dynamic user management via admin tools |
| LDAP / Active Directory | ldap |
Enterprise environments with centralized directory |
| Public key only | publickey |
Machine-to-machine transfers, no passwords |
1. XML Provider
Set <provider>xml</provider> in
conf/sftp-server.xml. User accounts are defined in
conf/users.xml. Each <user> element
contains a username, an optional BCrypt password hash, and optional
public keys. This provider is ideal for simple deployments with a
small number of users.
Example users.xml
<users xmlns="http://www.xmlpipelineserver.com/sftp/">
<user>
<username>alice</username>
<password>$2a$12$replaceWithRealBcryptHash</password>
</user>
<user>
<username>bob</username>
<public-keys>
<public-key>ssh-rsa AAAAB3... bob@workstation</public-key>
</public-keys>
</user>
</users>
2. JDBC Provider
Set <provider>jdbc</provider> in
conf/sftp-server.xml. User accounts are stored in the
sftp_users database table, which is auto-created on first
boot. Users are managed through the admin tools (CLI, Swing GUI, or
Web Console) rather than by editing files directly.
Supported Databases
| Database | Notes |
|---|---|
| H2 (default) | Embedded, zero-config. AUTO_SERVER=TRUE allows
simultaneous access from the server and admin tools. |
| MySQL | Driver downloaded at configuration time (not bundled). |
| PostgreSQL | Driver downloaded at configuration time (not bundled). |
| SQL Server | Microsoft JDBC Driver. Downloaded at configuration time (not bundled). |
Key Characteristics
- Passwords hashed with BCrypt (cost factor 12).
- Public keys stored in the
sftp_account_keystable. - Connection pooling via HikariCP.
- Account caching via Caffeine (10,000 entries, 300-second TTL).
3. LDAP / Active Directory
Set <provider>ldap</provider> in
conf/sftp-server.xml. Authentication uses a two-step
LDAP bind: a service account searches for the user's Distinguished
Name, then the server performs a bind with the user's own credentials.
The password is verified by the LDAP server — no password hash
is stored locally.
LDAP Configuration
Add an <ldap> block inside conf/sftp-server.xml:
<ldap>
<url>ldap://dc01.example.com:389</url>
<base-dn>ou=users,dc=example,dc=com</base-dn>
<bind-dn>cn=sftpservice,ou=serviceaccounts,dc=example,dc=com</bind-dn>
<bind-password>servicePassword</bind-password>
<username-attribute>sAMAccountName</username-attribute>
<user-filter>(objectClass=user)</user-filter>
<public-key-attribute>sshPublicKey</public-key-attribute>
<access-groups>
<group>SFTP-Writers</group>
<group>IT-FileTransfer</group>
</access-groups>
<read-only-groups>
<group>SFTP-Readers</group>
<group>Auditors</group>
</read-only-groups>
</ldap>
Public Keys via LDAP
If the <public-key-attribute> element is configured,
the server reads SSH public keys from the specified LDAP attribute
(e.g. sshPublicKey) during the service-account search
phase.
Access Control via AD/LDAP Groups
When LDAP is the active provider, access rights can be derived from the
user's memberOf attribute by configuring
<access-groups> and <read-only-groups>
in the <ldap> block. Each contains one or more
<group> elements with the CN of an AD/LDAP group.
| Configuration | Effect |
|---|---|
<access-groups> |
User can connect; home directory is read-write. |
<read-only-groups> |
User can connect; home directory is read-only (download only). |
Access Resolution Rules
- If the user matches any group in
<read-only-groups>, access is read-only (most restrictive wins). - If the user matches any group in
<access-groups>, access is read-write. - If no configured groups match (or no groups are configured), access
falls back to the
<default-access>setting inconf/filesystem-config.xml. - Group matching is case-insensitive on the CN value.
- Home directories are auto-created on first login from the
${username}template.
get-ldap-groups / set-ldap-groups).
Changes are hot-swappable — no restart required.
4. Public Key Provider
Public-key-only authentication. Keys can be stored in
conf/keys.xml or in the JDBC sftp_account_keys
table (depending on the active provider). Keys use the standard
OpenSSH authorized_keys format.
Supported Key Types
- RSA (2048-bit minimum recommended)
- ECDSA — P-256, P-384, P-521
- Ed25519
Admin CLI Key Management
| Command | Description |
|---|---|
add-key |
Associate a public key with a user account. |
list-keys |
List all public keys for a user. |
remove-keys |
Remove one or all public keys from a user. |
generate-keypair |
Generate a new key pair and register the public key. |
Password Hashing
All password-based providers use BCrypt with a cost factor of 12, implemented via the Bouncy Castle library. BCrypt is a deliberately slow, salted hashing algorithm designed to resist brute-force and rainbow-table attacks.
Generating BCrypt Hashes Externally
If you need to generate a BCrypt hash outside of the admin tools
(for example, to populate users.xml manually), you can
use the htpasswd utility:
htpasswd -bnBC 12 "" password | tr -d ':\n'
Username Validation
All usernames are validated against the following rules before authentication is attempted:
- Pattern:
^[a-zA-Z0-9._-]{1,100}$ - Only letters, digits, periods, underscores, and hyphens are allowed.
- No path separators (
/or\) and no parent-directory sequences (..). - Maximum length: 100 characters.
Usernames that fail validation are rejected immediately without consulting the authentication provider.
Multi-Factor Authentication (MFA)
Stylus SFTP Server supports time-based one-time password (TOTP) multi-factor authentication as defined by RFC 6238. When MFA is enabled, users must provide both their password and a 6-digit verification code from an authenticator app (such as Google Authenticator, Microsoft Authenticator, Authy, or FreeOTP).
How It Works
SFTP Connections (Keyboard-Interactive)
MFA for SFTP uses the SSH keyboard-interactive authentication method with a two-round challenge-response flow:
- Round 1 — the server prompts for a password. The password is verified against the active authentication provider (JDBC, LDAP, or XML).
- Round 2 — if the user has MFA enrolled, the server prompts for a verification code. The user enters the 6-digit code from their authenticator app (or a one-time recovery code).
If the user does not have MFA enrolled and the policy is
optional, authentication completes after round 1.
Web File Transfer Portal
When an MFA-enrolled user logs into the Web File Transfer Portal, the login
response includes mfaRequired: true. The portal then displays a
verification code form. After the user enters a valid TOTP code, the session
is fully authenticated. See the
Web Portal MFA section for details.
Public-Key Authentication
Public-key authentication bypasses MFA entirely. SSH keys are already a possession factor (something you have), making a second factor redundant. This is consistent with standard SSH server behavior and ensures that automated file-transfer clients using key-based authentication are not disrupted.
MFA Policy
The MFA policy is configured in the <mfa> block of
sftp-server.xml. See the
Security chapter for the full
configuration reference.
| Policy | Behavior |
|---|---|
disabled |
MFA is off. Password-only authentication (default). |
optional |
Users who have enrolled in MFA are prompted for a code. Users without MFA can log in with just a password. |
required |
All users must enroll in MFA. Users without MFA are rejected at login. |
Enrollment
MFA enrollment is a two-step process managed through the admin tools or self-service in the Web Portal:
- Begin enrollment — generates a TOTP secret and
produces an
otpauth://URI that can be scanned as a QR code by any authenticator app. Eight one-time recovery codes are also generated. - Confirm enrollment — the user enters a valid 6-digit code from their authenticator app. This verifies that the app is correctly configured before activating MFA.
Recovery Codes
Each enrollment generates 8 one-time recovery codes. These can be used in place of a TOTP code if the user loses access to their authenticator app. Each recovery code can be used exactly once. Recovery codes are stored as BCrypt hashes — the server never stores them in plain text.
Provider Compatibility
| Provider | MFA Support | Notes |
|---|---|---|
| JDBC | Full | TOTP secret stored in sftp_user_mfa table. |
| LDAP | Full | LDAP verifies the password; TOTP secret stored locally in the database. A user row is lazily created on first login. |
| XML | Full | XML verifies the password; TOTP secret stored locally. Same lazy user row creation as LDAP. |
| Public key only | N/A | Public-key authentication bypasses MFA. |
Account Lockout
After a configurable number of consecutive failed login attempts,
an account is temporarily locked. Lockout state is tracked in the
sftp_lockout database table. Both the failure threshold
and the lockout duration are configurable.
See the Security chapter for full
lockout configuration details, including the
unlock-user admin command.