Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- **Headless core + Blade starter kit (RFC 002, phase A)** — the package is now fully headless by default, Fortify-style:
- New `ui` config value (`NEEV_UI`: `'blade'` | `null`). `null` (default) registers no Blade page routes — API, OAuth/SSO, and email flows work standalone. `'blade'` registers the page routes, rendered from **app-owned** views
- The Blade page templates moved from package-loaded views to `stubs/blade/views/`; `php artisan neev:ui blade` ejects them to `resources/views/vendor/neev` where they belong to the app (existing published views keep working — same path)
- **Email templates are ejected to the app by the installer** (always, regardless of kit) so they're editable from day one; the package keeps fallback copies so headless installs still send mail. The per-template variable contract is documented in `docs/rfcs/002-starter-kits.md` §5.5 and treated as API
- `neev:install` gains a starter-kit prompt (`blade`/`none`) and third argument; new `neev:ui {kit} [--force]` command for kit ejection on existing apps (never overwrites app files without `--force`)
- Headless email links point at the app's frontend (`{app.url}/verify-email?...`, `/register?invitation_id=...`) instead of the unregistered Blade routes
- New publish tags: `neev-blade-kit`, `neev-mail` (replacing `neev-views`)
- **Configurable route prefix** — new `route_prefix` config key (`NEEV_ROUTE_PREFIX`, default `neev`) namespaces every machine-facing route the package registers: the API namespace, OAuth redirect/callback, tenant SSO, and `/csrf-cookie`. Blade UI pages (`/login`, `/account/...`) stay at the root. Route names are unchanged. The MFA-token route gate in `NeevAPIMiddleware` now follows the prefix (previously hardcoded — customised route files silently broke MFA step-up)

### Changed
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Neev - Enterprise User Management for Laravel

Neev is a comprehensive Laravel package that provides enterprise-grade user authentication, team management, and security features. It's designed as a complete starter kit for SaaS applications, eliminating the need to build complex user management systems from scratch.
Neev is a comprehensive Laravel package that provides enterprise-grade user authentication, team management, and security features for SaaS applications, eliminating the need to build complex user management systems from scratch. The package is headless by default — the API, OAuth/SSO, and email flows work standalone for SPA/API frontends — with an optional Blade starter kit whose pages are ejected into your app at install and are yours to edit.

