devshaded/laravel-sanctum-starter-kit
A Laravel starter kit built around Sanctum, giving you ready-made token-based API authentication scaffolding to bootstrap secure projects faster.
This is a starter kit, not a runtime dependency — you scaffold an application once and then own the code. Treat the first hour as environment validation, not feature work.
1. Verify the toolchain before anything else. The release requires PHP 8.5 and Composer 2.10+, and both are ahead of what most laptops, CI runners, and PaaS build images ship by default.
php -v # must be 8.5.x
composer -V # must be >= 2.10
docker -v # needed for the Sail workflow
If you can't match PHP 8.5 locally, don't fight it — use the Sail path so the container supplies the interpreter, and keep your host PHP out of the loop.
2. Install via GitHub, not Packagist, for now. The release notes are explicit that the short Composer command only resolves after Packagist registration. Until then:
git clone <public-github-repo> my-app && cd my-app
composer install
cp .env.example .env
php artisan key:generate
Or, if you prefer create-project semantics, point Composer at the VCS repository directly:
composer create-project <vendor>/<kit> my-app \
--repository='{"type":"vcs","url":"<public-github-repo>"}' \
--stability=dev
Note the short command may start working mid-project; pin your install method in the team README so onboarding is reproducible either way.
3. Bring up the stack. With Sail, the expected sequence is ./vendor/bin/sail up -d, then sail artisan migrate. Confirm the API responds before writing any code.
4. Where to look first, in order:
| Path | Why |
|---|---|
composer.json → scripts |
The "strict quality checks" are wired here; this is your lint/analyse/test entry point |
config/sanctum.php |
Stateful domains, token expiration, middleware |
routes/api.php |
The auth surface you'll extend |
| Auth controllers / form requests | Login, token issuance, registration — the pattern to copy |
| API documentation source + its route | Where the spec lives and whether it's publicly exposed |
docker-compose.yml / Sail config |
Ports, services, PHP version pinning |
5. First use case. Authenticate a request end-to-end before customising anything: log in through the provided endpoint, capture the returned plaintext token, and call a protected route with Authorization: Bearer <token>. That single loop exercises Sanctum, the middleware stack, and the database schema at once — and tells you whether the kit is healthy on your machine.
Choose your auth mode deliberately, on day one. Sanctum supports two distinct flows and mixing them casually is the most common source of confusion.
| Token (bearer) | SPA (cookie/session) | |
|---|---|---|
| Client | Mobile, CLI, third-party, service-to-service | First-party JS on a shared top-level domain |
| Requires | auth:sanctum on routes |
Stateful domain config, CSRF cookie, withCredentials |
| Revocation | Per-token, immediate | Session invalidation |
| Setup cost | Low | Higher — CORS, sessions, ports |
Most API-first projects want tokens; reach for the cookie flow only when you genuinely have a first-party SPA.
Token issuance pattern. Keep issuance in a single, auditable place: authenticate credentials, then mint a token with the narrowest ability set that works, and return the plaintext value exactly once inside the login response. Never persist or log the plaintext — only the hash survives in personal_access_tokens. Name tokens after the client ('ios-app', 'cli') so revocation is operable rather than guesswork.
Scope with abilities, not just authentication. Issue tokens with explicit abilities and gate routes with the ability middleware rather than checking roles inside controllers. This keeps authorisation declarative and visible in php artisan route:list. Provide a logout/revoke endpoint that deletes the current token ($request->user()->currentAccessToken()->delete()), and a "revoke all devices" path for account-security events.
Group and version your API from the start. Wrap the authenticated routes in a prefix + auth:sanctum group, apply throttling at the group level, and leave the public auth endpoints outside it. Retrofitting versioning after clients ship is painful; the kit's structure makes this cheap now.
Keep the documentation generated, not hand-written. Whatever the bundled API documentation tooling is, annotate controllers and requests at the point of truth and regenerate. Add the regeneration to CI so the spec can't drift from the code — a stale spec is worse than no spec, because clients trust it.
Mirror the release's own quality gate. The notes state PHP checks and a fresh-clone setup passed on the release commit, which means the kit is internally consistent. Preserve that: run the composer quality scripts on every PR, and add a CI job that does a clean composer install + migration from scratch. Fresh-clone breakage (missing .env.example keys, unpublished config, order-dependent migrations) is the classic starter-kit regression.
Test with Sanctum's helper, not real tokens. In feature tests, Sanctum::actingAs($user, ['orders:read']) bypasses the token plumbing and lets you assert ability enforcement precisely. Reserve real HTTP token round-trips for a small number of integration tests.
Seed a local token for manual poking. A seeder that prints a long-lived dev token saves a login call on every curl. Guard it behind the local environment only.
Plan for divergence. Because this is a scaffold, decide early how you'll absorb upstream security fixes: keep a pristine branch of the release commit, or fork and cherry-pick. Adopting the kit without a diff strategy means you silently fall behind on Sanctum and framework patches.
PHP 8.5 and Laravel 13 are bleeding-edge requirements. Expect hosted platforms, older Docker base images, and prebuilt CI actions to lag. Pin the platform in composer.json (config.platform.php) so dependency resolution matches the container, and standardise on the Sail image as the single source of truth for the interpreter version. If a dependency refuses to install, the mismatch is almost always the platform pin, not the package.
The Packagist gap will bite a teammate. composer create-project <vendor>/<kit> fails with "could not be found" until registration completes. Document the GitHub/VCS fallback in onboarding rather than letting each person rediscover it. Also be aware the short name is unregistered — treat any similarly named package on Packagist as suspect.
SANCTUM_STATEFUL_DOMAINS must include the port. A SPA on localhost:5173 hitting an API on localhost:8000 is a cross-site request as far as the browser is concerned. Include both host and port, set SESSION_DOMAIN appropriately, enable withCredentials/supports_credentials, and make sure the CSRF cookie route is reachable. Port mismatches produce a 419 that looks like a CSRF bug but is really a config bug.
EnsureFrontendRequestsAreStateful has to come first. For cookie auth, that middleware must lead the api group, ahead of auth:sanctum. If a route works with a bearer token but 401s from the SPA, middleware ordering is the first thing to check — php artisan route:list --middleware=sanctum exposes it quickly.
Tokens don't expire by default. Sanctum's expiration config is null out of the box, which means permanent credentials. Set a lifetime for user-facing tokens, and schedule sanctum:prune-expired so the table doesn't grow without bound. Long-lived CI tokens should be an explicit, narrow exception.
Plaintext tokens are unrecoverable. They're hashed at rest. A user who loses a token gets a new one; there is no "show me my token" feature to build. Design the UI copy accordingly, and never emit tokens in logs or error responses — check that your exception handler isn't dumping the request body on validation failures.
Gate the docs route in production. Auto-generated API documentation frequently ships exposed. If the kit mounts it publicly, wrap it in auth or restrict it to non-production environments — especially if it enumerates internal endpoints or includes example credentials.
Debug 401 vs 419 vs 403 by cause, not by guess. No/invalid token is 401; missing CSRF cookie or session on a cookie-authenticated request is 419; a valid token lacking the required ability is 403. Confirm the row exists in personal_access_tokens and that the ability string matches the route middleware exactly — case and separator mismatches are silent.
Strict quality checks can block your first commit. If the kit wires aggressive static analysis and formatting into git hooks, expect the initial run to flag the scaffold itself or your first edit. Learn the "fix" entry point (typically a formatter script plus a baseline for static analysis) before you start deleting code to appease it.
Sail port collisions are common. Default ports for the web server, MySQL, and Redis frequently clash with an existing local stack. Change the host-side ports in docker-compose.yml early, in one place, and note the mapping — otherwise sail artisan migrate connects to the wrong database and you'll debug phantom missing tables.
Treat config/sanctum.php and its migrations as yours now. Publish and edit them freely rather than waiting for upstream; but record every deviation from the release commit so future upstream diffs stay reviewable. That change log is the cheapest insurance against a painful merge six months from now.
How can I help you explore Laravel packages today?