Local users authenticate with a username and password stored in Dockhand’s database. This is the simplest authentication method and works without external dependencies.
Creating Users
Via Web UI
Navigate to Settings > Users
Click Add User
Fill in user details:
Username - Unique identifier (required)
Password - Minimum 8 characters (required)
Email - For notifications and password recovery
Display Name - Full name or alias
Click Create User
Via API
curl -X POST http://localhost:8000/api/users \
-H "Content-Type: application/json" \
-H "Cookie: dockhand_session=YOUR_SESSION_TOKEN" \
-d '{
"username": "alice",
"password": "SecureP@ssw0rd123",
"email": "[email protected] ",
"displayName": "Alice Johnson"
}'
{
"id" : 2 ,
"username" : "alice" ,
"email" : "[email protected] " ,
"displayName" : "Alice Johnson" ,
"isAdmin" : false ,
"isActive" : true ,
"createdAt" : "2026-03-04T10:30:00Z"
}
Password Requirements
Dockhand enforces these password requirements:
Minimum Length : 8 characters
Recommended : 12+ characters with mixed case, numbers, and symbols
Hashing Algorithm : Argon2id with these parameters:
Memory cost: 64 MB (65536 KiB)
Time cost: 3 iterations
Parallelism: 1 thread
Hash length: 32 bytes (256 bits)
Passwords are stored in PHC format:
$argon2id$v=19$m=65536,t=3,p=1$[salt]$[hash]
This format is compatible with standard Argon2 implementations.
User Management
List Users
curl http://localhost:8000/api/users \
-H "Cookie: dockhand_session=YOUR_SESSION_TOKEN"
[
{
"id" : 1 ,
"username" : "admin" ,
"email" : "[email protected] " ,
"displayName" : "System Administrator" ,
"mfaEnabled" : true ,
"isAdmin" : true ,
"isActive" : true ,
"isSso" : false ,
"authProvider" : "local" ,
"lastLogin" : "2026-03-04T09:15:22Z" ,
"createdAt" : "2026-01-15T08:00:00Z"
},
{
"id" : 2 ,
"username" : "alice" ,
"email" : "[email protected] " ,
"displayName" : "Alice Johnson" ,
"mfaEnabled" : false ,
"isAdmin" : false ,
"isActive" : true ,
"isSso" : false ,
"authProvider" : "local" ,
"lastLogin" : "2026-03-04T08:45:10Z" ,
"createdAt" : "2026-02-10T14:20:00Z"
}
]
Update User
curl -X PATCH http://localhost:8000/api/users/2 \
-H "Content-Type: application/json" \
-H "Cookie: dockhand_session=YOUR_SESSION_TOKEN" \
-d '{
"email": "[email protected] ",
"displayName": "Alice M. Johnson"
}'
Change Password
Users can change their own password:
curl -X PATCH http://localhost:8000/api/users/2 \
-H "Content-Type: application/json" \
-H "Cookie: dockhand_session=YOUR_SESSION_TOKEN" \
-d '{
"currentPassword": "OldP@ssw0rd",
"password": "NewSecureP@ssw0rd456"
}'
Admins can reset passwords without knowing the current one:
curl -X PATCH http://localhost:8000/api/users/2 \
-H "Content-Type: application/json" \
-H "Cookie: dockhand_session=YOUR_SESSION_TOKEN" \
-d '{
"password": "NewP@ssw0rd789"
}'
Disable User
Disable a user account (preserves data):
curl -X PATCH http://localhost:8000/api/users/2 \
-H "Content-Type: application/json" \
-H "Cookie: dockhand_session=YOUR_SESSION_TOKEN" \
-d '{"isActive": false}'
Disabled users cannot log in but their sessions remain active until they expire.
Delete User
Permanently delete a user account:
curl -X DELETE http://localhost:8000/api/users/2 \
-H "Cookie: dockhand_session=YOUR_SESSION_TOKEN"
Deleting a user removes all their data including audit logs, preferences, and role assignments. This action cannot be undone.
Login Flow
Basic Login
curl -X POST http://localhost:8000/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "alice",
"password": "SecureP@ssw0rd123"
}'
{
"success" : true ,
"user" : {
"id" : 2 ,
"username" : "alice" ,
"email" : "[email protected] " ,
"displayName" : "Alice Johnson" ,
"isAdmin" : false
}
}
Login with 2FA
If the user has 2FA enabled, the initial login returns:
Then submit the TOTP code:
curl -X POST http://localhost:8000/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "alice",
"password": "SecureP@ssw0rd123",
"mfaToken": "123456"
}'
See Two-Factor Authentication for details.
Rate Limiting
Dockhand protects against brute force attacks with rate limiting:
Threshold : 5 failed attempts per IP + username combination
Window : 15 minutes
Lockout : 15 minutes after threshold reached
Response : HTTP 429 with Retry-After header
Rate Limit Response
{
"error" : "Too many login attempts. Please try again in 847 seconds."
}
Rate limits are stored in-memory and reset on server restart.
Session Management
Session Token
After successful login, Dockhand sets a session cookie:
Set-Cookie : dockhand_session=BASE64URL_TOKEN;
Path=/;
HttpOnly;
Secure;
SameSite=Strict;
Max-Age=86400
Name : dockhand_session
Token Format : 32-byte random value, base64url encoded (256 bits entropy)
HttpOnly : Prevents JavaScript access (XSS protection)
Secure : Only sent over HTTPS (production)
SameSite=Strict : Prevents CSRF attacks
Max-Age : Configurable (default 24 hours)
Check Session
curl http://localhost:8000/api/auth/session \
-H "Cookie: dockhand_session=YOUR_SESSION_TOKEN"
{
"authenticated" : true ,
"user" : {
"id" : 2 ,
"username" : "alice" ,
"email" : "[email protected] " ,
"displayName" : "Alice Johnson" ,
"isAdmin" : false
},
"expiresAt" : "2026-03-05T09:15:22Z"
}
Logout
curl -X POST http://localhost:8000/api/auth/logout \
-H "Cookie: dockhand_session=YOUR_SESSION_TOKEN"
This deletes the session from the database and clears the cookie.
Security Considerations
Timing Attack Protection
Dockhand prevents username enumeration via timing attacks:
// Always hash a dummy password even if user doesn't exist
if ( ! user ) {
await hashPassword ( 'dummy' );
return { error: 'Invalid username or password' };
}
This ensures authentication failures take the same time whether the username exists or not.
Password Hash Migration
If you change the Argon2 parameters, existing hashes remain valid. Users are not required to reset passwords.
Session Token Security
Session tokens are:
Generated using crypto.randomBytes() (CSPRNG)
32 bytes = 256 bits of entropy
Base64url encoded for cookie safety
Stored as plain text in database (lookup key)
Not encrypted (entropy makes guessing infeasible)
First User Setup
When authentication is enabled but no admin exists, Dockhand allows creating the first user without authentication:
# This works only when no admin users exist
curl -X POST http://localhost:8000/api/users \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"password": "ChangeMe123!",
"email": "[email protected] "
}'
The first user automatically:
Receives the Admin role
Can create additional users
Is logged in automatically (if authEnabled: true)
This special case is implemented in hooks.server.ts:270:
const noAdminSetupMode = ! ( await hasAdminUser ());
if ( noAdminSetupMode && event . url . pathname === '/api/users' &&
event . request . method === 'POST' ) {
return compressResponse ( event . request , await resolve ( event ));
}
Disabling Local Login
For SSO-only deployments, disable local username/password authentication:
docker run -e DISABLE_LOCAL_LOGIN= true dockhand/dockhand:latest
This:
Removes “Local” from the login provider list
Rejects POST requests to /api/auth/login with provider: local
Forces all users to authenticate via OIDC or LDAP
Prevents password-based attacks
Keep at least one admin account with a known password as a backup before enabling this setting.
Database Schema
Local users are stored in the users table:
CREATE TABLE users (
id INTEGER PRIMARY KEY ,
username TEXT UNIQUE NOT NULL ,
email TEXT ,
password_hash TEXT NOT NULL ,
display_name TEXT ,
avatar TEXT ,
auth_provider TEXT DEFAULT 'local' ,
mfa_enabled BOOLEAN DEFAULT FALSE,
mfa_secret TEXT , -- JSON: {secret, backupCodes}
is_active BOOLEAN DEFAULT TRUE,
last_login TEXT ,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
);
Sessions are stored in the sessions table:
CREATE TABLE sessions (
id TEXT PRIMARY KEY ,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE ,
provider TEXT NOT NULL ,
expires_at TEXT NOT NULL ,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
Source Code Reference
Key implementation files:
src/lib/server/auth.ts - Core authentication logic
src/routes/api/auth/login/+server.ts - Login endpoint
src/routes/api/users/+server.ts - User management CRUD
src/hooks.server.ts - Session validation middleware
src/lib/server/authorize.ts - Permission checks
Next Steps
Two-Factor Auth Add TOTP-based 2FA to user accounts
OIDC/SSO Integrate with your Identity Provider
RBAC Configure role-based access control (Enterprise)
LDAP Connect to Active Directory (Enterprise)