This repository is a PyroCMS / Streams Platform application updated to support Laravel 12 and Passport-based headless API authentication.
This version changes a few important pieces:
- The underlying package set has been moved forward to the Laravel 12-compatible Streams Platform and addon versions.
- API authentication now uses Laravel Passport instead of the older simple token guard.
- Headless auth routes are included at
/api/register,/api/login,/api/logout, and/api/user. /api/loginissues bearer tokens by making an internal request to/oauth/token.- Passport password grant is explicitly enabled in
AppServiceProvider. - Existing Passport client secrets may need to be re-hashed for Passport 13+ validation.
- Legacy Anomaly code paths still need compatibility patching after Composer install/update. That is handled by
scripts/patch_anomaly_dispatch.phpthrough Composer scripts. - In
localandtesting, Redis sessions fall back to file sessions if the PHP Redis extension is not installed.
- PHP 8.4+
- Composer
- MySQL
- Redis recommended for normal runtime usage
- Install PHP dependencies:
composer install- Create and configure your
.envfile.
Important values include:
APP_NAMEAPP_ENVAPP_KEYAPP_URLDB_*REACT_CLIENT_IDREACT_CLIENT_SECRET
- Generate the application key if needed:
php artisan key:generate- Generate Passport keys:
php artisan passport:keys
chmod 600 storage/oauth-private.key storage/oauth-public.key- Create a Passport password grant client if you do not already have one:
php artisan passport:client --passwordSet the resulting client id and secret as:
REACT_CLIENT_IDREACT_CLIENT_SECRET
- Run migrations:
php artisan migrate- Clear caches if this is an upgraded install:
php artisan optimize:clearIf you are updating an existing older install instead of starting fresh:
- Deploy the updated code.
- Run Composer with scripts enabled:
composer install --no-dev --optimize-autoloader- Ensure Passport keys exist and have correct permissions:
php artisan passport:keys
chmod 600 storage/oauth-private.key storage/oauth-public.key- Run migrations:
php artisan migrate --force- Clear caches:
php artisan optimize:clearImportant:
- The migration
2026_03_13_130000_hash_existing_passport_client_secretsre-saves existing OAuth client secrets so Passport 13+ can validate them correctly. - Do not change
REACT_CLIENT_SECRETunless you also intentionally update the matching Passport password client. - Do not skip Composer scripts. The Anomaly compatibility patch is part of the install/update flow.
This app includes generic headless auth routes:
POST /api/registerPOST /api/loginPOST /api/logoutGET /api/user
Request:
{
"email": "user@example.com",
"password": "secret1234",
"first_name": "Jane",
"last_name": "Doe",
"auto_login": true
}Optional fields:
usernamedisplay_nameauto_login
If auto_login is true or omitted, the response will try to include a Passport access token after registration.
You can log in with any one of:
loginemailusername
Example:
{
"email": "user@example.com",
"password": "secret1234"
}or:
{
"login": "user@example.com",
"password": "secret1234"
}Successful login returns the Passport token payload from /oauth/token plus the resolved user record.
Use the returned bearer token in the Authorization header:
Authorization: Bearer YOUR_ACCESS_TOKENExample:
curl -X GET http://localhost/api/user \
-H "Accept: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Logout revokes the current access token:
curl -X POST http://localhost/api/logout \
-H "Accept: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"- The API guard now uses Passport in
config/auth.php. - The user provider now points at
App\UserModel, which extends the Pyro users model and addsHasApiTokens. - The login endpoint uses the configured Pyro user login mode to resolve whether Passport should authenticate by username or email.
The Docker setup supports persistent or shared mounts for the parts of the app that usually need to live outside the image:
addons/for shared or custom PyroCMS addonsstorage/for runtime app databootstrap/cache/for generated cache filespublic/app/for user-managed public assetsconfig/andresources/streams/config/if you want host-managed config overlays
The default production compose file already persists:
/var/www/html/addons/var/www/html/storage/var/www/html/bootstrap/cache
If you want host bind mounts instead of Docker-managed named volumes, uncomment the example mount lines in docker-compose.yml and point each source path at the host directory you want to share.
Important:
- Mounting
config/orresources/streams/config/replaces the image copy for those directories, so only do that when your host copy is complete. - The development compose file already bind mounts the whole repository, so local addons, config, and app data can all be edited directly there.
If you discover any security related issues, please email admin@formable.app instead of using the issue tracker.