Repository navigation
User Manual v6.2.0
PureAuth is an authentication plugin for Minecraft servers running Paper or compatible Purpur builds. It provides password and PIN login, registration, sessions, CAPTCHA, two-factor authentication, recovery questions, Premium Auto-Login, administrative tools, backups, Discord webhooks and optional PlaceholderAPI integration.
PureAuth 6.2.0 adds a persistent progressive login-lock system. It works alongside passwords, PIN, 2FA and recovery, and retains the existing permanent account lock.
This manual covers:
- Paper/Purpur backend;
- BungeeCord proxy;
- Velocity proxy.
The Paper/Purpur module performs player authentication and supplies commands, the PIN GUI and lock displays. BungeeCord and Velocity only synchronize an already authenticated player after a backend switch. A proxy JAR does not replace the Paper JAR.
- Requirements and compatibility
- Files and installation
- Updating from 6.1.0
- Authentication flow
- Progressive login lock — new in 6.2.0
- PIN authentication
- Commands, 2FA and recovery
- Administration and permissions
- Paper/Purpur configuration
- BungeeCord and Velocity configuration
- Network synchronization and placeholders
- Recommended configurations
- Troubleshooting and security
- Quick reference
| Component | PureAuth 6.2.0 requirement |
|---|---|
| Minecraft server line | 26.2 |
| API used by the plugin | Paper API 26.2.build.117-stable |
| plugin.yml api-version | 26.2 |
| Recommended software | Paper 26.2 or compatible Purpur |
| Plugin bytecode target | Java 21 |
| Effective backend requirement | Java 25 or newer, required by Paper 26.2 |
| server.properties | online-mode=false |
The Paper module uses Paper APIs. Spigot, Bukkit and Folia are not supported targets.
PureAuth rejects a player when a protected backend has online-mode=true. The backend must operate in offline mode because PureAuth supplies its own authentication flow.
| Component | Requirement |
|---|---|
| Build API | BungeeCord API 1.21-R0.4 |
| Plugin bytecode target | Java 21 |
| Recommended network Java | Java 25 or newer |
| Component | Requirement |
|---|---|
| Build API | Velocity API 3.5.1 |
| Plugin bytecode target | Java 21 |
| Recommended network Java | Java 25 or newer |
Standalone Paper/Purpur:
online-mode=false
For a proxy network, the proxy is public while every protected Paper/Purpur backend is offline mode. Firewall backend ports so players cannot bypass the proxy or PureAuth.
PureAuth 6.2.0 files:
pureauth-paper-6.2.0-R.jar
pureauth-bungee-6.2.0-R.jar
pureauth-velocity-6.2.0-R.jar
| Setup | Required JARs |
|---|---|
| Single Paper/Purpur server | Paper JAR only |
| BungeeCord network | Paper JAR on every backend + BungeeCord JAR on proxy |
| Velocity network | Paper JAR on every backend + Velocity JAR on proxy |
Do not install both proxy JARs on one proxy.
- Stop the server.
- Put the Paper 6.2.0 JAR in plugins.
- Set online-mode=false in server.properties.
- Start once to generate plugins/PureAuth/config.yml.
- Stop, review the config and start again.
- Confirm the console reports “PureAuth Paper Module Enabled!”.
SQLite is appropriate for one server:
database:
type: "SQLITE"
- Stop proxy and backends.
- Install the matching proxy JAR and Paper JAR on every protected backend.
- Set every backend to online-mode=false.
- Configure all PureAuth modules with one shared MySQL/MariaDB database and matching table prefix.
- Configure proxy forwarding and firewall backend ports.
- Start the network and verify backend switching after a successful login.
SQLite must not be shared across servers. MySQL/MariaDB is required for a proxy network.
- Stop every PureAuth backend and proxy.
- Back up the database and existing configuration files.
- Replace each 6.1.0 JAR with its matching 6.2.0 JAR.
- Do not mix 6.1.0 and 6.2.0 modules on one network.
- Start a Paper/Purpur backend once to apply the automatic database migration.
- Compare custom config with the 6.2.0 default, especially the progressive_lock section and new messages.
- Restart the network.
- Test password, PIN, 2FA, recovery and controlled failed-login scenarios.
6.2.0 adds this account column:
temporary_lock_until BIGINT DEFAULT 0
Existing UUIDs, usernames, password hashes, PIN hashes, PIN lengths, 2FA data, recovery data and kick history remain intact. The new field is added automatically for SQLite and MySQL/MariaDB.
- Three-stage progressive login locks.
- Default first temporary lock: 30 minutes.
- Default second temporary lock: 24 hours.
- Third max-attempt kick: permanent account lock.
- Persistent timed-lock timestamp that survives restart.
- Timed lock title, subtitle, actionbar and chat countdown.
- Automatic restart of normal login/PIN flow after timer expiry.
- All player commands blocked during temporary and permanent lock.
- Atomic database write for a max-attempt kick and new lock state.
- Admin unlock clears permanent flag, temporary timestamp and history.
- New Polish and English message keys.
- Correct semantic version comparison in updater.
- Five-second updater connection timeout and ten-second request timeout.
- Stable Paper API 26.2.build.117-stable and Velocity API 3.5.1 build dependencies.
Join -> /register or /login -> optional 2FA -> authenticated
Join -> PIN GUI -> optional 2FA -> authenticated
Join -> lock countdown -> timer expires -> normal password/PIN flow
Join -> permanent-lock information -> admin unlock required
PIN is the primary credential for a PIN-enabled account. It does not bypass or replace 2FA. Sessions and Premium Auto-Login cannot bypass active PIN verification.
Before authentication, PureAuth blocks movement, interaction, item actions, inventory manipulation and ordinary chat. During a temporary or permanent login lock, all player commands are also cancelled, including login, register and recovery commands.
One strike is added for:
- wrong password entered through /login;
- wrong PIN submitted in the PIN GUI;
- wrong 2FA login code;
- wrong backup code used during the login 2FA stage.
The connection-level limit is controlled by settings.max_attempts and defaults to 3. Reaching the limit produces one kick and records one persistent kick stage.
| Max-attempt kick count | Default action | Behaviour on later join |
|---|---|---|
| 1 | kick + 30-minute timed login lock | player may join but cannot authenticate |
| 2 | kick + 24-hour timed login lock | player may join but cannot authenticate |
| 3 | kick + permanent account lock | admin unlock is required |
The stage uses persistent kick_count, not the temporary current-connection strike counter. Restarting a server never resets the stage.
After the final failed attempt, PureAuth cancels normal prompts, saves the penalty first and then kicks the player. It uses the temporary_lock_kick message.
When the player reconnects before expiry:
- the connection is allowed;
- the player stays in PureAuth’s protected unauthenticated state;
- title/subtitle, actionbar and optional chat show remaining time;
- every player command is cancelled;
- the usual login timeout is not running;
- the timer checks the expiry every second;
- on expiry, temporary_lock_until is cleared and normal login begins automatically.
The title and actionbar refresh every second. Chat is sent immediately and then every reminder_interval_seconds.
The permanent threshold is settings.security.kicks_before_lock, default 3. At that count PureAuth writes locked=true and clears a temporary timestamp.
Permanent lock has no automatic expiry. It uses title_locked, subtitle_locked and error_locked_chat after join, plus permanent_lock_kick for the kick that creates the permanent lock.
Default:
reset_history_after_successful_login: false
This preserves the intended escalation:
first temporary lock -> successful login -> next max-attempt kick is second temporary lock
Set reset_history_after_successful_login to true only if a correct login should erase the history. With true, successful authentication resets kick_count and temporary_lock_until for an account that is not permanently locked.
| Setting | Behaviour |
|---|---|
| account_lock_enabled: false | max-attempt kicks remain, but no timed or permanent database lock is applied |
| progressive_lock.enabled: false | disables the first/second timed stages; permanent locking still follows kicks_before_lock |
| kicks_before_lock | permanent threshold; values below 1 are safely treated as 1 |
| first_lock_seconds / second_lock_seconds | timed stage duration; values below 0 are safely treated as 0 |
Setting a temporary duration to 0 means the kick does not leave an active timed lock.
/pureauth unlock <player>
/pa unlock <player>
Unlock clears:
- locked;
- temporary_lock_until;
- kick_count.
The runtime lock state is cleared too. An online unauthenticated target is safely disconnected so a clean join starts the normal flow.
After normal login:
/setpin
The user clicks player-head number buttons 0–9, enters a new PIN and repeats it. Default length is six digits; 1–9 is supported. PIN is stored only as a BCrypt hash.
A PIN-enabled account automatically receives the GUI:
Join -> PIN GUI -> optional 2FA -> authenticated
For such account, /login is blocked while PIN support is enabled.
/changepass # PIN account: current PIN -> new PIN -> confirmation
/rempin # removes PIN and restores password login
Closing the PIN login window halts normal login for that connection and tells the player to reconnect. Recovery instructions are shown only if recovery is enabled and configured. Closing PIN setup or PIN change cancels only that operation.
Closing a GUI does not create a strike or progressive lock.
| Command | Description |
|---|---|
| /register [captcha] | creates password account |
| /login | password login; unavailable for active PIN/locked accounts |
| /changepass | password change |
| /changepass | PIN change GUI for PIN accounts |
| /setpin | PIN setup GUI |
| /rempin | restores password login |
| /premium | enables IP-trust Premium Auto-Login |
| /cracked | disables Premium Auto-Login |
| /2fa setup | starts 2FA setup |
| /2fa remove | removes 2FA after code confirmation |
/2fa recovery
|
uses backup code where permitted |
| /recovery setup/remove/start/pass | recovery management and recovery |
All entries above are blocked while a temporary or permanent login lock is active.
Password -> 2FA -> authenticated
PIN GUI -> 2FA -> authenticated
Set up 2FA with /2fa setup after login. A player adds the generated secret to an authenticator app and enters the current six-digit code. Backup codes are created after setup.
Wrong 2FA login codes participate in max-attempt strike counting. Correct 2FA does not reset persistent escalation history unless reset_history_after_successful_login is true.
Enable:
settings:
recovery:
enabled: true
Commands:
/recovery setup
/recovery remove
/recovery start
/recovery pass <new_password> <new_password>
For a PIN account, successful password recovery atomically saves the new password and removes the forgotten PIN. Recovery cannot run while a progressive temporary/permanent lock is active, because every player command is intentionally blocked during that state.
Administrative entry points:
/pureauth
/pa
| Command | Permission | Notes |
|---|---|---|
| /pureauth reload | pureauth.admin.reload | reloads config and cached PIN GUI elements |
| /pureauth stats | pureauth.admin.stats | statistics |
| /pureauth info | pureauth.admin.info | displays account and lock information |
| /pureauth unlock | pureauth.admin.unlock | clears all lock state/history |
| /pureauth unregister | pureauth.admin.unregister | deletes account |
| /pureauth register | pureauth.admin.register | registers account |
| /pureauth forcelogin | pureauth.admin.forcelogin | completes online login |
| /pureauth setpassword | pureauth.admin.setpassword | replaces password |
| /pureauth setpin | pureauth.admin.pin | sets absent PIN |
| /pureauth changepin | pureauth.admin.pin | replaces PIN |
| /pureauth rempin | pureauth.admin.pin | removes PIN |
| /pureauth accounts | pureauth.admin.accounts | lists IP accounts |
| /pureauth location <login | register> | pureauth.admin.location |
Info output includes:
- Locked;
- Temporary Lock Until;
- Failed Login Kicks;
- PIN Enabled;
- Recovery and 2FA state.
If settings.safe_mode is true, sensitive account commands are console-only. PINs and passwords are redacted from administration audit logs.
Declared permissions:
pureauth.admin
pureauth.admin.reload
pureauth.admin.stats
pureauth.admin.unlock
pureauth.admin.unregister
pureauth.admin.register
pureauth.admin.forcelogin
pureauth.admin.info
pureauth.admin.setpassword
pureauth.admin.accounts
pureauth.admin.location
pureauth.admin.pin
pureauth.admin.guard
File:
plugins/PureAuth/config.yml
database:
type: "SQLITE" # SQLITE or MYSQL
pool:
maximum_pool_size: -1
minimum_idle: -1
connection_timeout_ms: 10000
idle_timeout_ms: 600000
max_lifetime_ms: 1800000
mysql:
host: "localhost"
port: 3306
database: "minecraft"
username: "root"
password: "password"
table_prefix: "pureauth_"
SQLite data is stored locally at plugins/PureAuth/database.db. Use a dedicated MySQL/MariaDB user for production networks.
settings:
security:
account_lock_enabled: true
kicks_before_lock: 3
progressive_lock:
enabled: true
first_lock_seconds: 1800
second_lock_seconds: 86400
reset_history_after_successful_login: false
reminder_interval_seconds: 10
display:
title: true
chat: true
actionbar: true
login_timeout: 60
max_attempts: 3
| Key | Default | Exact effect |
|---|---|---|
| max_attempts | 3 | wrong attempts in one connection before kick |
| account_lock_enabled | true | stores persistent timed/permanent penalties |
| kicks_before_lock | 3 | kick count that becomes permanent |
| progressive_lock.enabled | true | enables stage one and stage two |
| first_lock_seconds | 1800 | first stage, 30 minutes |
| second_lock_seconds | 86400 | second stage, 24 hours |
| reset_history_after_successful_login | false | clears or preserves history after successful login |
| reminder_interval_seconds | 10 | interval between chat reminders |
| display.title | true | title/subtitle countdown |
| display.chat | true | lock chat notices |
| display.actionbar | true | actionbar countdown |
A reminder interval below 1 is safely treated as 1 second.
Add/retain these keys in messages.en and messages.pl:
temporary_lock_title
temporary_lock_subtitle
temporary_lock_chat
temporary_lock_actionbar
temporary_lock_kick
temporary_lock_expired
permanent_lock_kick
lock_database_error_kick
Time placeholders:
| Placeholder | Meaning |
|---|---|
| %time% | formatted remaining duration, such as 30m 0s or 24h 0m 0s |
| %seconds% | remaining whole seconds |
The existing title_locked, subtitle_locked and error_locked_chat keys remain the permanent-lock join display.
settings:
pin:
enabled: true
length: 6
sessions:
enabled: true
time_minutes: 1
premium_login:
enabled: true
require_same_ip: true
recovery:
enabled: true
admin_guard:
enabled: true
update_checker: true
The full PIN GUI configuration, player-head textures, digit item names/lore, input indicators, controls and sounds stay under settings.pin.gui. PIN supports 1–9 digits and uses six safely for invalid values.
The update checker now has a five-second connection timeout and a ten-second request timeout. It compares semantic versions and will not announce an older Modrinth version as an update.
settings:
discord:
enabled: false
events:
failed_attempts: true
account_locked: true
One max-attempt kick triggers failed_attempts. A temporary or permanent stage also triggers account_locked.
File:
plugins/PureAuth/config.yml
database:
type: MYSQL
mysql:
host: "localhost"
port: 3306
database: "pureauth"
username: "root"
password: ""
table_prefix: "pureauth_"
network:
mode: "LOCAL"
Use the same database and prefix as Paper. The Bungee module holds authenticated UUIDs for active connections and forwards their success after backend change. It has no PIN GUI, timed lock display or player commands.
File:
plugins/pureauth/config.yml
database:
driver: "MYSQL"
host: "localhost"
port: "3306"
database: "pureauth"
username: "root"
password: ""
prefix: "pureauth_"
network:
mode: "LOCAL"
Velocity uses driver and prefix rather than the Paper/Bungee type and mysql.table_prefix keys. It registers the same synchronization channel but does not provide GUI/commands.
Paper sends this after successful backend authentication:
PLAYER_LOGIN:<uuid>
The proxy sends this after a backend switch:
AUTH_SUCCESS:<uuid>
Reliable synchronization requires matching PureAuth 6.2.0 modules, one correct proxy JAR, Paper on every protected backend, shared MySQL/MariaDB, matching prefix, working plugin messaging, consistent forwarding and protected backend ports.
LOCAL mode keeps authenticated UUIDs in one proxy’s memory until disconnect. It is not a multi-proxy Redis implementation.
PlaceholderAPI is optional on Paper:
%pureauth_logged% -> true / false
%pureauth_has_2fa% -> true / false
database:
type: "SQLITE"
pool:
maximum_pool_size: 1
minimum_idle: 1
settings:
security:
account_lock_enabled: true
kicks_before_lock: 3
progressive_lock:
enabled: true
first_lock_seconds: 1800
second_lock_seconds: 86400
reset_history_after_successful_login: false
reminder_interval_seconds: 10
display:
title: true
chat: true
actionbar: true
pin:
enabled: true
length: 6
Use only in a private test environment:
settings:
security:
progressive_lock:
first_lock_seconds: 5
second_lock_seconds: 10
Restore production durations after testing.
Use MySQL/MariaDB for every module. Keep the account-lock policy consistent on all Paper backends, otherwise a player may receive a different lock policy after a switch.
Expected in 6.2.0. Rejoin to see the countdown or wait until it expires. The player can stay connected while the temporary block counts down.
Expected. Every player command, including login/register/recovery, is blocked while the lock is active.
Expected for first and second stage. The kick ends the failed attempt. A later connection is allowed only so the player can see the remaining lock time.
Check:
/pureauth info <player>
/pureauth unlock <player>
Permanent lock requires administrator action. The unlock command also clears escalation history.
Expected when its timestamp is still future. This persistence is intentional. The next join or online timer clears it after expiry.
Inspect reset_history_after_successful_login. Default false preserves escalation. True resets history after successful authentication.
Check:
settings.security.progressive_lock.display.title
settings.security.progressive_lock.display.chat
settings.security.progressive_lock.display.actionbar
Then confirm the matching language block contains every temporary_lock message key.
PureAuth fails closed: it kicks rather than allowing an unrecorded max-attempt penalty. Check database connection, credentials, table prefix, firewall and pool timeout.
Check settings.pin.enabled, account PIN state and the Paper JAR. A proxy JAR alone never opens a GUI.
- Keep account_lock_enabled and progressive_lock.enabled enabled in production.
- Keep default 3 attempts, 30 minutes and 24 hours unless policy requires different values.
- Keep reset_history_after_successful_login false when repeated abuse should escalate.
- Use strong passwords even with PIN.
- Enable 2FA for staff and Admin Guard.
- Enable safe mode for sensitive commands.
- Use a dedicated MySQL/MariaDB user for networks.
- Protect backend ports and forwarding secrets.
- Encourage recovery setup and offline 2FA backup-code storage.
- Back up the database before updates and test restoration.
- Never publish database passwords or Discord webhooks.
3 wrong attempts -> kick
1st max-attempt kick -> 30-minute login lock
2nd max-attempt kick -> 24-hour login lock
3rd max-attempt kick -> permanent account lock
settings:
max_attempts: 3
security:
account_lock_enabled: true
kicks_before_lock: 3
progressive_lock:
enabled: true
first_lock_seconds: 1800
second_lock_seconds: 86400
reset_history_after_successful_login: false
/pureauth info <player>
/pureauth unlock <player>
/pa info <player>
/pa unlock <player>
Standalone: Paper JAR on Paper/Purpur
Bungee: Paper JAR on every backend + BungeeCord JAR on proxy
Velocity: Paper JAR on every backend + Velocity JAR on proxy
PureAuth 6.2.0 is developed by SzymeX Dev Studio.