[![Latest Version](https://img.shields.io/packagist/v/ssntpl/neev.svg?style=flat-square)](https://packagist.org/packages/ssntpl/neev)
[![License](https://img.shields.io/packagist/l/ssntpl/neev.svg?style=flat-square)](https://packagist.org/packages/ssntpl/neev)
Expand Down Expand Up @@ -82,6 +82,8 @@ composer require ssntpl/neev
php artisan neev:install
```

The wizard asks three questions: multi-tenant isolation (yes/no), team support (yes/no), and which frontend starter kit you want — `blade` ejects ready-made pages into `resources/views/vendor/neev/` (app-owned), `none` keeps the package headless. Email templates are ejected to your app either way. You can eject a kit later with `php artisan neev:ui blade`.

### 3. Configure Environment

```env
Expand Down Expand Up @@ -332,6 +334,8 @@ All API routes are prefixed with `/neev` — the prefix is configurable via `rou

## Web Routes

The Blade page routes below (everything except the OAuth/SSO endpoints) register only when the Blade starter kit is installed (`'ui' => 'blade'` in `config/neev.php`) — headless installs use the API instead.

### Public Routes

| Method | Route | Name | Description |
Expand Down Expand Up @@ -676,7 +680,8 @@ Email verification is enforced by applying the opt-in `neev:verified-email` midd
### Setup

```bash
php artisan neev:install # Interactive setup (asks: tenants? teams?)
php artisan neev:install # Interactive setup (asks: tenants? teams? starter kit?)
php artisan neev:ui blade # Eject the Blade starter kit (or `none` for headless)
php artisan neev:download-geoip # Download GeoIP database
```

Expand Down
23 changes: 23 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,29 @@ changes see [CHANGELOG.md](./CHANGELOG.md).

## 0.4.5 → Unreleased

**The package is now headless by default (RFC 002, action required for
Blade UI users).**
Page views are no longer auto-loaded from the package, and the Blade
page routes (`/login`, `/account/...`) only register when
`config('neev.ui') === 'blade'`.

- **Using the shipped Blade UI?** Run `php artisan neev:ui blade` —
it ejects the views to `resources/views/vendor/neev` (app-owned from
then on) and sets `'ui' => 'blade'`. If you had already published
the views, your files are untouched (same path); just set the `ui`
config.
- **Headless / SPA / API-only?** Nothing to do — the Blade page routes
disappear (they were dead weight), and verification/invitation email
links now point at your frontend (`{app.url}/verify-email?...`,
`{app.url}/register?invitation_id=...&hash=...`) carrying the
signed query for the API endpoints.
- Email templates: the installer copies them to
`resources/views/vendor/neev/emails` so they're yours to edit; the
package retains fallbacks. The available variables per template are
documented and stable (see RFC 002 §5.5).
- The `neev-views` publish tag is replaced by `neev-blade-kit` and
`neev-mail`.

**OAuth/SSO routes moved under the route prefix (action required for
identity providers).**
All machine-facing routes now live under the configurable
Expand Down
16 changes: 16 additions & 0 deletions config/neev.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@
*/
'route_prefix' => env('NEEV_ROUTE_PREFIX', 'neev'),

/*
|--------------------------------------------------------------------------
| Frontend UI
|--------------------------------------------------------------------------
|
| Which starter kit drives the frontend. 'blade' registers the Blade
| page routes (/login, /account/..., rendered from the app-owned views
| the installer ejected). null runs the package headless: only the API,
| OAuth/SSO, and email flows are active, and you build the frontend
| yourself (see docs/rfcs/002-starter-kits.md).
|
| Set by `php artisan neev:install` / `php artisan neev:ui`.
|
*/
'ui' => env('NEEV_UI'),

/*
|--------------------------------------------------------------------------
| Authentication
Expand Down
17 changes: 11 additions & 6 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Neev Documentation

Neev is an enterprise-grade Laravel package for user authentication, team management, and multi-tenancy in SaaS applications. See the [main README](../README.md) for a quick overview and getting started guide.
Neev is an enterprise-grade Laravel package for user authentication, team management, and multi-tenancy in SaaS applications. The package is headless by default — API, OAuth/SSO, and email flows work standalone — with an optional Blade starter kit whose pages are ejected into your app at install ([RFC 002](./rfcs/002-starter-kits.md)). See the [main README](../README.md) for a quick overview and getting started guide.

---

Expand All @@ -26,7 +26,7 @@ Endpoint and route details for building against Neev.
| Reference | Description |
|-----------|-------------|
| [API Reference](./api-reference.md) | Every REST endpoint with request/response examples |
| [Web Routes](./web-routes.md) | All Blade-rendered web routes and view files |
| [Web Routes](./web-routes.md) | All Blade-rendered web routes and view files (require the Blade starter kit, `'ui' => 'blade'`) |

## Architecture

Expand All @@ -40,12 +40,12 @@ Design decisions and internal patterns — useful when extending Neev or contrib

## Proposals

Design proposals under review, not yet implemented.
Design proposals and their implementation status.

| Document | Status | Description |
|----------|--------|-------------|
| [SPA Cookie Mode](./spa-cookie-mode.md) | Phases 1–3 implemented; phase 4 (consumer guide) pending | HttpOnly-cookie auth + signed double-submit CSRF for same-origin SPAs. Additive to the existing bearer-token API. Driven by the TAILLOG web rebuild and otper. |
| [RFC 002 — Headless Core + Starter Kits](./rfcs/002-starter-kits.md) | Proposed (design agreed 2026-07-02) | Package becomes fully headless (Fortify-style); Blade UI ejects into the app as a starter kit at install; email templates app-owned with a documented variable contract; React kit reserved as a future kit. |
| [RFC 002 — Headless Core + Starter Kits](./rfcs/002-starter-kits.md) | Phase A implemented | Package is fully headless (Fortify-style); Blade UI ejects into the app as a starter kit at install; email templates app-owned with a documented variable contract; React kit reserved as a future kit. |

---

Expand All @@ -59,10 +59,14 @@ neev/
│ ├── factories/ # Model factories (testing)
│ └── migrations/ # Database migrations
├── resources/
│ └── views/ # Blade templates (64 files)
│ └── views/
│ └── emails/ # Email templates (headless fallbacks; ejected to the app at install)
├── routes/
│ ├── neev.php # Web and API routes
│ └── sso.php # Tenant SSO routes
├── stubs/
│ └── blade/
│ └── views/ # Blade starter kit page views (ejected via neev:ui blade)
└── src/
├── Commands/ # Artisan commands
├── Contracts/ # Interfaces (ContextContainer, HasMembers, etc.)
Expand Down Expand Up @@ -204,7 +208,8 @@ See [CLI Commands](./cli-commands.md) for full reference with options and exampl

| Command | Description |
|---------|-------------|
| `neev:install` | Interactive setup wizard |
| `neev:install` | Interactive setup wizard (tenant, teams, starter kit) |
| `neev:ui` | Eject a frontend starter kit (`blade`/`none`) and the email templates |
| `neev:download-geoip` | Download MaxMind GeoLite2 database |
| `neev:clean-login-attempts` | Remove old login attempt records |
| `neev:tenant:create` | Create a tenant (isolated) or team (shared) |
Expand Down
26 changes: 25 additions & 1 deletion docs/cli-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,33 @@ Interactive setup wizard for new installations.

```bash
php artisan neev:install

# Non-interactive: {tenant} {teams} {kit}
php artisan neev:install yes no blade
```

Prompts for: multi-tenant isolation and team support. Publishes the config file and sets the `tenant` and `team` options accordingly. Only runs on a fresh installation (fails if the `users` table has records).
Prompts for: multi-tenant isolation (`yes`/`no`), team support (`yes`/`no`), and the frontend starter kit (`blade`/`none`, default `blade`). Publishes the config file, sets the `tenant` and `team` options accordingly, and calls `neev:ui` to eject the chosen kit (and, always, the email templates). Only runs on a fresh installation (fails if the `users` table has records).

### `neev:ui`

Eject a frontend starter kit into the application. Also the way to switch an existing install between Blade and headless.

```bash
php artisan neev:ui blade # eject the Blade kit
php artisan neev:ui blade --force # overwrite files that already exist in the app
php artisan neev:ui none # headless — no page views, page routes disabled
```

| Argument / Option | Description |
|-------------------|-------------|
| `kit` | Starter kit to eject: `blade` or `none` |
| `--force` | Overwrite files that already exist in the app (without it, existing files are kept) |

What it does:

- **`blade`**: copies the package's Blade page views (auth, account, team pages, components, layouts) from `stubs/blade/views/` to `resources/views/vendor/neev/` — app-owned from then on — and sets `'ui' => 'blade'` in `config/neev.php`, which registers the Blade page routes.
- **`none`**: sets `'ui' => env('NEEV_UI')` (headless — no Blade page routes); no page views are copied.
- **Always** (both kits): ejects the email templates to `resources/views/vendor/neev/emails/` so they're the app's to edit. The package keeps fallback copies, so headless installs send mail with zero setup. The variables available in each template are documented in [RFC 002 §5.5](./rfcs/002-starter-kits.md#55-email-templates--app-owned-with-a-variable-contract) and treated as API.

### `neev:download-geoip`

Expand Down
19 changes: 19 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ Blade UI pages (`/login`, `/register`, `/account/...`) stay at the root — they

---

## Frontend UI

```php
'ui' => env('NEEV_UI'),
```

Which starter kit drives the frontend. Valid values:

- **`'blade'`** — registers the Blade page routes (`/login`, `/register`, `/account/...`), rendered from the app-owned views the installer ejected to `resources/views/vendor/neev/`. Setting `'blade'` without ejecting the kit gives a clear "view not found" error — run `php artisan neev:ui blade` first.
- **`null`** (default) — headless: no Blade page routes are registered. The API routes, OAuth/SSO endpoints, and email flows are unaffected; you build the frontend yourself. Verification and new-user invitation email links point at your frontend (`{app.url}/verify-email?...`, `{app.url}/register?invitation_id=...&hash=...`) carrying the signed query for the API endpoints.

This value is normally set for you by `php artisan neev:install` / `php artisan neev:ui` — see [CLI Commands](./cli-commands.md#neevui) and [RFC 002](./rfcs/002-starter-kits.md).

---

## Authentication

### Username Support
Expand Down Expand Up @@ -371,6 +386,9 @@ return [
// Routes
'route_prefix' => env('NEEV_ROUTE_PREFIX', 'neev'),

// Frontend UI ('blade' = Blade page routes from app-owned views; null = headless)
'ui' => env('NEEV_UI'),

// Authentication
'support_username' => false,
'oauth' => [
Expand Down Expand Up @@ -456,6 +474,7 @@ return [
| Variable | Description | Default |
|----------|-------------|---------|
| `NEEV_ROUTE_PREFIX` | Prefix for machine-facing routes (API, OAuth, SSO, csrf-cookie) | `neev` |
| `NEEV_UI` | Frontend starter kit: `blade` or unset (headless) | unset (headless) |
| `NEEV_JWT_SECRET` | Secret for signing MFA JWTs | Falls back to `APP_KEY` |
| `MAXMIND_EDITION` | GeoIP database edition | `GeoLite2-City` |
| `MAXMIND_LICENSE_KEY` | MaxMind license key | - |
Expand Down
33 changes: 28 additions & 5 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,33 @@ php artisan neev:install
This command must be run on a fresh installation — it aborts if the `users` table already contains records. It will:
1. Publish the configuration file to `config/neev.php` (overwriting any existing copy)
2. Set the `tenant` and `team` config options based on your answers
3. Eject your chosen frontend starter kit — and, always, the email templates — via `neev:ui`

### Installation Options

The command takes two arguments and interactively prompts for any that are missing:
The command takes three arguments and interactively prompts for any that are missing:

| Prompt | Config key | Description |
|--------|------------|-------------|
| Would you like to enable multi-tenant isolation? | `tenant` | Users scoped to a tenant; the same email can exist in different tenants |
| Would you like to install team support? | `team` | Enable team/organization features |
| Which frontend starter kit would you like? | `ui` | `blade` — ready-made pages ejected into your app; `none` — headless, you build the frontend yourself |

**Starter kit choices:**

- **`blade`** (default): copies the Blade page views (login, account, team pages, components, layouts) to `resources/views/vendor/neev/` — they are app-owned from then on — and sets `'ui' => 'blade'` so the Blade page routes (`/login`, `/account/...`) are registered.
- **`none`**: headless. No page views are copied and no Blade page routes are registered — the API, OAuth/SSO, and email flows are fully functional, and you build the frontend yourself.

Either way, the email templates are copied to `resources/views/vendor/neev/emails/` so they're yours to edit from day one.

You can also pass the answers directly:

```bash
php artisan neev:install yes no # tenant: yes, teams: no
php artisan neev:install yes no blade # tenant: yes, teams: no, kit: blade
```

If you start headless and want the Blade kit later, run `php artisan neev:ui blade` at any time — see [CLI Commands](./cli-commands.md#neevui).

All other features (username support, OAuth, MFA, password policies, etc.) are configured by editing `config/neev.php` — see the [Configuration Reference](./configuration.md).

---
Expand Down Expand Up @@ -181,13 +192,25 @@ php artisan vendor:publish --tag=neev-migrations

This copies migrations to `database/migrations/` for customization. Publishing is optional — migrations load automatically from the package.

### Publish Views
### Eject the Blade Starter Kit

Neev is headless by default — the Blade pages only exist once the starter kit is ejected into your app (normally done by the installer). To eject it later, or re-eject:

```bash
php artisan neev:ui blade # never overwrites your files
php artisan neev:ui blade --force # overwrites existing files
```

This copies the page views to `resources/views/vendor/neev/` (app-owned) and sets `'ui' => 'blade'` in your config, which registers the Blade page routes. See [CLI Commands](./cli-commands.md#neevui).

The raw publish tags also exist if you prefer `vendor:publish` (note these do not set the `ui` config):

```bash
php artisan vendor:publish --tag=neev-views
php artisan vendor:publish --tag=neev-blade-kit # page views, components, layouts
php artisan vendor:publish --tag=neev-mail # email templates only
```

This copies Blade templates to `resources/views/vendor/neev/` for customization.
> The old `neev-views` tag no longer exists — it was replaced by `neev-blade-kit` and `neev-mail`.

### Publish Routes

Expand Down
3 changes: 2 additions & 1 deletion docs/rfcs/002-starter-kits.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# RFC 002 — Headless Core + Starter Kits

> **Status:** Proposed (design agreed with maintainer 2026-07-02; Phase A pending implementation)
> **Status:** Phase A implemented (headless core, `ui` config, Blade kit in `stubs/`, `neev:ui` command, mail ejection); Phase B (React kit) future; Phase C (docs polish) pending
> **§8 resolutions:** one Blade kit including the app-shell views (Q1, Q2); kit ejection on existing apps via the dedicated `neev:ui` command, keeping the installer's fresh-install guard (Q3). Kit ejection sources email templates from the package's own `resources/views/emails` rather than a duplicate `stubs/mail/` copy.
> **Drivers:** neev as a Fortify-class drop-in for fresh Laravel installs; TAILLOG/otper React rebuilds
> **Depends on:** SPA cookie mode (`docs/spa-cookie-mode.md`), configurable `route_prefix`

Expand Down
Loading