silverday / nexus-dropin-user
Security-first, framework-agnostic PHP 8.2+ drop-in user management module
Requires
- php: ^8.2
- ext-pdo: *
- psr/log: ^3.0
- web-auth/webauthn-lib: ^4.8
README
Security-first, framework-agnostic PHP 8.2+ user management module for MariaDB/MySQL.
What this is
A drop-in auth/user module you can mount into an existing PHP app.
- Not a full framework
- Not a standalone IAM server
- Built for embedding and extension
What you get
- Registration + email verification
- Login (username or email) + secure sessions + remember-me
- Password reset + password change
- Role checks (
user,admin,super_admin) with permission checks (can()) - TOTP + recovery codes + step-up verification
- Passkeys (WebAuthn) with safe default disabled mode
- Google OIDC login hooks
- Session/device listing and revocation
- Audit logging for security-sensitive actions
- HTML modules and JSON endpoints
Quick start
- Install dependencies:
composer install
- Configure database (environment mode):
export NEXUS_DB_DSN='mysql:host=127.0.0.1;port=3306;dbname=nexus_user;charset=utf8mb4' export NEXUS_DB_USER='root' export NEXUS_DB_PASS=''
- Run migrations:
php migrations/run.php
- Start demo router:
php -S 127.0.0.1:8080 examples/minimal_router.php
Lightweight host-app demo
If you want a cleaner, app-like example (homepage + register/login + restricted user/admin pages), use:
- Configure the demo in this file:
examples/config/module_webapp.config.php
Set at least:
db_dsndb_userdb_password
- Run migrations (uses the same config file by default if
NEXUS_CONFIG_FILEis not set):
php migrations/run.php
- Start the demo app:
php -S 127.0.0.1:8081 examples/module_webapp.php
Then open http://127.0.0.1:8081.
Optional: use a different config file via environment variable:
NEXUS_CONFIG_FILE=/absolute/path/to/your.config.php php -S 127.0.0.1:8081 examples/module_webapp.php
This example demonstrates the intended drop-in style:
- include the module
- instantiate core services once
- protect host-app routes using session + role checks
Install in a host app
Use Composer to install this module into your application. This is the recommended approach for production usage.
If the package is available on Packagist:
composer require silverday/nexus-dropin-user:^0.2.1
If you want to install directly from GitHub (VCS source):
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/SilverDay/nexus.git"
}
]
}
Then run:
composer require silverday/nexus-dropin-user:^0.2.1
Manual unzip/copy into your app directory is possible for experiments, but not recommended because dependency updates, autoloading, and version pinning are harder to maintain.
Configuration modes
The module now supports both environment-based and file-based configuration.
A) Environment variables
Used by default when no config file is provided.
Primary keys:
NEXUS_DB_DSNNEXUS_DB_USERNEXUS_DB_PASSNEXUS_TOTP_KEYNEXUS_GOOGLE_OIDC_CLIENT_IDNEXUS_GOOGLE_OIDC_CLIENT_SECRETNEXUS_GOOGLE_OIDC_REDIRECT_URINEXUS_PASSKEY_WEBAUTHN_ENABLED
B) Config file (app-local by default)
The demo router and migration runner look for an app-local config file first:
examples/minimal_router.phpdefaults toexamples/config/module.config.phpmigrations/run.phpdefaults toexamples/config/module.config.php
Override options (in precedence order):
NEXUS_CONFIG_FILE_PATHconstant in your entrypoint- Web-server scoped
$_SERVER['NEXUS_CONFIG_FILE'] - Process environment variable
NEXUS_CONFIG_FILE - App-local default file path
You can still use NEXUS_CONFIG_FILE when useful, but for multi-app servers prefer app-local files or per-app entrypoint constants.
Migration runner also accepts an explicit config path argument:
php migrations/run.php /path/to/your/module.config.php
See the template in examples/config/module.config.php.
Supported config keys include:
- Core config:
db_dsn,db_user,db_password,from_email,from_name - Security behavior:
secure_cookies,same_site,ip_binding_mode,bind_user_agent - Phase-2 toggles/secrets:
totp_key,google_oidc_*,passkey_webauthn_enabled - Mail transport:
mail_transport,smtp_host,smtp_port,smtp_username,smtp_password,smtp_encryption,smtp_timeout_seconds - Mail template files:
email_template_locale,email_template_roots,verification_link_template,admin_registration_notify_to - Optional mail fallback templates:
email_templates - UI fields:
profile_fields
Email text and transport configuration
You can configure both what is sent and how it is sent from the config file.
Transport options:
mail_transport = null(default; no mail sent)mail_transport = php(uses PHPmail())mail_transport = smtp(uses SMTP with optional AUTH/STARTTLS)
Example transport config:
'mail_transport' => 'smtp', 'smtp_host' => 'smtp.example.com', 'smtp_port' => 587, 'smtp_username' => 'smtp-user', 'smtp_password' => 'smtp-password', 'smtp_encryption' => 'tls', // tls|ssl|none 'smtp_timeout_seconds' => 10, 'verification_link_template' => 'https://app.example.com/verify-email?token={{token}}', 'admin_registration_notify_to' => 'security@example.com,ops@example.com',
Email text is file-based by default. Template file lookup is:
<root>/<locale>/<template_name>.subject.txt<root>/<locale>/<template_name>.body.txt- fallback to language-only locale (for example
defromde-DE) - fallback to
en
Example file-template config:
'email_template_locale' => 'de-DE', 'email_template_roots' => [ __DIR__ . '/../../templates/email', '/opt/myapp/mail-templates', ],
Default template example:
templates/email/en/verify_email.subject.txttemplates/email/en/verify_email.body.txt
Default mail notification templates shipped:
verify_emailpassword_reset_requestedpassword_reset_completedadmin_new_user_registered
Template file contents are preformatted text with placeholders:
Please verify your account
Hi {{real_name}},
Your verification token: {{token}}
Optional array fallback when no file template exists:
'email_templates' => [ 'verify_email' => [ 'subject' => 'Please verify your account', 'text' => "Hi {{real_name}},\n\nYour verification token: {{token}}", ], ],
Available placeholders for verify_email:
{{token}}{{verify_link}}{{username}}{{email}}{{real_name}}
Available placeholders for admin_new_user_registered:
{{user_id}}{{username}}{{email}}{{real_name}}{{source_ip}}{{request_id}}
When admin_registration_notify_to is set (array or comma-separated list), admin notifications are emitted on successful registration using the admin_new_user_registered template.
Host app database reuse (drop-in embedding)
For host-app embedding, the config file can provide a shared PDO instance via pdo.
If pdo is present, the demo router and migration runner reuse it instead of opening a separate DB connection.
This lets the module use the same database/session context as the embedding application.
Host app bootstrap example
For a concrete shared-PDO integration example, see:
examples/host_app_bootstrap.php
Run it like this:
php -S 127.0.0.1:8090 examples/host_app_bootstrap.php
It exposes sample host-mounted routes:
POST /host/auth/registerPOST /host/auth/login
Example endpoints (JSON)
POST /registerPOST /loginPOST /verify-emailPOST /password-reset/requestPOST /password-reset/confirmPOST /totp/enroll/begin(auth + CSRF)POST /totp/enroll/confirm(auth + CSRF)POST /recovery-codes/regenerate(auth + CSRF)POST /step-up/verifyPOST /passkeys/register/begin(auth + CSRF)POST /passkeys/register/finish(auth + CSRF)POST /passkeys/authenticate/begin(CSRF)POST /passkeys/authenticate/finish(CSRF)GET /passkeys/list(auth)POST /passkeys/revoke(auth + CSRF)GET /sessions(auth)POST /sessions/revoke(auth + CSRF)GET /oidc/google/startGET /oidc/google/callback
HTML modules
- Register/login/verify-email/password-reset
- TOTP enroll + recovery code regeneration
- Step-up verification
- Passkey list + revoke
- Sessions/devices list + revoke
- Profile
All POST /ui/* routes require valid CSRF tokens.
Testing
Fast security suite:
composer test:security
Full DB-backed security suite (ephemeral MariaDB):
composer test:security:db
Focused WebAuthn-enabled DB path:
composer test:security:db:webauthn
Security defaults
- PDO prepared statements
- Argon2id password hashing (preferred)
random_bytes()token generation- Hashed tokens at rest
- Session ID regeneration on login
- CSRF enforcement on state-changing routes
- Generic auth failures (no account enumeration)
- Audit events for critical auth/security actions
- Pluggable risk engine with
allow,require_step_up,deny
Architecture notes
Key extension interfaces:
StepUpServiceInterfaceTotpServiceInterfaceRecoveryCodeServiceInterfacePasskeyServiceInterfacePasskeyCeremonyValidatorInterfaceOidcProviderInterfaceEventDispatcherInterface
Primary composition roots:
examples/minimal_router.phpmigrations/run.php
Current status
Phase-1 and planned Phase-2 capabilities are implemented in this repository, with DB-backed security regression coverage and CI workflow validation.