Repository navigation
v0.4.1
A hardening release: no breaking changes. The installer closes five Fortify
endpoints that answered with a 500, keeps a user's two-factor secrets out of
its JSON, and several things that were README instructions now happen on their
own. "Upgrading from 0.4.0" at the end of this entry has the steps.
Security
- Fortify's endpoints the kit does not own are switched off.
views => falseonly removed Fortify's GET pages; it still registeredPOST /register,
POST /forgot-password,POST /reset-password,PUT /user/profile-information
andPUT /user/password. Each resolves an action class (CreatesNewUsers
and friends) that no kit app binds, so each answered with a 500, and an app
that did bindCreatesNewUsersgot a second registration path that skipped
the kit's disposable-email rule and its limiter. The installer now comments
outFeatures::registration(),resetPasswords(),updateProfileInformation()
andupdatePasswords()inconfig/fortify.php(the kit's own pages do all
five jobs) and leavesemailVerification()alone. The patch is idempotent
and lint-checked, and update mode applies it to an existing install. - The two-factor columns are hidden on the User model. The profile module
addsTwoFactorAuthenticatableto the model but never hid its columns, so
toArray()and the JSON of any user carried the encrypted
two_factor_secretandtwo_factor_recovery_codes. The installer adds both
to the model's$hidden, whether that is aprotected $hiddenarray (the
Laravel 12 skeleton), a#[Hidden([...])]attribute (Laravel 13), or
missing. A model that already lists them is left alone, and the result is
lint-checked like the trait patch.
Fixed
- Fortify's
POST /loginno longer 500s for a two-factor user. It
redirects such a user to the routetwo-factor.login, which did not exist:
the kit's page istwo-factor.challenge.routes/auth.phpadds
two-factor.loginas a redirect to the kit's page, which now also accepts
the login Fortify staged in the session (login.id,login.remember), so
that sign-in completes.route('two-factor.challenge')is unchanged. - The installer no longer leaves passkeys on. Fortify's published config
spreadsFeatures::passkeys([...])over three lines, which the 0.4.0 patch
never matched. Each disabled feature's whole call is now commented out. - The profile page's two-factor card honours
confirmPassword. Fortify's
published config sets it, but the card called the actions directly, so
enabling, disabling, showing and regenerating recovery codes never asked for
the password. Without a confirmation insideauth.password_timeout, those
actions now go to the confirm-password page, which returns to the profile. - A failed registration no longer uses up the hourly limit. The register
page counted every submit against its ten per IP per hour before validating,
so a few typos locked the address out. Only a successful registration counts
now. - The 2FA challenge reads secrets with Fortify's encrypter
(Fortify::currentEncrypter()), so an app that set its own with
Fortify::encryptUsing()can sign in. - A failed login fires
Illuminate\Auth\Events\Failed. The login form
checks credentials withvalidate(), which fires no event, so listeners and
audit logs never saw a failed login. - Registering regenerates the session, as signing in already did.
- Avatars are stored under a random 40-character name, not the user's id,
which made every photo on the public disk guessable. The old file is still
deleted on replace, and deleting an account now deletes its photo too. ui-kit:doctorreports correctly when called twice in one process. It
kept the first run's rows and its failure.
Added
- Sidebar badges work without code.
ModuleRegistryhas always declared
theopen_ticketsandunread_contactsbadges, but bound a resolver that
returned nothing. The new default,Shipbytes\UiKit\Support\DefaultBadgeResolver,
countsSupportTicket::open()andContactSubmission::unread()(new scope:
unread and not archived) when those models exist, once per request. A count
that fails, such as a table not yet migrated, is left out instead of failing
every admin page. An app that binds its ownSidebarBadgeResolverstill wins. - The phone bottom bar shows the badges, through the new
badgeprop on
ds::bottom-nav-item. Requiresshipbytes/blade-ui^1.2.3, which caps the
pill at "9+" and reads the full count to screen readers. - The phone "More" sheet ends with Profile (when the profile module is
installed), Back to site and Log out, which a phone could not reach before.
The user shell gains a Profile link (the user's avatar) beside Log out. - Avatars appear across the shells. The generated
UiKitUsertrait gains
avatarUrl(): ?stringwhen the profile module is installed, and the admin
rail, the user list, the user detail page and the user shell show it.
UiKit::avatarUrl($user)returns null without the module, so those views
fall back to initials. - Admin user screens. The user list and detail show an "Admin" badge
(UiKit::isAdmin($user): the Spatieadminrole or theis_admincolumn,
the same test as the user shell). The detail page has a Delete user button,
which the server refuses for admins and for yourself. - The impersonation button is wired in. The user detail page includes
partials.impersonation-buttonwhen it exists, so the README's "add this
line yourself" step is gone. - Resending the verification email is throttled to six a minute per user,
shared between the verify-email page and the profile page, with a message
when the limit is hit. - The register page checks names against letters, spaces, apostrophes,
hyphens and full stops, with a plain message. - Doctor rows:
fortify features limited to two-factor,
two-factor.login route registered,two-factor columns hidden on the User model,sidebar badges resolve,impersonation button wired(with the
impersonation module) andtrusted proxies configured. The last passes when
bootstrap/app.phpcallstrustProxies(, is a skip outside production and
fails in production. Behind an untrusted proxy every visitor shares one IP,
so ten registrations an hour lock out everyone. - Behaviour tests for every auth page (login, register, password reset,
email verification, confirm password, the 2FA challenge), the profile's 2FA
card and avatar, the admin user screens, the shell and the admin ticket
screen: 133 tests, up from 68.
Upgrading from 0.4.0
composer update shipbytes/laravel-ui-kit -W(-Wlets Composer take
shipbytes/blade-uito 1.2.3).- Commit first, then either:
- re-run the installer in update mode with every installed module:
php artisan ui-kit:install --force --modules=…(the names
ui-kit:list-modulesshows). This applies the two security patches and
republishes the changed stubs. As in 0.4.0,--forceoverwrites
config/admin.phpandconfig/ui-kit.php, so readgit diffand
restore your own nav entries and values; or - without
--force, which keeps every file you own and still applies both
security patches (they editconfig/fortify.phpandapp/Models/User.php
in place), then follow the doctor's hints for the rest: the
two-factor.loginroute line inroutes/auth.phpand the impersonation
include on the user detail page.
- re-run the installer in update mode with every installed module:
npm run build.php artisan ui-kit:doctor. Every row should pass, or skip with a reason.