artisan-build/built-for-cloud
A Laravel package that helps you build cloud-ready applications by auditing common deployment pitfalls and guiding configuration for stateless, scalable hosting. Artisan commands surface issues with local storage, sessions, and caches so your app runs smoothly across containers and multiple servers.
This release is the first usable cut of the device-authorization feature, so treat it as the entry point rather than an add-on.
php artisan migrate before touching routes. Nothing in the flow works against an unmigrated database.POST /bfc/device-authorizations, GET|POST /bfc/device, POST /bfc/device/token, GET|POST /bfc/loopback/authorize, POST /bfc/loopback/token. Write your client against the contract, not against the scripted clients.First use case: an installer or CLI that needs a purpose-scoped credential. The CLI calls POST /bfc/device-authorizations, prints the verification URL and user code, polls POST /bfc/device/token at the advertised cadence, and on success holds a bearer credential bound to the approving subject. Do this end-to-end against a local approval view before wiring your real command.
Choose the flow by topology, not by preference.
Model purposes explicitly. Request the narrowest purpose the caller needs and keep the profile/purpose policy in config. An installer credential should not become your runtime API credential. Withdrawal of a kind or profile is terminal — design your credential storage so a terminal denial is a "re-provision from scratch" path, not a refresh path.
Respect the cadence contract. Poll on the interval the start response gives you, back off on slow-down responses, and stop on expiry. Do not implement polling as a tight retry loop with your own interval; the server-side cadence is part of the protocol.
Serialise the exchange. Exactly one token exchange per authorization is permitted. In queue workers or multi-process CLIs, guard the exchange (lock keyed on the authorization, or a single owning process) — two workers racing the same authorization is a self-inflicted denial.
Wire the bearer check into host resolution. The credential's bound subject must equal the host's resolved request subject. Put subject resolution behind one service your auth middleware and the package both use, so the comparison has a single definition. Test it through your real middleware stack, not just the controller.
Integrate via the approval views and clients. Override the shipped approval views for branding/copy, but keep the decision payload shape intact. Use the scripted clients as test fixtures: run them against a fake HTTP layer to assert your cadence and terminal-denial handling.
Testing patterns that pay off:
Http::fake() the contract endpoints; assert the sequence start → poll → token, including the "slow down" and "expired" branches.travel) for cadence windows and expiry boundaries.Scheduling: prune on a schedule tighter than your expiry window, and alert if prune stops running; an unpruned table is both a growth and a stale-authorization problem.
DeviceAuthorizationStart is not a public payload. It carries an internal id and a reveal-once nonce. Do not toArray()/toJson()/log it, and do not return it verbatim from your own controller or console output. Map it to your own DTO at the boundary — this is disclosed in the release notes, so assume it will be checked in review.
The denial-reason vocabulary is not database-enforced. The closed set exists in code but not as a DB constraint. If you persist or branch on denial reasons, validate against the vocabulary in your own layer and add a test that an unexpected value fails loudly rather than silently falling through a match.
Denial can be connection-wide. Containment rules mean a denial can terminate other in-flight authorizations on the same connection. Do not model a denial as isolated to one request; have a path that clears cached pending state for the connection/tenant.
Terminal vs retryable is the whole error model. Kind/profile withdrawal is terminal. Expiry is terminal. Cadence violations are recoverable. Retrying a terminal denial turns a clear failure into an opaque one, and with single-exchange semantics a naive retry on the token endpoint can burn the authorization.
Exact-bound bearer means proxy sensitivity. Anything that changes the resolved request subject — load balancer headers, trusted-proxy config, host normalisation — can turn valid credentials into denials. Verify behind the same proxy config you run in production, not just artisan serve.
Loopback URIs are exact. http://127.0.0.1:PORT/cb and http://localhost:PORT/cb are different registrations, and a random ephemeral port needs a matching registration strategy. Decide this before you ship the desktop client.
Clock skew affects both cadence floor and expiry. Keep hosts NTP-synced; a fast client clock looks like cadence abuse.
Extension points: purpose/profile policy config, the approval views, and the scripted clients. Prefer these over subclassing the lifecycle internals — the internals (nonce, serialized exchange) are the parts the release notes explicitly mark as not-for-consumption.
How can I help you explore Laravel packages today?