padosoft / laravel-rebel-admin-api
Control-plane JSON API for Laravel Rebel: security metrics, audit-event explorer, OTP/step-up funnels, provider health, with permission-gated and tenant-scoped read models. Part of padosoft/laravel-rebel-*.
Package info
github.com/padosoft/laravel-rebel-admin-api
pkg:composer/padosoft/laravel-rebel-admin-api
Requires
- php: ^8.3
- illuminate/contracts: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- padosoft/laravel-rebel-core: ^0.1
- spatie/laravel-package-tools: ^1.92
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0|^11.0
- padosoft/laravel-rebel-ai-guard: ^0.1
- padosoft/laravel-rebel-sessions: ^0.1
- padosoft/laravel-rebel-step-up: ^0.1
- pestphp/pest: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
Suggests
- padosoft/laravel-rebel-ai-guard: Anomaly cases + AI security copilot endpoints (anomalies, ai/anomalies/{case}/explain, ai/policies/suggest).
- padosoft/laravel-rebel-sessions: Device & session trust endpoints (subjects/{subject}/devices & sessions, revoke, logout-everywhere, untrust).
- padosoft/laravel-rebel-step-up: Adds PSD2/SCA counts to compliance/overview from rebel_step_up_challenges.
This package is auto-updated.
Last update: 2026-06-04 02:20:23 UTC
README
A control-plane JSON API for your auth security. Rebel writes every login, OTP, step-up and channel decision into one audit trail; this package turns that into a clean, permission-gated, tenant-aware read API: hourly metrics, a security overview, and a filterable audit-event explorer — ready to power a dashboard. Part of the
padosoft/laravel-rebel-*suite.
Table of contents
- What it is
- Quick glossary
- Why this package
- Rebel Admin API vs the alternatives
- Installation
- Configuration
- Endpoints
- The metrics projector
- Security notes
.env.example- Web Admin Panel
- Testing & License
What it is
The read side of the Rebel control plane. It does not authenticate end users; it lets your operators/SREs observe what the auth stack is doing — totals, funnels, and the raw event log — over a JSON API that a dashboard (or your own tooling) can consume.
It ships a metrics projector that aggregates the raw rebel_auth_events log into hourly
buckets, and read-model endpoints that serve those buckets and the event log, all gated by a
configurable guard + ability and scoped per tenant.
Depends on padosoft/laravel-rebel-core
(the audit log + tenancy). The matching web UI lives in laravel-rebel-admin.
Quick glossary
| Term | In plain words |
|---|---|
| Control plane | The "operate & observe" layer, as opposed to the user-facing login flow. |
| Metric bucket | An hourly count of events of one type/channel (a pre-aggregate, so dashboards are fast). |
| Projector | The job that turns raw events into buckets. |
| Read model | An endpoint that only reads/aggregates — never mutates. |
| Ability | A Laravel Gate check; here it gates access to the whole API. |
Why this package
| ★ | What | In short |
|---|---|---|
| ★★★ | Dashboard-ready read models | Health, security overview, and an audit explorer — JSON, paginated, filterable. |
| ★★★ | Fail-closed authorization | Out of the box NOBODY gets in until you grant the rebel-admin ability — no accidental open admin API. |
| ★★★ | Tenant-aware, explicitly | Looks across tenants for a super-admin, or ?tenant=<id> to scope — never a silent ambient leak. |
| ★★ | Cheap at scale | A streaming, idempotent projector pre-aggregates the log; overviews are DB-aggregated, not loaded into PHP. |
| ★★ | Privacy-first | Identifiers/IPs are HMAC'd at rest (by core); the API never returns plaintext PII. |
| ★★ | Robust pagination | A validated compound (created_at, id) keyset cursor — no skipped rows on timestamp ties. |
Rebel Admin API vs the alternatives
Building an auth-observability dashboard, compared:
| Capability | Rebel Admin API | Shopify | Generic admin panel (Nova/Filament) on raw tables | Hand-rolled queries |
|---|---|---|---|---|
| Purpose-built auth metrics/funnels | ✅ | ❌ | ❌ | ➖ |
| Pre-aggregated hourly buckets (fast) | ✅ | ❌ | ❌ | ❌ |
| Fail-closed authorization by default | ✅ | ➖ | ➖ | ❌ |
| Explicit cross-tenant vs scoped reads | ✅ | ❌ | ❌ | ❌ |
| No plaintext PII exposure | ✅ | ➖ | ➖ (depends) | ➖ |
| Validated keyset pagination | ✅ | ➖ | ➖ | ❌ |
| Versioned, documented JSON contract | ✅ | ➖ | ❌ | ❌ |
| Self-hosted, runs in your app | ✅ | ❌ | ✅ | ✅ |
Legend: ✅ built-in · ➖ partial / hosted-only / DIY · ❌ not available. A generic CRUD panel over the raw tables can show rows, but it won't give you funnels, fail-closed access, tenant-explicit reads or pre-aggregation — that's what this package is for. Shopify is a closed, hosted commerce platform: it offers a hosted admin over its own data, but you can't self-host it, query a tenant-scoped read API of your own auth events, or consume an OpenAPI contract for these primitives — it's a black box, not a library.
Installation
composer require padosoft/laravel-rebel-admin-api php artisan vendor:publish --tag="rebel-admin-api-config" php artisan vendor:publish --tag="rebel-admin-api-migrations" php artisan migrate
Grant access by defining the rebel-admin Gate (fail-closed by default):
// AppServiceProvider::boot() Gate::define('rebel-admin', fn ($user) => $user->is_admin === true);
Schedule the projector hourly:
// routes/console.php (Laravel 11/12+) or app/Console/Kernel.php Schedule::command('rebel:project-metrics')->hourly();
Configuration
File config/rebel-admin-api.php:
| Key | Default | What it does |
|---|---|---|
prefix |
rebel/admin/api/v1 |
Where the endpoints are mounted. |
guard |
'' |
Auth guard to require ('' = app default). |
ability |
rebel-admin |
Gate ability to require. Fail-closed: empty it only if your guard already implies admin. |
middleware |
[] |
Base middleware applied before the EnsureAdmin gate. |
Endpoints
All under {prefix} and gated by EnsureAdmin. Add ?tenant=<id> to scope to one tenant.
Most list endpoints accept the shared query parameters tenant, from, to,
granularity (minute|hour|day) — or the days shorthand — and endpoint-specific filters.
| Method & path | Section | Returns |
|---|---|---|
GET /me |
identity | { id, permissions: [...] } |
GET /health |
— | { status, events_total, buckets_total, last_event_at } |
GET /security/overview |
§3.1 | { period, generated_at, kpis{…}, timeseries, open_anomalies, providers } |
GET /otp/funnel?channel=&guard= |
§3.2 | { stages: [...], resend_rate } |
GET /step-up/funnel?purpose= |
§3.2 | { by_purpose: [...] } |
GET /channels/performance?channel=&provider= |
§3.3 | { rows: [...], timeseries } — real per-channel sent / verify_conversion, plus delivered_rate + cost_amount/cost_currency derived from channel.verification.delivered events (provider status webhooks, e.g. Twilio). Latency stays null until captured — never fabricated |
GET /providers/health |
§3.4 | { providers: [...] } |
GET /auth-events?... |
§3.5 | { data, per_page, next_before, next_before_id } — each row includes country, ip_hmac, user_agent_hash |
GET /auth-events/{id} |
§3.5 | { data: { …, country, ip_hmac, user_agent_hash, metadata (sanitized) } } |
GET /subjects?per_page= |
§3.6 | { data: [ { subject, masked, devices, sessions, last_seen_at } ], meta } — searchable subject list (masked ids, never raw PII) |
GET /subjects/{subject}/devices |
§3.6 | { devices: [...] } |
GET /subjects/{subject}/sessions |
§3.6 | { sessions: [...] } |
POST /subjects/{subject}/sessions/{id}/revoke |
§3.6 | { revoked: true } |
POST /subjects/{subject}/logout-everywhere |
§3.6 | { revoked: <n> } |
POST /subjects/{subject}/devices/{id}/untrust |
§3.6 | { untrusted: true } |
GET /risk-rules |
§3.7 | { rules: [...] } |
POST /risk-rules |
§3.7 | { rule: {…} } (persisted as a draft by default) |
POST /risk-rules/simulate |
§3.7 | { decision, required_assurance, require_phishing_resistant, allowed_drivers, matched_rules, reasons } |
GET /anomalies?type=&severity=&status=&cursor= |
§3.8 | { data, meta: { next_cursor, has_more } } |
GET /anomalies/{case} |
§3.8 | { id, type, severity, status, signals, timeline, suggested_actions } |
POST /anomalies/{case}/actions |
§3.8 | { ok, action } (mitigate requires confirm:true) |
POST /ai/anomalies/{case}/explain |
§3.9 | { explanation, confidence, sources } |
POST /ai/policies/suggest |
§3.9 | { draft_rule, rationale } |
GET /compliance/overview |
§3.10 | { nist, amr, psd2, gdpr } — amr is the real factor distribution (otp/passkey/…) flattened from the event log |
GET /settings · PUT /settings/{key} |
— | tenant-scoped key/value settings |
Example:
curl -H "Authorization: Bearer <token>" \ "https://app.test/rebel/admin/api/v1/security/overview?days=30"
{ "period": "30d", "generated_at": "2026-06-03T10:00:00+00:00",
"kpis": { "login_requests": { "value": 12840, "delta_pct": 8.1, "sparkline": [/* … */] } },
"timeseries": [], "open_anomalies": [], "providers": [] }
The device/session, anomaly and AI endpoints read from the optional sibling packages (
laravel-rebel-sessions,laravel-rebel-ai-guard,laravel-rebel-step-up). When a package is not installed the corresponding endpoints return an honest empty state / 404 — they never error.
The metrics projector
rebel:project-metrics {--hours=2} aggregates the raw event log into rebel_metric_buckets.
It streams events (constant memory), truncates each to the hour, and upserts — so
re-running over an overlapping window simply corrects late-arriving counts. Run it hourly;
the default 2-hour window re-projects the current and previous hour.
php artisan rebel:project-metrics # last 2 hours php artisan rebel:project-metrics --hours=48 # backfill 2 days
Security notes
- Fail-closed gate: the default
rebel-adminability denies until you define the Gate — no accidentally-open admin API. - Explicit tenancy: reads bypass the ambient tenant scope and look across tenants by
default;
?tenant=<id>scopes deterministically (no silent wrong-tenant results). - No plaintext PII: identifiers/IPs are HMAC'd by core; the API surfaces only those hashes.
- Memory-safe: the projector streams with a cursor; the overview aggregates in the DB.
- Validated cursor: a bad
beforevalue returns422, never a 500 or silent empty page.
.env.example
REBEL_ADMIN_API_PREFIX=rebel/admin/api/v1 REBEL_ADMIN_API_GUARD= REBEL_ADMIN_API_ABILITY=rebel-admin
Web Admin Panel
This API powers the Laravel Rebel Web Admin Panel (the laravel-rebel-admin package) —
a ready-made dashboard over these read models (security overview, funnels, event explorer,
provider health). The API is fully usable on its own for custom tooling.
🔋 Vibe coding with batteries included
This package ships AI batteries — so you (and your AI agent) can extend it correctly on the first try:
CLAUDE.md— a concise AI working guide (purpose, conventions, architecture, how to extend, Definition of Done). Plain Markdown, so Claude Code, Cursor, Copilot and Codex all read it.AGENTS.md— the agent/workflow contract (branch → PR → CI → tag/release, the gates)..claude/skills/— invocable skills (at leastrebel-package-dev) encoding the suite's TDD loop, the PHPStan-level-max recipes, the security/telemetry rules, and the release discipline.
Open the repo in your AI editor and just start — the rules, guardrails and extension recipes come
with it. PRs that follow the shipped CLAUDE.md pass CI (PHPStan max + Pest + Pint) and review the
first time around.
Testing & License
composer test # Pest (gate, projector, overview, explorer) composer phpstan # static analysis, level max composer pint # code style
License: MIT — see LICENSE. Part of the padosoft/laravel-rebel suite.
