Files
huntingclubroster/AUTHENTICATION.md
T
2026-10-18 19:27:17 -04:00

156 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Authentication changes and deployment
Login, signup, password changes, and password reset now use `classes/AuthService.php`.
The existing PHP/XSL presentation structure is retained. Signup creates an active
`User` account; it does not grant administrator access or club membership.
## Before using this version against your database
1. Back up the database and use a maintenance window for the schema update.
2. Use 64-bit PHP 8.3 or newer. Run `composer install` and
`composer check-platform-reqs` with the same PHP configuration used by the web server.
3. Configure database and SMTP settings as described below.
4. From the project directory, run `php bin/migrate-auth.php` once. It is safe to
rerun. It adds the session version, password reset storage, rate limit storage,
and refreshes `view_users`. It preserves accounts and password hashes. MySQL
schema changes may briefly lock tables; this is not a web-request migration.
5. Serve the site over HTTPS and verify the private-directory blocking rules.
6. Test delivery of a reset email to an account you control.
The migration expects the schema in `tables/users.php`, including
`user_password_hash`, `user_status`, and `user_last_login`. It refuses to continue
if those fields are missing. Do not rebuild the database to fix an older schema;
map and migrate its existing account fields while preserving the data.
On October 18, 2026, the migration was applied to the configured development
database, `192.168.1.190/huntingclubroster`, after a complete backup was restored
and checked locally. Existing account data and original table row counts were
preserved. Signup, login, logout and password reset were verified using the local
application against that database; the SMTP provider accepted a real test reset
email over TLS. The temporary account and restored database copy were removed.
The public hostname did not resolve from the verification machine, so these
results do not confirm deployment or operation of the public site. See
[database verification](DATABASE-VERIFICATION.md) for the backup and results.
## Settings
Your existing settings were preserved in ignored `xml/settings.local.xml`.
The tracked `xml/settings.xml` now contains no credentials. Settings are loaded in
this order: `HCR_SETTINGS_FILE`, local settings if present, then tracked defaults.
Environment variables override individual fields:
| Variable | XML field |
| --- | --- |
| `HCR_ENVIRONMENT` | `Environment` |
| `HCR_DB_HOST` | `DatabaseServer` |
| `HCR_DB_PORT` | `DatabasePort` (default 3306) |
| `HCR_DB_NAME` | `DatabaseName` |
| `HCR_DB_USER` | `DatabaseUser` |
| `HCR_DB_PASSWORD` | `DatabasePassword` |
| `HCR_SMTP_HOST` | `SmtpServer` |
| `HCR_SMTP_PORT` | `SmtpPort` |
| `HCR_SMTP_USER` | `SmtpUserName` |
| `HCR_SMTP_PASSWORD` | `SmtpPassword` |
| `HCR_SMTP_FROM` | `SmtpFromEmail` (defaults to SMTP username) |
| `HCR_APPLICATION_URL` | `ApplicationUrl` |
The configured application URL is **https://app.myhuntingclubroster.com**. It must
be the trusted HTTPS application directory, without `/main.php`, a query string,
or a fragment. Request `Host` headers are never used to construct email links.
SMTP uses implicit TLS on port 465 and required STARTTLS on other ports,
including 587. Certificate verification remains enabled. The sender must be
authorized by your SMTP provider. For production, use an external settings file
outside the document root or environment variables and set `Environment` to
`Production`. Production requests require the web server to set `HTTPS=on`.
If TLS terminates at a reverse proxy, configure that mapping in the web server;
the application does not trust arbitrary forwarded headers.
Moving credentials out of tracked files does not remove them from Git history.
Rotate the previously committed database/SMTP credentials, webhook, and any
account still using the former hardcoded initial administrator password.
## Behavior and protections
- Login accepts the email shown by the form, with username compatibility.
SQL uses bound parameters. Passwords use PHP's password hashing API.
- Signup validates names, email, username, duplicate accounts, password length,
and confirmation on the server. Privilege fields submitted by the browser are
ignored. New passwords must be 12–72 bytes to avoid bcrypt truncation.
- Password reset emails contain 256-bit random links. Only token hashes are
stored; links expire after 30 minutes. Completing a reset consumes every
outstanding token for that account inside a transaction. Requesting a reset
does not change the password. Failed email delivery removes the unsent token.
- Reset requests return the same public message for unknown, inactive,
throttled, and undeliverable accounts. Delivery errors go to the server log.
- Password changes require the current password and matching confirmation.
Successful changes and resets revoke all old sessions. Administrator role and
status changes also revoke sessions. Account status and privileges are checked
against the database on each authenticated request.
- Sessions rotate on login/logout, expire after 30 idle minutes, and use
HttpOnly/SameSite cookies plus Secure cookies on HTTPS.
- CSRF checks cover public authentication forms and authenticated POST actions.
Existing application forms and same-origin AJAX requests receive the token.
- Database-backed limits apply across new cookies: 10 login attempts per account
and 50 per IP in 15 minutes; 3 reset emails per account and 10 per IP per hour;
5 signup attempts per IP per hour. Reset submissions and password changes are
limited separately.
- Routing uses an explicit allowlist. Administrative screens, account editing,
resets for other users, and destructive maintenance actions require an
administrator role on the server. General helper methods are not endpoints.
System administrator names in the old settings file no longer override the
role stored in the database.
- Errors are logged without recursively opening a database connection or
emailing from the error handler. Visitors receive a generic failure message.
Run `php bin/cleanup-auth.php` daily using your normal server scheduler to remove
expired reset tokens and old rate-limit rows. No scheduler was installed here.
Exclude query strings containing reset tokens from retained access/analytics
logs where your hosting supports it.
## Hosting and setup scripts
Apache `.htaccess` and IIS `web.config` block private application directories.
Apache needs the rewrite module and permission to apply these rules. On Nginx,
configure equivalent denies for `xml`, `classes`, `vendor`, `tables`, `views`,
`bin`, `migrations`, `tests`, `reports`, `nbproject`, and hidden files. Verify that
`/xml/settings.local.xml` and setup scripts cannot be downloaded or executed.
The production web server configuration has not been exercised by local tests.
Setup scripts also refuse HTTP execution independently. The system rebuild is
disabled unless `HCR_ALLOW_REBUILD=yes`; it requires a unique
`HCR_INITIAL_ADMIN_PASSWORD` of 12–72 bytes before doing any rebuilding. It is
destructive and is not the authentication migration.
For local development only: `php -S 127.0.0.1:8080 bin/dev-router.php`. This router
serves the entry point and static assets while denying private files. Set the
environment to `Development` for plain HTTP development. Never use PHP's built-in
server for production.
## Dependencies and verification
Composer requirements now explicitly declare the PHP version and authentication
extensions. PhpSpreadsheet was upgraded from 5.2.0 to 5.10.0 after the dependency
audit found advisories. Compatible ZipStream and Composer PCRE dependencies were
updated, and the lock file and installed autoloader were synchronized. Existing
auxiliary TCPDF library files were preserved. Composer reported no known
dependency security advisories after the update.
`tests/auth_integration.php` creates and drops a randomly named database on the
server explicitly supplied by `HCR_TEST_DSN`, `HCR_TEST_USER`, and
`HCR_TEST_PASSWORD`. It never reads the application's database credentials.
Run `php tests/auth_integration.php` (or `composer test:auth`). The test server
account needs permission to create and drop databases. Use an isolated server.
`tests/auth_http.py` runs the real forms against a temporary PHP development
server and disposable database. Supply the same test variables plus
`HCR_DB_HOST`, `HCR_DB_PORT`, `HCR_DB_USER`, and `HCR_DB_PASSWORD` for that isolated
server. `PHP_TEST_COMMAND` can hold a JSON argument list for a PHP executable and
extension options; it defaults to `["php"]`. Run `python tests/auth_http.py`.
Email delivery is disabled in HTTP tests; the service tests capture reset emails
and simulate transport failure. Tests cover schema migration, signup/login,
validation, SQL injection input, CSRF, authorization, token expiry/reuse,
throttling, session revocation, logout, and rendering.
A wider review of unrelated roster/reporting logic and deployment penetration
testing are outside this authentication change.