ninjaportal / portal-api
REST API package for NinjaPortal
Requires
- php: ^8.2
- illuminate/contracts: ^11.0||^12.0
- illuminate/support: ^11.0||^12.0
- ninjaportal/portal: ^0.1
- php-open-source-saver/jwt-auth: ^2.8
- spatie/laravel-package-tools: ^1.16
- spatie/laravel-permission: ^6.0
Requires (Dev)
- laravel/pint: ^1.13
- orchestra/testbench: ^9.0 || ^10.0
- phpstan/phpstan: ^1.11 || ^2.0
- phpunit/phpunit: ^11.0
This package is auto-updated.
Last update: 2026-03-01 15:51:32 UTC
README
REST API package for NinjaPortal (ninjaportal/portal) with:
- Versioned routes (
/api/v1/...) - Admin + consumer auth flows (JWT access token + DB refresh token)
- Admin RBAC (Spatie permissions)
- Consistent response envelope + API exception normalization
- Activity logging subscriber for Portal domain events
Routes
- Default base path:
api/v1 - Admin sub-prefix:
admin(full admin base path:api/v1/admin)
Configuration Highlights
Main config: config/portal-api.php
Important keys:
portal-api.prefixportal-api.admin_prefixportal-api.auth.consumer_modelportal-api.auth.admin_modelportal-api.auth.guards.*portal-api.rbac.enabledportal-api.authorization.use_policiesportal-api.tokens.*portal-api.tokens.prune.*
Out of the box, portal-api ships JWT-ready auth models:
NinjaPortal\Api\Models\UserNinjaPortal\Api\Models\Admin
When the shared ninjaportal.models.User / ninjaportal.models.Admin config values
are still pointing at the core Portal models, portal-api promotes them to these API
models automatically so the Portal repositories and API auth layer resolve the same
classes. If your application already overrides those model config values, portal-api
leaves them untouched.
Extensibility (Client Customization)
1) Custom User/Admin Models
The package supports overriding the consumer/admin auth models via config:
PORTAL_API_CONSUMER_MODELPORTAL_API_ADMIN_MODEL
The API package now resolves model classes and table names through NinjaPortal\Api\Support\PortalApiContext, which is used by:
- auth flow (login)
- route-adjacent user resolution in admin user endpoints
- request validation rules that previously hardcoded
userstable
2) Authentication Plugins (MFA / SSO)
Auth controllers now depend on NinjaPortal\Api\Contracts\Auth\AuthFlowInterface instead of directly implementing login logic.
Default implementation:
NinjaPortal\Api\Auth\AuthFlow
Token lifecycle is abstracted by:
NinjaPortal\Api\Contracts\Auth\TokenServiceInterface- default:
NinjaPortal\Api\Services\TokenService
This makes it easier for a separate package to:
- replace the auth flow (SSO-first login, MFA challenge flow)
- decorate token issuing/refresh/revoke behavior
- listen to auth lifecycle events
3) Auth Lifecycle Events (for plugins)
Events emitted by the API package:
NinjaPortal\Api\Events\Auth\LoginAttemptedEventNinjaPortal\Api\Events\Auth\LoginFailedEventNinjaPortal\Api\Events\Auth\LoginSucceededEventNinjaPortal\Api\Events\Auth\TokenIssuedEventNinjaPortal\Api\Events\Auth\TokenRefreshedEventNinjaPortal\Api\Events\Auth\TokenRevokedEvent
Suggested MFA package approach:
- Bind your own
AuthFlowInterfaceimplementation. - Reuse
TokenServiceInterfaceafter challenge success. - Optionally listen to auth events for auditing / throttling / risk scoring.
4) Authorization Strategy Replacement
Admin controllers can optionally perform policy checks through:
NinjaPortal\Api\Contracts\Authorization\ApiAuthorizerInterface- default implementation:
NinjaPortal\Api\Authorization\PolicyApiAuthorizer
Disable policy checks (keep route RBAC only):
PORTAL_API_USE_POLICIES=false
Replace the authorizer in another package/service provider if you need:
- ABAC
- tenant-aware checks
- external policy engines
RBAC vs Policy Checks
Two layers can coexist:
- Route RBAC (Spatie): controlled by
PORTAL_API_RBAC_ENABLED - Controller policy checks: controlled by
PORTAL_API_USE_POLICIES
Recommended for standalone portal deployments:
- Keep both enabled (
true)
Pagination / List Endpoint Conventions
Shared helpers live in:
src/Http/Controllers/Controller.phpsrc/Http/Mixins/ApiRequestMixin.php
Preferred list pattern:
- Use
listParams()forper_page,order_by,direction - Query/paginate in service or query builder
- Return via
respondPaginated()
Intentionally Unpaginated Endpoints (Current Design)
These are intentionally returned as full collections because they are typically small/config-like datasets:
- Admin settings index
- Admin menus index
- Admin menu items index (per menu)
- RBAC roles index
- RBAC permissions index
If a client deployment grows these significantly, convert them to the standard paginated pattern.
Refresh Token Pruning
Refresh tokens are stored in portal_api_refresh_tokens.
Prune expired/revoked rows manually:
php artisan portal-api:tokens:prune
Queue prune job instead:
php artisan portal-api:tokens:prune --queue
Optional scheduler integration (package-level):
PORTAL_API_REFRESH_PRUNE_ENABLED=truePORTAL_API_REFRESH_PRUNE_QUEUE=falsePORTAL_API_REFRESH_PRUNE_FREQUENCY=daily(dailyorhourly)PORTAL_API_REFRESH_PRUNE_TIME=03:00(used fordaily)
Docs (Scribe)
- Generate docs:
php artisan scribe:generate - Docs endpoint (if enabled in
config/scribe.php):/docs
Development (Package-local)
Available scripts:
composer testcomposer analysecomposer format
Note:
- In this monorepo, package-local installs may need Composer path repositories for local package dependencies (
ninjaportal/portal, etc.).